ConversationViewFragment.java revision 6b3d0d9ab407c3d8b6bcb73bddbfd23f2513bb83
19b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira/* 29b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira * Copyright (C) 2012 Google Inc. 39b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira * Licensed to The Android Open Source Project. 49b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira * 59b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira * Licensed under the Apache License, Version 2.0 (the "License"); 69b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira * you may not use this file except in compliance with the License. 79b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira * You may obtain a copy of the License at 89b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira * 99b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira * http://www.apache.org/licenses/LICENSE-2.0 109b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira * 119b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira * Unless required by applicable law or agreed to in writing, software 129b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira * distributed under the License is distributed on an "AS IS" BASIS, 139b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 149b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira * See the License for the specific language governing permissions and 159b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira * limitations under the License. 169b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira */ 179b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira 189b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereirapackage com.android.mail.ui; 199b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira 20f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 21b8361c9f8938b74977316319885998aae09aab77Paul Westbrookimport android.content.ContentResolver; 229b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereiraimport android.content.Context; 238e915724b6e4374da9b70161ee0a55f0c763e563Mindy Pereiraimport android.content.Loader; 24ad0c30d0517e06c472c76b11795092f5e67d8f5amindypimport android.content.res.Resources; 259b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereiraimport android.database.Cursor; 269d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huangimport android.database.DataSetObserver; 27b8361c9f8938b74977316319885998aae09aab77Paul Westbrookimport android.net.Uri; 28cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrookimport android.os.AsyncTask; 299b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereiraimport android.os.Bundle; 303bcf180f8104bc27319086a9a6ece5a3c2917c37mindypimport android.os.SystemClock; 3147aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huangimport android.text.TextUtils; 329b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereiraimport android.view.LayoutInflater; 339b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereiraimport android.view.View; 344071c2f73218ce75750345557bb31a9110737841Mark Weiimport android.view.View.OnLayoutChangeListener; 359b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereiraimport android.view.ViewGroup; 36f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangimport android.webkit.ConsoleMessage; 37cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrookimport android.webkit.CookieManager; 38cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrookimport android.webkit.CookieSyncManager; 39974c966c14000d42f089e2021b098d5cf61f9245Mindy Pereiraimport android.webkit.JavascriptInterface; 40f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangimport android.webkit.WebChromeClient; 41f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangimport android.webkit.WebSettings; 4217a9cde3b0e28fc98fdeda19de81e18056eb09dbAndy Huangimport android.webkit.WebView; 4317a9cde3b0e28fc98fdeda19de81e18056eb09dbAndy Huangimport android.webkit.WebViewClient; 4447aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huangimport android.widget.TextView; 459b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira 4659e0b18db1bd06cfb74693d7dbb0cb48112a69b1Andy Huangimport com.android.mail.FormattedDateBuilder; 479b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereiraimport com.android.mail.R; 485ff63747a1b5c6e2197528972cbc3ba808b09d8dAndy Huangimport com.android.mail.browse.ConversationContainer; 49adbf3e8cadb66666f307352b72537fbac57b916fAndy Huangimport com.android.mail.browse.ConversationContainer.OverlayPosition; 5046dfba6160b55a582b344328067e3dafeb881dd9Andy Huangimport com.android.mail.browse.ConversationOverlayItem; 517bdc3750454efe59617b7df945eadd7e59bee954Andy Huangimport com.android.mail.browse.ConversationViewAdapter; 5228b7aee7fa1f7d096d33fc823a88a64f7a3fa79dAndy Huangimport com.android.mail.browse.ConversationViewAdapter.ConversationAccountController; 5346dfba6160b55a582b344328067e3dafeb881dd9Andy Huangimport com.android.mail.browse.ConversationViewAdapter.MessageFooterItem; 547bdc3750454efe59617b7df945eadd7e59bee954Andy Huangimport com.android.mail.browse.ConversationViewAdapter.MessageHeaderItem; 5546dfba6160b55a582b344328067e3dafeb881dd9Andy Huangimport com.android.mail.browse.ConversationViewAdapter.SuperCollapsedBlockItem; 565ff63747a1b5c6e2197528972cbc3ba808b09d8dAndy Huangimport com.android.mail.browse.ConversationViewHeader; 575ff63747a1b5c6e2197528972cbc3ba808b09d8dAndy Huangimport com.android.mail.browse.ConversationWebView; 58dde3f9ff76c1a7f9a6e9fa959a6422a8a8d82e81mindypimport com.android.mail.browse.ConversationWebView.ContentSizeChangeListener; 597bdc3750454efe59617b7df945eadd7e59bee954Andy Huangimport com.android.mail.browse.MessageCursor; 60cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huangimport com.android.mail.browse.MessageCursor.ConversationController; 6128b7aee7fa1f7d096d33fc823a88a64f7a3fa79dAndy Huangimport com.android.mail.browse.MessageCursor.ConversationMessage; 6259e0b18db1bd06cfb74693d7dbb0cb48112a69b1Andy Huangimport com.android.mail.browse.MessageHeaderView; 633233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huangimport com.android.mail.browse.MessageHeaderView.MessageHeaderViewCallbacks; 64adbf3e8cadb66666f307352b72537fbac57b916fAndy Huangimport com.android.mail.browse.ScrollIndicatorsView; 6546dfba6160b55a582b344328067e3dafeb881dd9Andy Huangimport com.android.mail.browse.SuperCollapsedBlock; 660b7ed6fae6e36f2abc4ca916764177d1879731b4Andy Huangimport com.android.mail.browse.WebViewContextMenu; 679b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereiraimport com.android.mail.providers.Account; 6865fe28fa88daad08f3be4c084ca5b4eaa366d1a7Andy Huangimport com.android.mail.providers.Address; 699b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereiraimport com.android.mail.providers.Conversation; 70f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangimport com.android.mail.providers.Message; 71b8361c9f8938b74977316319885998aae09aab77Paul Westbrookimport com.android.mail.providers.UIProvider; 72cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huangimport com.android.mail.ui.ConversationViewState.ExpansionState; 73b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrookimport com.android.mail.utils.LogTag; 749b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereiraimport com.android.mail.utils.LogUtils; 752e9acfe527426c00b5df2ca66189262dc40c8575Andy Huangimport com.android.mail.utils.Utils; 7646dfba6160b55a582b344328067e3dafeb881dd9Andy Huangimport com.google.common.collect.Lists; 77b8331b4565566ca733997398e8c07a26cd2bee98Andy Huangimport com.google.common.collect.Sets; 7865fe28fa88daad08f3be4c084ca5b4eaa366d1a7Andy Huang 7946dfba6160b55a582b344328067e3dafeb881dd9Andy Huangimport java.util.List; 80b8331b4565566ca733997398e8c07a26cd2bee98Andy Huangimport java.util.Set; 819b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira 82f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 839b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira/** 849b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira * The conversation view UI component. 859b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira */ 86f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyppublic final class ConversationViewFragment extends AbstractConversationViewFragment implements 8746dfba6160b55a582b344328067e3dafeb881dd9Andy Huang MessageHeaderViewCallbacks, 88cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang SuperCollapsedBlock.OnClickListener, 8928b7aee7fa1f7d096d33fc823a88a64f7a3fa79dAndy Huang ConversationController, 904071c2f73218ce75750345557bb31a9110737841Mark Wei ConversationAccountController, 914071c2f73218ce75750345557bb31a9110737841Mark Wei OnLayoutChangeListener { 928e915724b6e4374da9b70161ee0a55f0c763e563Mindy Pereira 93b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrook private static final String LOG_TAG = LogTag.getLogTag(); 94632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang public static final String LAYOUT_TAG = "ConvLayout"; 959b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira 969d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang /** 971b3cc47f54072105c161d6ed557550e0e149b8bbmindyp * Difference in the height of the message header whose details have been expanded/collapsed 981b3cc47f54072105c161d6ed557550e0e149b8bbmindyp */ 991b3cc47f54072105c161d6ed557550e0e149b8bbmindyp private int mDiff = 0; 1001b3cc47f54072105c161d6ed557550e0e149b8bbmindyp 1011b3cc47f54072105c161d6ed557550e0e149b8bbmindyp /** 1029d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang * Default value for {@link #mLoadWaitReason}. Conversation load will happen immediately. 1039d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang */ 1049d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang private final int LOAD_NOW = 0; 1059d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang /** 1069d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang * Value for {@link #mLoadWaitReason} that means we are offscreen and waiting for the visible 1079d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang * conversation to finish loading before beginning our load. 1089d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang * <p> 1099d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang * When this value is set, the fragment should register with {@link ConversationListCallbacks} 1109d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang * to know when the visible conversation is loaded. When it is unset, it should unregister. 1119d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang */ 1129d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang private final int LOAD_WAIT_FOR_INITIAL_CONVERSATION = 1; 1139d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang /** 1149d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang * Value for {@link #mLoadWaitReason} used when a conversation is too heavyweight to load at 1159d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang * all when not visible (e.g. requires network fetch, or too complex). Conversation load will 1169d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang * wait until this fragment is visible. 1179d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang */ 1189d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang private final int LOAD_WAIT_UNTIL_VISIBLE = 2; 1193bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp 120f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang private ConversationContainer mConversationContainer; 1219b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira 122f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang private ConversationWebView mWebView; 1239b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira 12456d83850db72592a16f4e6ee9e0d59b60ec0824aMark Wei private ScrollIndicatorsView mScrollIndicators; 12556d83850db72592a16f4e6ee9e0d59b60ec0824aMark Wei 12647aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang private View mNewMessageBar; 12747aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang 128f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang private HtmlConversationTemplates mTemplates; 129f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 130f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang private final MailJsBridge mJsBridge = new MailJsBridge(); 131f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 13217a9cde3b0e28fc98fdeda19de81e18056eb09dbAndy Huang private final WebViewClient mWebViewClient = new ConversationWebViewClient(); 13317a9cde3b0e28fc98fdeda19de81e18056eb09dbAndy Huang 1347bdc3750454efe59617b7df945eadd7e59bee954Andy Huang private ConversationViewAdapter mAdapter; 13551067133f35911bf4b43f97be52b00e5bd9f07abAndy Huang 13651067133f35911bf4b43f97be52b00e5bd9f07abAndy Huang private boolean mViewsCreated; 1374071c2f73218ce75750345557bb31a9110737841Mark Wei // True if we attempted to render before the views were laid out 1384071c2f73218ce75750345557bb31a9110737841Mark Wei // We will render immediately once layout is done 1394071c2f73218ce75750345557bb31a9110737841Mark Wei private boolean mNeedRender; 14051067133f35911bf4b43f97be52b00e5bd9f07abAndy Huang 14146dfba6160b55a582b344328067e3dafeb881dd9Andy Huang /** 14246dfba6160b55a582b344328067e3dafeb881dd9Andy Huang * Temporary string containing the message bodies of the messages within a super-collapsed 14346dfba6160b55a582b344328067e3dafeb881dd9Andy Huang * block, for one-time use during block expansion. We cannot easily pass the body HTML 14446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang * into JS without problematic escaping, so hold onto it momentarily and signal JS to fetch it 14546dfba6160b55a582b344328067e3dafeb881dd9Andy Huang * using {@link MailJsBridge}. 14646dfba6160b55a582b344328067e3dafeb881dd9Andy Huang */ 14746dfba6160b55a582b344328067e3dafeb881dd9Andy Huang private String mTempBodiesHtml; 14846dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 149632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang private int mMaxAutoLoadMessages; 150632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 1519d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang /** 1529d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang * If this conversation fragment is not visible, and it's inappropriate to load up front, 1539d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang * this is the reason we are waiting. This flag should be cleared once it's okay to load 1549d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang * the conversation. 1559d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang */ 1569d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang private int mLoadWaitReason = LOAD_NOW; 157632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 1583bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp private boolean mEnableContentReadySignal; 15928b7aee7fa1f7d096d33fc823a88a64f7a3fa79dAndy Huang 160dde3f9ff76c1a7f9a6e9fa959a6422a8a8d82e81mindyp private ContentSizeChangeListener mWebViewSizeChangeListener; 161dde3f9ff76c1a7f9a6e9fa959a6422a8a8d82e81mindyp 162e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang private float mWebViewYPercent; 163e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang 164e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang /** 165e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang * Has loadData been called on the WebView yet? 166e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang */ 167e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang private boolean mWebViewLoadedData; 168e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang 16963b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang private long mWebViewLoadStartMs; 17063b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang 1719d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang private final DataSetObserver mLoadedObserver = new DataSetObserver() { 1729d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang @Override 1739d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang public void onChanged() { 1749d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang getHandler().post(new FragmentRunnable("delayedConversationLoad") { 1759d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang @Override 1769d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang public void go() { 1779d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang LogUtils.d(LOG_TAG, "CVF load observer fired, this=%s", 1789d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang ConversationViewFragment.this); 1799d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang handleDelayedConversationLoad(); 1809d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 1819d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang }); 1829d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 1839d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang }; 184f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 18530bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang private final Runnable mOnProgressDismiss = new FragmentRunnable("onProgressDismiss") { 1867d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang @Override 1877d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang public void go() { 1887d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang if (isUserVisible()) { 1897d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang onConversationSeen(); 1907d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang } 19130bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang mWebView.onRenderComplete(); 1927d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang } 1937d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang }; 1947d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang 195bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang private static final boolean DEBUG_DUMP_CONVERSATION_HTML = false; 19647aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang private static final boolean DISABLE_OFFSCREEN_LOADING = false; 19706c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang private static final boolean DEBUG_DUMP_CURSOR_CONTENTS = false; 198e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang 199e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang private static final String BUNDLE_KEY_WEBVIEW_Y_PERCENT = 200e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang ConversationViewFragment.class.getName() + "webview-y-percent"; 201bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang 2026c51158ad3269f157424e6c7bd488425c98da08fVikram Aggarwal /** 2036c51158ad3269f157424e6c7bd488425c98da08fVikram Aggarwal * Constructor needs to be public to handle orientation changes and activity lifecycle events. 2046c51158ad3269f157424e6c7bd488425c98da08fVikram Aggarwal */ 205f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang public ConversationViewFragment() { 2066c51158ad3269f157424e6c7bd488425c98da08fVikram Aggarwal super(); 2079b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira } 2089b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira 2099b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira /** 2109b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira * Creates a new instance of {@link ConversationViewFragment}, initialized 211632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * to display a conversation with other parameters inherited/copied from an existing bundle, 212632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * typically one created using {@link #makeBasicArgs}. 213632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang */ 214632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang public static ConversationViewFragment newInstance(Bundle existingArgs, 215632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang Conversation conversation) { 216632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang ConversationViewFragment f = new ConversationViewFragment(); 217632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang Bundle args = new Bundle(existingArgs); 218632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang args.putParcelable(ARG_CONVERSATION, conversation); 219632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang f.setArguments(args); 220632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang return f; 221632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 222632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 223f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 224adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang public void onAccountChanged(Account newAccount, Account oldAccount) { 225adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang // if overview mode has changed, re-render completely (no need to also update headers) 226adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang if (isOverviewMode(newAccount) != isOverviewMode(oldAccount)) { 227adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang setupOverviewMode(); 228adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang final MessageCursor c = getMessageCursor(); 229adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang if (c != null) { 230adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang renderConversation(c); 231adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang } else { 232adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang // Null cursor means this fragment is either waiting to load or in the middle of 233adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang // loading. Either way, a future render will happen anyway, and the new setting 234adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang // will take effect when that happens. 235adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang } 236adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang return; 237adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang } 238adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang 239f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // settings may have been updated; refresh views that are known to 240f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // depend on settings 241f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mAdapter.notifyDataSetChanged(); 242632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 243632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 2449b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira @Override 2459b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira public void onActivityCreated(Bundle savedInstanceState) { 2469d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang LogUtils.d(LOG_TAG, "IN CVF.onActivityCreated, this=%s visible=%s", this, isUserVisible()); 2479b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira super.onActivityCreated(savedInstanceState); 2481abfcaf0ee345acee28f0b4892314119082b28a2Mark Wei 2491abfcaf0ee345acee28f0b4892314119082b28a2Mark Wei if (mActivity == null || mActivity.isFinishing()) { 2501abfcaf0ee345acee28f0b4892314119082b28a2Mark Wei // Activity is finishing, just bail. 2511abfcaf0ee345acee28f0b4892314119082b28a2Mark Wei return; 2521abfcaf0ee345acee28f0b4892314119082b28a2Mark Wei } 2531abfcaf0ee345acee28f0b4892314119082b28a2Mark Wei 254f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp Context context = getContext(); 255f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mTemplates = new HtmlConversationTemplates(context); 25659e0b18db1bd06cfb74693d7dbb0cb48112a69b1Andy Huang 257f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp final FormattedDateBuilder dateBuilder = new FormattedDateBuilder(context); 25859e0b18db1bd06cfb74693d7dbb0cb48112a69b1Andy Huang 2598081df46ef5a7794374e41cd1836e778a2da9b31Paul Westbrook mAdapter = new ConversationViewAdapter(mActivity, this, 260f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp getLoaderManager(), this, getContactInfoSource(), this, 2618081df46ef5a7794374e41cd1836e778a2da9b31Paul Westbrook this, mAddressCache, dateBuilder); 26251067133f35911bf4b43f97be52b00e5bd9f07abAndy Huang mConversationContainer.setOverlayAdapter(mAdapter); 26351067133f35911bf4b43f97be52b00e5bd9f07abAndy Huang 26459e0b18db1bd06cfb74693d7dbb0cb48112a69b1Andy Huang // set up snap header (the adapter usually does this with the other ones) 26559e0b18db1bd06cfb74693d7dbb0cb48112a69b1Andy Huang final MessageHeaderView snapHeader = mConversationContainer.getSnapHeader(); 26628b7aee7fa1f7d096d33fc823a88a64f7a3fa79dAndy Huang snapHeader.initialize(dateBuilder, this, mAddressCache); 26759e0b18db1bd06cfb74693d7dbb0cb48112a69b1Andy Huang snapHeader.setCallbacks(this); 268f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp snapHeader.setContactInfoSource(getContactInfoSource()); 26959e0b18db1bd06cfb74693d7dbb0cb48112a69b1Andy Huang 270632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang mMaxAutoLoadMessages = getResources().getInteger(R.integer.max_auto_load_messages); 271632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 272f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mWebView.setOnCreateContextMenuListener(new WebViewContextMenu(getActivity())); 2730b7ed6fae6e36f2abc4ca916764177d1879731b4Andy Huang 274adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang // set this up here instead of onCreateView to ensure the latest Account is loaded 275adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang setupOverviewMode(); 276adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang 2779d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang // Defer the call to initLoader with a Handler. 2789d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang // We want to wait until we know which fragments are present and their final visibility 2799d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang // states before going off and doing work. This prevents extraneous loading from occurring 2809d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang // as the ViewPager shifts about before the initial position is set. 2819d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang // 2829d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang // e.g. click on item #10 2839d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang // ViewPager.setAdapter() actually first loads #0 and #1 under the assumption that #0 is 2849d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang // the initial primary item 2859d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang // Then CPC immediately sets the primary item to #10, which tears down #0/#1 and sets up 2869d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang // #9/#10/#11. 2879d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang getHandler().post(new FragmentRunnable("showConversation") { 2889d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang @Override 2899d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang public void go() { 2909d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang showConversation(); 2919d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 2929d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang }); 293cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook 294cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook if (mConversation.conversationBaseUri != null && 295b8361c9f8938b74977316319885998aae09aab77Paul Westbrook !Utils.isEmpty(mAccount.accoutCookieQueryUri)) { 296cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook // Set the cookie for this base url 297b8361c9f8938b74977316319885998aae09aab77Paul Westbrook new SetCookieTask(getContext(), mConversation.conversationBaseUri, 298b8361c9f8938b74977316319885998aae09aab77Paul Westbrook mAccount.accoutCookieQueryUri).execute(); 299cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook } 3009b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira } 3019b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira 3029b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira @Override 303e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang public void onCreate(Bundle savedState) { 304e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang super.onCreate(savedState); 305e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang 306e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang if (savedState != null) { 307e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang mWebViewYPercent = savedState.getFloat(BUNDLE_KEY_WEBVIEW_Y_PERCENT); 308e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang } 309e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang } 310e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang 311e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang @Override 3129b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira public View onCreateView(LayoutInflater inflater, 3139b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira ViewGroup container, Bundle savedInstanceState) { 314839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang 315632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang View rootView = inflater.inflate(R.layout.conversation_view, container, false); 316f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang mConversationContainer = (ConversationContainer) rootView 317f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang .findViewById(R.id.conversation_container); 31847aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang 31947aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang mNewMessageBar = mConversationContainer.findViewById(R.id.new_message_notification_bar); 32047aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang mNewMessageBar.setOnClickListener(new View.OnClickListener() { 32147aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang @Override 32247aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang public void onClick(View v) { 32347aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang onNewMessageBarClick(); 32447aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang } 32547aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang }); 32647aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang 327ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp instantiateProgressIndicators(rootView); 3283bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp 3295ff63747a1b5c6e2197528972cbc3ba808b09d8dAndy Huang mWebView = (ConversationWebView) mConversationContainer.findViewById(R.id.webview); 330f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 331f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang mWebView.addJavascriptInterface(mJsBridge, "mail"); 3323bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp // On JB or newer, we use the 'webkitAnimationStart' DOM event to signal load complete 3333bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp // Below JB, try to speed up initial render by having the webview do supplemental draws to 3343bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp // custom a software canvas. 335b941fdb95f61cdde4e1775ac9034343951e8b075mindyp // TODO(mindyp): 336b941fdb95f61cdde4e1775ac9034343951e8b075mindyp //PAGE READINESS SIGNAL FOR JELLYBEAN AND NEWER 337b941fdb95f61cdde4e1775ac9034343951e8b075mindyp // Notify the app on 'webkitAnimationStart' of a simple dummy element with a simple no-op 338b941fdb95f61cdde4e1775ac9034343951e8b075mindyp // animation that immediately runs on page load. The app uses this as a signal that the 339b941fdb95f61cdde4e1775ac9034343951e8b075mindyp // content is loaded and ready to draw, since WebView delays firing this event until the 340b941fdb95f61cdde4e1775ac9034343951e8b075mindyp // layers are composited and everything is ready to draw. 341b941fdb95f61cdde4e1775ac9034343951e8b075mindyp // This signal does not seem to be reliable, so just use the old method for now. 34232d911f491afad153983f7519267ddb764927355mindyp mEnableContentReadySignal = Utils.isRunningJellybeanOrLater(); 343afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp mWebView.setUseSoftwareLayer(!mEnableContentReadySignal); 34430bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang mWebView.onUserVisibilityChanged(isUserVisible()); 34517a9cde3b0e28fc98fdeda19de81e18056eb09dbAndy Huang mWebView.setWebViewClient(mWebViewClient); 346f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang mWebView.setWebChromeClient(new WebChromeClient() { 347f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang @Override 348f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang public boolean onConsoleMessage(ConsoleMessage consoleMessage) { 349f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang LogUtils.i(LOG_TAG, "JS: %s (%s:%d)", consoleMessage.message(), 350f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang consoleMessage.sourceId(), consoleMessage.lineNumber()); 351f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang return true; 352f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang } 353f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang }); 354f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 3553233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang final WebSettings settings = mWebView.getSettings(); 356f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 35756d83850db72592a16f4e6ee9e0d59b60ec0824aMark Wei mScrollIndicators = (ScrollIndicatorsView) rootView.findViewById(R.id.scroll_indicators); 35856d83850db72592a16f4e6ee9e0d59b60ec0824aMark Wei mScrollIndicators.setSourceView(mWebView); 35956d83850db72592a16f4e6ee9e0d59b60ec0824aMark Wei 360f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang settings.setJavaScriptEnabled(true); 361f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 362c319b551bebe40b9607acf0ee1d26a880fde9212Andy Huang final float fontScale = getResources().getConfiguration().fontScale; 363ba283732e7cbcc55a3dbc8ab78950cc38cd078fbAndy Huang final int desiredFontSizePx = getResources() 364ba283732e7cbcc55a3dbc8ab78950cc38cd078fbAndy Huang .getInteger(R.integer.conversation_desired_font_size_px); 365ba283732e7cbcc55a3dbc8ab78950cc38cd078fbAndy Huang final int unstyledFontSizePx = getResources() 366ba283732e7cbcc55a3dbc8ab78950cc38cd078fbAndy Huang .getInteger(R.integer.conversation_unstyled_font_size_px); 367ba283732e7cbcc55a3dbc8ab78950cc38cd078fbAndy Huang 368ba283732e7cbcc55a3dbc8ab78950cc38cd078fbAndy Huang int textZoom = settings.getTextZoom(); 369ba283732e7cbcc55a3dbc8ab78950cc38cd078fbAndy Huang // apply a correction to the default body text style to get regular text to the size we want 370ba283732e7cbcc55a3dbc8ab78950cc38cd078fbAndy Huang textZoom = textZoom * desiredFontSizePx / unstyledFontSizePx; 371ba283732e7cbcc55a3dbc8ab78950cc38cd078fbAndy Huang // then apply any system font scaling 372c319b551bebe40b9607acf0ee1d26a880fde9212Andy Huang textZoom = (int) (textZoom * fontScale); 373c319b551bebe40b9607acf0ee1d26a880fde9212Andy Huang settings.setTextZoom(textZoom); 374c319b551bebe40b9607acf0ee1d26a880fde9212Andy Huang 37551067133f35911bf4b43f97be52b00e5bd9f07abAndy Huang mViewsCreated = true; 376e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang mWebViewLoadedData = false; 37751067133f35911bf4b43f97be52b00e5bd9f07abAndy Huang 3789b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira return rootView; 3799b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira } 3809b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira 3819b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira @Override 3829b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira public void onDestroyView() { 3839b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira super.onDestroyView(); 38446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang mConversationContainer.setOverlayAdapter(null); 38546dfba6160b55a582b344328067e3dafeb881dd9Andy Huang mAdapter = null; 3869d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang resetLoadWaiting(); // be sure to unregister any active load observer 38751067133f35911bf4b43f97be52b00e5bd9f07abAndy Huang mViewsCreated = false; 3889b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira } 3899b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira 390e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang @Override 391e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang public void onSaveInstanceState(Bundle outState) { 392e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang super.onSaveInstanceState(outState); 393e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang 394e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang outState.putFloat(BUNDLE_KEY_WEBVIEW_Y_PERCENT, calculateScrollYPercent()); 395e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang } 396e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang 397e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang private float calculateScrollYPercent() { 398e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang float p; 399e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang int scrollY = mWebView.getScrollY(); 400e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang int viewH = mWebView.getHeight(); 401e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang int webH = (int) (mWebView.getContentHeight() * mWebView.getScale()); 402e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang 403e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang if (webH == 0 || webH <= viewH) { 404e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang p = 0; 405e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang } else if (scrollY + viewH >= webH) { 406e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang // The very bottom is a special case, it acts as a stronger anchor than the scroll top 407e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang // at that point. 408e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang p = 1.0f; 409e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang } else { 410e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang p = (float) scrollY / webH; 411e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang } 412e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang return p; 413e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang } 414e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang 4159d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang private void resetLoadWaiting() { 4169d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang if (mLoadWaitReason == LOAD_WAIT_FOR_INITIAL_CONVERSATION) { 4179d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang getListController().unregisterConversationLoadedObserver(mLoadedObserver); 4189d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 4199d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang mLoadWaitReason = LOAD_NOW; 4209d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 4219d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 4225ff63747a1b5c6e2197528972cbc3ba808b09d8dAndy Huang @Override 423f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp protected void markUnread() { 424839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang // Ignore unsafe calls made after a fragment is detached from an activity 425839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang final ControllableActivity activity = (ControllableActivity) getActivity(); 426839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang if (activity == null) { 427839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang LogUtils.w(LOG_TAG, "ignoring markUnread for conv=%s", mConversation.id); 428839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang return; 429839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang } 430839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang 43128e31e2417d775b343919cf6018f85871e40a294Andy Huang if (mViewState == null) { 43228e31e2417d775b343919cf6018f85871e40a294Andy Huang LogUtils.i(LOG_TAG, "ignoring markUnread for conv with no view state (%d)", 43328e31e2417d775b343919cf6018f85871e40a294Andy Huang mConversation.id); 43428e31e2417d775b343919cf6018f85871e40a294Andy Huang return; 43528e31e2417d775b343919cf6018f85871e40a294Andy Huang } 436839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang activity.getConversationUpdater().markConversationMessagesUnread(mConversation, 4374a878b606961825fb4fd412b460df75779f09ad2Vikram Aggarwal mViewState.getUnreadMessageUris(), mViewState.getConversationInfo()); 438839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang } 439839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang 440f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 441f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void onUserVisibleHintChanged() { 4429d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang final boolean userVisible = isUserVisible(); 4439d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 4449d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang if (!userVisible) { 4459d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang dismissLoadingStatus(); 4469d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } else if (mViewsCreated) { 4479d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang if (getMessageCursor() != null) { 4489d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang LogUtils.d(LOG_TAG, "Fragment is now user-visible, onConversationSeen: %s", this); 449f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp onConversationSeen(); 4509d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } else if (isLoadWaiting()) { 4519d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang LogUtils.d(LOG_TAG, "Fragment is now user-visible, showing conversation: %s", this); 4529d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang handleDelayedConversationLoad(); 453632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 454632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 455f8cf5468500677fceac3025727daba8155d319d6Andy Huang 45630bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang if (mWebView != null) { 45730bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang mWebView.onUserVisibilityChanged(userVisible); 45830bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang } 459f8cf5468500677fceac3025727daba8155d319d6Andy Huang } 460f8cf5468500677fceac3025727daba8155d319d6Andy Huang 4619d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang /** 4629d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang * Will either call initLoader now to begin loading, or set {@link #mLoadWaitReason} and do 4639d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang * nothing (in which case you should later call {@link #handleDelayedConversationLoad()}). 4649d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang */ 4659b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira private void showConversation() { 4669d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang final int reason; 4679d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 4689d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang if (isUserVisible()) { 4699d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang LogUtils.i(LOG_TAG, 4709d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang "SHOWCONV: CVF is user-visible, immediately loading conversation (%s)", this); 4719d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang reason = LOAD_NOW; 4729d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } else { 4739d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang final boolean disableOffscreenLoading = DISABLE_OFFSCREEN_LOADING 4749d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang || (mConversation.isRemote 4759d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang || mConversation.getNumMessages() > mMaxAutoLoadMessages); 4769d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 4779d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang // When not visible, we should not immediately load if either this conversation is 4789d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang // too heavyweight, or if the main/initial conversation is busy loading. 4799d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang if (disableOffscreenLoading) { 4809d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang reason = LOAD_WAIT_UNTIL_VISIBLE; 4819d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang LogUtils.i(LOG_TAG, "SHOWCONV: CVF waiting until visible to load (%s)", this); 4829d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } else if (getListController().isInitialConversationLoading()) { 4839d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang reason = LOAD_WAIT_FOR_INITIAL_CONVERSATION; 4849d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang LogUtils.i(LOG_TAG, "SHOWCONV: CVF waiting for initial to finish (%s)", this); 4859d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang getListController().registerConversationLoadedObserver(mLoadedObserver); 4869d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } else { 4879d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang LogUtils.i(LOG_TAG, 4889d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang "SHOWCONV: CVF is not visible, but no reason to wait. loading now. (%s)", 4899d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang this); 4909d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang reason = LOAD_NOW; 4919d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 492632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 4939d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 4949d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang mLoadWaitReason = reason; 4959d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang if (mLoadWaitReason == LOAD_NOW) { 4969d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang startConversationLoad(); 4979d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 4989d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 4999d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 5009d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang private void handleDelayedConversationLoad() { 5019d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang resetLoadWaiting(); 5029d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang startConversationLoad(); 5039d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 5049d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 5059d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang private void startConversationLoad() { 5063bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp mWebView.setVisibility(View.VISIBLE); 507f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp getLoaderManager().initLoader(MESSAGE_LOADER, Bundle.EMPTY, getMessageLoaderCallbacks()); 5089d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang if (isUserVisible()) { 5095460ce477cb1dcc439dd4e5c9e8393470f00f618Andy Huang final SubjectDisplayChanger sdc = mActivity.getSubjectDisplayChanger(); 5105460ce477cb1dcc439dd4e5c9e8393470f00f618Andy Huang if (sdc != null) { 5115460ce477cb1dcc439dd4e5c9e8393470f00f618Andy Huang sdc.setSubject(mConversation.subject); 5125460ce477cb1dcc439dd4e5c9e8393470f00f618Andy Huang } 5135460ce477cb1dcc439dd4e5c9e8393470f00f618Andy Huang } 5143bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp // TODO(mindyp): don't show loading status for a previously rendered 5153bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp // conversation. Ielieve this is better done by making sure don't show loading status 5163bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp // until XX ms have passed without loading completed. 5173bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp showLoadingStatus(); 5188e915724b6e4374da9b70161ee0a55f0c763e563Mindy Pereira } 5198e915724b6e4374da9b70161ee0a55f0c763e563Mindy Pereira 5207d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang private void revealConversation() { 52130bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang dismissLoadingStatus(mOnProgressDismiss); 5227d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang } 5237d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang 5249d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang private boolean isLoadWaiting() { 5259d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang return mLoadWaitReason != LOAD_NOW; 5269d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 5279d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 52851067133f35911bf4b43f97be52b00e5bd9f07abAndy Huang private void renderConversation(MessageCursor messageCursor) { 5293bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp final String convHtml = renderMessageBodies(messageCursor, mEnableContentReadySignal); 530bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang 531bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang if (DEBUG_DUMP_CONVERSATION_HTML) { 532bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang java.io.FileWriter fw = null; 533bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang try { 534bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang fw = new java.io.FileWriter("/sdcard/conv" + mConversation.id 535bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang + ".html"); 536bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang fw.write(convHtml); 537bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang } catch (java.io.IOException e) { 538bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang e.printStackTrace(); 539bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang } finally { 540bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang if (fw != null) { 541bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang try { 542bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang fw.close(); 543bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang } catch (java.io.IOException e) { 544bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang e.printStackTrace(); 545bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang } 546bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang } 547bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang } 548bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang } 549bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang 550e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang // save off existing scroll position before re-rendering 551e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang if (mWebViewLoadedData) { 552e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang mWebViewYPercent = calculateScrollYPercent(); 553e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang } 554e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang 555bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang mWebView.loadDataWithBaseURL(mBaseUri, convHtml, "text/html", "utf-8", null); 556e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang mWebViewLoadedData = true; 55763b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang mWebViewLoadStartMs = SystemClock.uptimeMillis(); 55851067133f35911bf4b43f97be52b00e5bd9f07abAndy Huang } 55951067133f35911bf4b43f97be52b00e5bd9f07abAndy Huang 5607bdc3750454efe59617b7df945eadd7e59bee954Andy Huang /** 5617bdc3750454efe59617b7df945eadd7e59bee954Andy Huang * Populate the adapter with overlay views (message headers, super-collapsed blocks, a 5627bdc3750454efe59617b7df945eadd7e59bee954Andy Huang * conversation header), and return an HTML document with spacer divs inserted for all overlays. 5637bdc3750454efe59617b7df945eadd7e59bee954Andy Huang * 5647bdc3750454efe59617b7df945eadd7e59bee954Andy Huang */ 5653bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp private String renderMessageBodies(MessageCursor messageCursor, 5663bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp boolean enableContentReadySignal) { 567f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang int pos = -1; 568632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 5691ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang LogUtils.d(LOG_TAG, "IN renderMessageBodies, fragment=%s", this); 5707bdc3750454efe59617b7df945eadd7e59bee954Andy Huang boolean allowNetworkImages = false; 5717bdc3750454efe59617b7df945eadd7e59bee954Andy Huang 572c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang // TODO: re-use any existing adapter item state (expanded, details expanded, show pics) 57328b7aee7fa1f7d096d33fc823a88a64f7a3fa79dAndy Huang 5747bdc3750454efe59617b7df945eadd7e59bee954Andy Huang // Walk through the cursor and build up an overlay adapter as you go. 5757bdc3750454efe59617b7df945eadd7e59bee954Andy Huang // Each overlay has an entry in the adapter for easy scroll handling in the container. 5767bdc3750454efe59617b7df945eadd7e59bee954Andy Huang // Items are not necessarily 1:1 in cursor and adapter because of super-collapsed blocks. 5777bdc3750454efe59617b7df945eadd7e59bee954Andy Huang // When adding adapter items, also add their heights to help the container later determine 5787bdc3750454efe59617b7df945eadd7e59bee954Andy Huang // overlay dimensions. 5797bdc3750454efe59617b7df945eadd7e59bee954Andy Huang 580db620fe42dcf1909468822b61238a23103d15376Andy Huang // When re-rendering, prevent ConversationContainer from laying out overlays until after 581db620fe42dcf1909468822b61238a23103d15376Andy Huang // the new spacers are positioned by WebView. 582db620fe42dcf1909468822b61238a23103d15376Andy Huang mConversationContainer.invalidateSpacerGeometry(); 583db620fe42dcf1909468822b61238a23103d15376Andy Huang 5847bdc3750454efe59617b7df945eadd7e59bee954Andy Huang mAdapter.clear(); 5857bdc3750454efe59617b7df945eadd7e59bee954Andy Huang 58647aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang // re-evaluate the message parts of the view state, since the messages may have changed 58747aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang // since the previous render 58847aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang final ConversationViewState prevState = mViewState; 58947aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang mViewState = new ConversationViewState(prevState); 59047aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang 5915ff63747a1b5c6e2197528972cbc3ba808b09d8dAndy Huang // N.B. the units of height for spacers are actually dp and not px because WebView assumes 5922e9acfe527426c00b5df2ca66189262dc40c8575Andy Huang // a pixel is an mdpi pixel, unless you set device-dpi. 5935ff63747a1b5c6e2197528972cbc3ba808b09d8dAndy Huang 5947bdc3750454efe59617b7df945eadd7e59bee954Andy Huang // add a single conversation header item 5957bdc3750454efe59617b7df945eadd7e59bee954Andy Huang final int convHeaderPos = mAdapter.addConversationHeader(mConversation); 59623014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang final int convHeaderPx = measureOverlayHeight(convHeaderPos); 5973233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang 598256b35c0a8287f48c28e0d1ba3fae65790063295Andy Huang final int sideMarginPx = getResources().getDimensionPixelOffset( 599256b35c0a8287f48c28e0d1ba3fae65790063295Andy Huang R.dimen.conversation_view_margin_side) + getResources().getDimensionPixelOffset( 600256b35c0a8287f48c28e0d1ba3fae65790063295Andy Huang R.dimen.conversation_message_content_margin_side); 601256b35c0a8287f48c28e0d1ba3fae65790063295Andy Huang 602256b35c0a8287f48c28e0d1ba3fae65790063295Andy Huang mTemplates.startConversation(mWebView.screenPxToWebPx(sideMarginPx), 603256b35c0a8287f48c28e0d1ba3fae65790063295Andy Huang mWebView.screenPxToWebPx(convHeaderPx)); 6043233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang 60546dfba6160b55a582b344328067e3dafeb881dd9Andy Huang int collapsedStart = -1; 606839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang ConversationMessage prevCollapsedMsg = null; 60746dfba6160b55a582b344328067e3dafeb881dd9Andy Huang boolean prevSafeForImages = false; 60846dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 609f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang while (messageCursor.moveToPosition(++pos)) { 610839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang final ConversationMessage msg = messageCursor.getMessage(); 61146dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 6123233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang // TODO: save/restore 'show pics' state 6133233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang final boolean safeForImages = msg.alwaysShowImages /* || savedStateSaysSafe */; 6143233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang allowNetworkImages |= safeForImages; 6152405528bb15245f594277fe7b6efacd6a18ce69eAndy Huang 61608098ec4c894d9a15dfe800ad2397494e7e0a79aPaul Westbrook final Integer savedExpanded = prevState.getExpansionState(msg); 61708098ec4c894d9a15dfe800ad2397494e7e0a79aPaul Westbrook final int expandedState; 618839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang if (savedExpanded != null) { 6191ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang if (ExpansionState.isSuperCollapsed(savedExpanded) && messageCursor.isLast()) { 6201ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang // override saved state when this is now the new last message 6211ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang // this happens to the second-to-last message when you discard a draft 6221ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang expandedState = ExpansionState.EXPANDED; 6231ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang } else { 6241ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang expandedState = savedExpanded; 6251ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang } 626839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang } else { 627cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang // new messages that are not expanded default to being eligible for super-collapse 62808098ec4c894d9a15dfe800ad2397494e7e0a79aPaul Westbrook expandedState = (!msg.read || msg.starred || messageCursor.isLast()) ? 629cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang ExpansionState.EXPANDED : ExpansionState.SUPER_COLLAPSED; 630839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang } 63108098ec4c894d9a15dfe800ad2397494e7e0a79aPaul Westbrook mViewState.setExpansionState(msg, expandedState); 632839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang 633839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang // save off "read" state from the cursor 634839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang // later, the view may not match the cursor (e.g. conversation marked read on open) 635423bea25992492efea7d414819729f9eae7ce72eAndy Huang // however, if a previous state indicated this message was unread, trust that instead 636423bea25992492efea7d414819729f9eae7ce72eAndy Huang // so "mark unread" marks all originally unread messages 637423bea25992492efea7d414819729f9eae7ce72eAndy Huang mViewState.setReadState(msg, msg.read && !prevState.isUnread(msg)); 638c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang 639cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang // We only want to consider this for inclusion in the super collapsed block if 640cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang // 1) The we don't have previous state about this message (The first time that the 641cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang // user opens a conversation) 642cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang // 2) The previously saved state for this message indicates that this message is 643cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang // in the super collapsed block. 644cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang if (ExpansionState.isSuperCollapsed(expandedState)) { 645cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang // contribute to a super-collapsed block that will be emitted just before the 646cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang // next expanded header 647cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang if (collapsedStart < 0) { 648cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang collapsedStart = pos; 64946dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 650cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang prevCollapsedMsg = msg; 651cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang prevSafeForImages = safeForImages; 652cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang continue; 65346dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 6547bdc3750454efe59617b7df945eadd7e59bee954Andy Huang 65546dfba6160b55a582b344328067e3dafeb881dd9Andy Huang // resolve any deferred decisions on previous collapsed items 65646dfba6160b55a582b344328067e3dafeb881dd9Andy Huang if (collapsedStart >= 0) { 65746dfba6160b55a582b344328067e3dafeb881dd9Andy Huang if (pos - collapsedStart == 1) { 65846dfba6160b55a582b344328067e3dafeb881dd9Andy Huang // special-case for a single collapsed message: no need to super-collapse it 65946dfba6160b55a582b344328067e3dafeb881dd9Andy Huang renderMessage(prevCollapsedMsg, false /* expanded */, 66046dfba6160b55a582b344328067e3dafeb881dd9Andy Huang prevSafeForImages); 66146dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } else { 66246dfba6160b55a582b344328067e3dafeb881dd9Andy Huang renderSuperCollapsedBlock(collapsedStart, pos - 1); 66346dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 66446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang prevCollapsedMsg = null; 66546dfba6160b55a582b344328067e3dafeb881dd9Andy Huang collapsedStart = -1; 66646dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 6672405528bb15245f594277fe7b6efacd6a18ce69eAndy Huang 66808098ec4c894d9a15dfe800ad2397494e7e0a79aPaul Westbrook renderMessage(msg, ExpansionState.isExpanded(expandedState), safeForImages); 669f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang } 6703233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang 6713233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang mWebView.getSettings().setBlockNetworkImage(!allowNetworkImages); 6723233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang 673cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook // If the conversation has specified a base uri, use it here, use mBaseUri 674cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook final String conversationBaseUri = mConversation.conversationBaseUri != null ? 675cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook mConversation.conversationBaseUri.toString() : mBaseUri; 676cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook return mTemplates.endConversation(mBaseUri, conversationBaseUri, 320, 677adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang mWebView.getViewportWidth(), enableContentReadySignal, isOverviewMode(mAccount)); 678f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang } 679f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 68046dfba6160b55a582b344328067e3dafeb881dd9Andy Huang private void renderSuperCollapsedBlock(int start, int end) { 68146dfba6160b55a582b344328067e3dafeb881dd9Andy Huang final int blockPos = mAdapter.addSuperCollapsedBlock(start, end); 68223014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang final int blockPx = measureOverlayHeight(blockPos); 68323014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang mTemplates.appendSuperCollapsedHtml(start, mWebView.screenPxToWebPx(blockPx)); 68446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 68546dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 686839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang private void renderMessage(ConversationMessage msg, boolean expanded, 687839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang boolean safeForImages) { 68846dfba6160b55a582b344328067e3dafeb881dd9Andy Huang final int headerPos = mAdapter.addMessageHeader(msg, expanded); 68946dfba6160b55a582b344328067e3dafeb881dd9Andy Huang final MessageHeaderItem headerItem = (MessageHeaderItem) mAdapter.getItem(headerPos); 69046dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 69146dfba6160b55a582b344328067e3dafeb881dd9Andy Huang final int footerPos = mAdapter.addMessageFooter(headerItem); 69246dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 69346dfba6160b55a582b344328067e3dafeb881dd9Andy Huang // Measure item header and footer heights to allocate spacers in HTML 69446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang // But since the views themselves don't exist yet, render each item temporarily into 69546dfba6160b55a582b344328067e3dafeb881dd9Andy Huang // a host view for measurement. 69623014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang final int headerPx = measureOverlayHeight(headerPos); 69723014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang final int footerPx = measureOverlayHeight(footerPos); 69846dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 699256b35c0a8287f48c28e0d1ba3fae65790063295Andy Huang mTemplates.appendMessageHtml(msg, expanded, safeForImages, 70023014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang mWebView.screenPxToWebPx(headerPx), mWebView.screenPxToWebPx(footerPx)); 70146dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 70246dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 70346dfba6160b55a582b344328067e3dafeb881dd9Andy Huang private String renderCollapsedHeaders(MessageCursor cursor, 70446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang SuperCollapsedBlockItem blockToReplace) { 70546dfba6160b55a582b344328067e3dafeb881dd9Andy Huang final List<ConversationOverlayItem> replacements = Lists.newArrayList(); 70646dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 70746dfba6160b55a582b344328067e3dafeb881dd9Andy Huang mTemplates.reset(); 70846dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 7092b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei // In devices with non-integral density multiplier, screen pixels translate to non-integral 7102b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei // web pixels. Keep track of the error that occurs when we cast all heights to int 7112b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei float error = 0f; 71246dfba6160b55a582b344328067e3dafeb881dd9Andy Huang for (int i = blockToReplace.getStart(), end = blockToReplace.getEnd(); i <= end; i++) { 71346dfba6160b55a582b344328067e3dafeb881dd9Andy Huang cursor.moveToPosition(i); 714839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang final ConversationMessage msg = cursor.getMessage(); 71546dfba6160b55a582b344328067e3dafeb881dd9Andy Huang final MessageHeaderItem header = mAdapter.newMessageHeaderItem(msg, 71646dfba6160b55a582b344328067e3dafeb881dd9Andy Huang false /* expanded */); 71746dfba6160b55a582b344328067e3dafeb881dd9Andy Huang final MessageFooterItem footer = mAdapter.newMessageFooterItem(header); 71846dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 71923014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang final int headerPx = measureOverlayHeight(header); 72023014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang final int footerPx = measureOverlayHeight(footer); 7212b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei error += mWebView.screenPxToWebPxError(headerPx) 7222b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei + mWebView.screenPxToWebPxError(footerPx); 7232b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei 7242b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei // When the error becomes greater than 1 pixel, make the next header 1 pixel taller 7252b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei int correction = 0; 7262b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei if (error >= 1) { 7272b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei correction = 1; 7282b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei error -= 1; 7292b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei } 73046dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 731256b35c0a8287f48c28e0d1ba3fae65790063295Andy Huang mTemplates.appendMessageHtml(msg, false /* expanded */, msg.alwaysShowImages, 7322b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei mWebView.screenPxToWebPx(headerPx) + correction, 7332b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei mWebView.screenPxToWebPx(footerPx)); 73446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang replacements.add(header); 73546dfba6160b55a582b344328067e3dafeb881dd9Andy Huang replacements.add(footer); 736839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang 73708098ec4c894d9a15dfe800ad2397494e7e0a79aPaul Westbrook mViewState.setExpansionState(msg, ExpansionState.COLLAPSED); 73846dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 73946dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 74046dfba6160b55a582b344328067e3dafeb881dd9Andy Huang mAdapter.replaceSuperCollapsedBlock(blockToReplace, replacements); 74106c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang mAdapter.notifyDataSetChanged(); 74246dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 74346dfba6160b55a582b344328067e3dafeb881dd9Andy Huang return mTemplates.emit(); 74446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 74546dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 74646dfba6160b55a582b344328067e3dafeb881dd9Andy Huang private int measureOverlayHeight(int position) { 74746dfba6160b55a582b344328067e3dafeb881dd9Andy Huang return measureOverlayHeight(mAdapter.getItem(position)); 74846dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 74946dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 7507bdc3750454efe59617b7df945eadd7e59bee954Andy Huang /** 751b8331b4565566ca733997398e8c07a26cd2bee98Andy Huang * Measure the height of an adapter view by rendering an adapter item into a temporary 75246dfba6160b55a582b344328067e3dafeb881dd9Andy Huang * host view, and asking the view to immediately measure itself. This method will reuse 7537bdc3750454efe59617b7df945eadd7e59bee954Andy Huang * a previous adapter view from {@link ConversationContainer}'s scrap views if one was generated 7547bdc3750454efe59617b7df945eadd7e59bee954Andy Huang * earlier. 7557bdc3750454efe59617b7df945eadd7e59bee954Andy Huang * <p> 75646dfba6160b55a582b344328067e3dafeb881dd9Andy Huang * After measuring the height, this method also saves the height in the 75746dfba6160b55a582b344328067e3dafeb881dd9Andy Huang * {@link ConversationOverlayItem} for later use in overlay positioning. 7587bdc3750454efe59617b7df945eadd7e59bee954Andy Huang * 75946dfba6160b55a582b344328067e3dafeb881dd9Andy Huang * @param convItem adapter item with data to render and measure 76023014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang * @return height of the rendered view in screen px 7617bdc3750454efe59617b7df945eadd7e59bee954Andy Huang */ 76246dfba6160b55a582b344328067e3dafeb881dd9Andy Huang private int measureOverlayHeight(ConversationOverlayItem convItem) { 7637bdc3750454efe59617b7df945eadd7e59bee954Andy Huang final int type = convItem.getType(); 7647bdc3750454efe59617b7df945eadd7e59bee954Andy Huang 7657bdc3750454efe59617b7df945eadd7e59bee954Andy Huang final View convertView = mConversationContainer.getScrapView(type); 766b8331b4565566ca733997398e8c07a26cd2bee98Andy Huang final View hostView = mAdapter.getView(convItem, convertView, mConversationContainer, 767b8331b4565566ca733997398e8c07a26cd2bee98Andy Huang true /* measureOnly */); 7687bdc3750454efe59617b7df945eadd7e59bee954Andy Huang if (convertView == null) { 7697bdc3750454efe59617b7df945eadd7e59bee954Andy Huang mConversationContainer.addScrapView(type, hostView); 7707bdc3750454efe59617b7df945eadd7e59bee954Andy Huang } 7717bdc3750454efe59617b7df945eadd7e59bee954Andy Huang 7729875bb4fc00347fc76f432a6e9ec5e6987679ca9Andy Huang final int heightPx = mConversationContainer.measureOverlay(hostView); 7737bdc3750454efe59617b7df945eadd7e59bee954Andy Huang convItem.setHeight(heightPx); 7749875bb4fc00347fc76f432a6e9ec5e6987679ca9Andy Huang convItem.markMeasurementValid(); 7757bdc3750454efe59617b7df945eadd7e59bee954Andy Huang 77623014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang return heightPx; 7777bdc3750454efe59617b7df945eadd7e59bee954Andy Huang } 7787bdc3750454efe59617b7df945eadd7e59bee954Andy Huang 7795ff63747a1b5c6e2197528972cbc3ba808b09d8dAndy Huang @Override 7805ff63747a1b5c6e2197528972cbc3ba808b09d8dAndy Huang public void onConversationViewHeaderHeightChange(int newHeight) { 781ab2d998506c83e82ddae25b6ba1419414e1e8122Mark Wei final int h = mWebView.screenPxToWebPx(newHeight); 782ab2d998506c83e82ddae25b6ba1419414e1e8122Mark Wei 783ab2d998506c83e82ddae25b6ba1419414e1e8122Mark Wei mWebView.loadUrl(String.format("javascript:setConversationHeaderSpacerHeight(%s);", h)); 7845ff63747a1b5c6e2197528972cbc3ba808b09d8dAndy Huang } 7855ff63747a1b5c6e2197528972cbc3ba808b09d8dAndy Huang 7863233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang // END conversation header callbacks 7873233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang 7883233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang // START message header callbacks 7893233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang @Override 790c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang public void setMessageSpacerHeight(MessageHeaderItem item, int newSpacerHeightPx) { 791c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang mConversationContainer.invalidateSpacerGeometry(); 792c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang 793c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang // update message HTML spacer height 79423014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang final int h = mWebView.screenPxToWebPx(newSpacerHeightPx); 79523014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang LogUtils.i(LAYOUT_TAG, "setting HTML spacer h=%dwebPx (%dscreenPx)", h, 79623014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang newSpacerHeightPx); 7975349ce14695dc98615108eb26f96806921b0abbaVikram Aggarwal mWebView.loadUrl(String.format("javascript:setMessageHeaderSpacerHeight('%s', %s);", 798014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang mTemplates.getMessageDomId(item.getMessage()), h)); 7993233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang } 8003233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang 8013233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang @Override 802c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang public void setMessageExpanded(MessageHeaderItem item, int newSpacerHeightPx) { 803c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang mConversationContainer.invalidateSpacerGeometry(); 804c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang 805c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang // show/hide the HTML message body and update the spacer height 80623014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang final int h = mWebView.screenPxToWebPx(newSpacerHeightPx); 80723014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang LogUtils.i(LAYOUT_TAG, "setting HTML spacer expanded=%s h=%dwebPx (%dscreenPx)", 80823014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang item.isExpanded(), h, newSpacerHeightPx); 8095349ce14695dc98615108eb26f96806921b0abbaVikram Aggarwal mWebView.loadUrl(String.format("javascript:setMessageBodyVisible('%s', %s, %s);", 810014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang mTemplates.getMessageDomId(item.getMessage()), item.isExpanded(), h)); 811839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang 812014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang mViewState.setExpansionState(item.getMessage(), 81308098ec4c894d9a15dfe800ad2397494e7e0a79aPaul Westbrook item.isExpanded() ? ExpansionState.EXPANDED : ExpansionState.COLLAPSED); 8143233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang } 8153233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang 8163233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang @Override 8173233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang public void showExternalResources(Message msg) { 8183233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang mWebView.getSettings().setBlockNetworkImage(false); 8193233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang mWebView.loadUrl("javascript:unblockImages('" + mTemplates.getMessageDomId(msg) + "');"); 8203233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang } 8213233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang // END message header callbacks 8225ff63747a1b5c6e2197528972cbc3ba808b09d8dAndy Huang 82346dfba6160b55a582b344328067e3dafeb881dd9Andy Huang @Override 82446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang public void onSuperCollapsedClick(SuperCollapsedBlockItem item) { 825f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp MessageCursor cursor = getMessageCursor(); 826f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (cursor == null || !mViewsCreated) { 82746dfba6160b55a582b344328067e3dafeb881dd9Andy Huang return; 82846dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 82946dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 830f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mTempBodiesHtml = renderCollapsedHeaders(cursor, item); 83146dfba6160b55a582b344328067e3dafeb881dd9Andy Huang mWebView.loadUrl("javascript:replaceSuperCollapsedBlock(" + item.getStart() + ")"); 83246dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 83346dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 83447aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang private void showNewMessageNotification(NewMessagesInfo info) { 83547aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang final TextView descriptionView = (TextView) mNewMessageBar.findViewById( 83647aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang R.id.new_message_description); 83747aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang descriptionView.setText(info.getNotificationText()); 83847aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang mNewMessageBar.setVisibility(View.VISIBLE); 83947aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang } 84047aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang 84147aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang private void onNewMessageBarClick() { 84247aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang mNewMessageBar.setVisibility(View.GONE); 84347aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang 844f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp renderConversation(getMessageCursor()); // mCursor is already up-to-date 845f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // per onLoadFinished() 8465fbda023f1b0570e192e03a33834244a05edf200Andy Huang } 8475fbda023f1b0570e192e03a33834244a05edf200Andy Huang 848adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang private static OverlayPosition[] parsePositions(final String[] topArray, 849adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang final String[] bottomArray) { 850adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang final int len = topArray.length; 851adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang final OverlayPosition[] positions = new OverlayPosition[len]; 852b5078b287b1cec38817e342ff054ea901d199329Andy Huang for (int i = 0; i < len; i++) { 853adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang positions[i] = new OverlayPosition( 854adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang Integer.parseInt(topArray[i]), Integer.parseInt(bottomArray[i])); 855b5078b287b1cec38817e342ff054ea901d199329Andy Huang } 856adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang return positions; 857b5078b287b1cec38817e342ff054ea901d199329Andy Huang } 858b5078b287b1cec38817e342ff054ea901d199329Andy Huang 85947aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang @Override 86047aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang public String toString() { 86147aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang // log extra info at DEBUG level or finer 86247aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang final String s = super.toString(); 86347aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang if (!LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG) || mConversation == null) { 86447aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang return s; 86547aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang } 86647aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang return "(" + s + " subj=" + mConversation.subject + ")"; 86747aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang } 86847aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang 8691617481de7c1f9b8dfcd25ba9828e933cf9d5490Andy Huang private Address getAddress(String rawFrom) { 8701617481de7c1f9b8dfcd25ba9828e933cf9d5490Andy Huang Address addr = mAddressCache.get(rawFrom); 8711617481de7c1f9b8dfcd25ba9828e933cf9d5490Andy Huang if (addr == null) { 8721617481de7c1f9b8dfcd25ba9828e933cf9d5490Andy Huang addr = Address.getEmailAddress(rawFrom); 8731617481de7c1f9b8dfcd25ba9828e933cf9d5490Andy Huang mAddressCache.put(rawFrom, addr); 8741617481de7c1f9b8dfcd25ba9828e933cf9d5490Andy Huang } 8751617481de7c1f9b8dfcd25ba9828e933cf9d5490Andy Huang return addr; 8761617481de7c1f9b8dfcd25ba9828e933cf9d5490Andy Huang } 8771617481de7c1f9b8dfcd25ba9828e933cf9d5490Andy Huang 8789d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang private void ensureContentSizeChangeListener() { 8799d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang if (mWebViewSizeChangeListener == null) { 8809d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang mWebViewSizeChangeListener = new ConversationWebView.ContentSizeChangeListener() { 8819d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang @Override 8829d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang public void onHeightChange(int h) { 8839d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang // When WebKit says the DOM height has changed, re-measure 8849d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang // bodies and re-position their headers. 8859d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang // This is separate from the typical JavaScript DOM change 8869d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang // listeners because cases like NARROW_COLUMNS text reflow do not trigger DOM 8879d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang // events. 8889d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang mWebView.loadUrl("javascript:measurePositions();"); 8899d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 8909d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang }; 8919d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 8929d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang mWebView.setContentSizeChangeListener(mWebViewSizeChangeListener); 8939d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 8949d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 895adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang private static boolean isOverviewMode(Account acct) { 896adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang return acct.settings.conversationViewMode == UIProvider.ConversationViewMode.OVERVIEW; 897adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang } 898adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang 899adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang private void setupOverviewMode() { 900adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang final boolean overviewMode = isOverviewMode(mAccount); 901adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang final WebSettings settings = mWebView.getSettings(); 90206def56be1f03acd2e5706ecf207ab147ce6f5c4Andy Huang settings.setUseWideViewPort(overviewMode); 903adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang settings.setSupportZoom(overviewMode); 904adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang if (overviewMode) { 905adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang settings.setBuiltInZoomControls(true); 906adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang settings.setDisplayZoomControls(false); 907adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang } 908adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang } 909adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang 910542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook private class ConversationWebViewClient extends AbstractConversationWebViewClient { 91117a9cde3b0e28fc98fdeda19de81e18056eb09dbAndy Huang @Override 91217a9cde3b0e28fc98fdeda19de81e18056eb09dbAndy Huang public void onPageFinished(WebView view, String url) { 9139a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang // Ignore unsafe calls made after a fragment is detached from an activity. 9149a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang // This method needs to, for example, get at the loader manager, which needs 9159a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang // the fragment to be added. 916de56e970299fb413e4136dc12a7b08c5cc89e1c1Andy Huang final ControllableActivity activity = (ControllableActivity) getActivity(); 9179a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang if (!isAdded() || !mViewsCreated) { 918de56e970299fb413e4136dc12a7b08c5cc89e1c1Andy Huang LogUtils.i(LOG_TAG, "ignoring CVF.onPageFinished, url=%s fragment=%s", url, 919b95da853ffbff343c8ca11470de2f0047e0807a6Andy Huang ConversationViewFragment.this); 920b95da853ffbff343c8ca11470de2f0047e0807a6Andy Huang return; 921b95da853ffbff343c8ca11470de2f0047e0807a6Andy Huang } 922b95da853ffbff343c8ca11470de2f0047e0807a6Andy Huang 92330bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang LogUtils.i(LOG_TAG, "IN CVF.onPageFinished, url=%s fragment=%s wv=%s t=%sms", url, 92430bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang ConversationViewFragment.this, view, 92563b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang (SystemClock.uptimeMillis() - mWebViewLoadStartMs)); 926632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 9279d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang ensureContentSizeChangeListener(); 9289d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 9293bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp if (!mEnableContentReadySignal) { 9307d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang revealConversation(); 9313bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp } 9329d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 9339a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang final Set<String> emailAddresses = Sets.newHashSet(); 9349a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang for (Address addr : mAddressCache.values()) { 9359a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang emailAddresses.add(addr.getAddress()); 936b8331b4565566ca733997398e8c07a26cd2bee98Andy Huang } 9379a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang ContactLoaderCallbacks callbacks = getContactInfoSource(); 9389a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang getContactInfoSource().setSenders(emailAddresses); 9399a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang getLoaderManager().restartLoader(CONTACT_LOADER, Bundle.EMPTY, callbacks); 94017a9cde3b0e28fc98fdeda19de81e18056eb09dbAndy Huang } 94117a9cde3b0e28fc98fdeda19de81e18056eb09dbAndy Huang 942af5d4e0e2a5ce3300fffa0c484431d83adb89ee8Andy Huang @Override 943af5d4e0e2a5ce3300fffa0c484431d83adb89ee8Andy Huang public boolean shouldOverrideUrlLoading(WebView view, String url) { 944542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook return mViewsCreated && super.shouldOverrideUrlLoading(view, url); 945af5d4e0e2a5ce3300fffa0c484431d83adb89ee8Andy Huang } 94617a9cde3b0e28fc98fdeda19de81e18056eb09dbAndy Huang } 94717a9cde3b0e28fc98fdeda19de81e18056eb09dbAndy Huang 948f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang /** 949f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * NOTE: all public methods must be listed in the proguard flags so that they can be accessed 950f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * via reflection and not stripped. 951f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * 952f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang */ 953f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang private class MailJsBridge { 954f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 955f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang @SuppressWarnings("unused") 956974c966c14000d42f089e2021b098d5cf61f9245Mindy Pereira @JavascriptInterface 957adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang public void onWebContentGeometryChange(final String[] overlayTopStrs, 958adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang final String[] overlayBottomStrs) { 9599a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang getHandler().post(new FragmentRunnable("onWebContentGeometryChange") { 9601b3cc47f54072105c161d6ed557550e0e149b8bbmindyp 9619a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang @Override 9629a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang public void go() { 9639a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang try { 96446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang if (!mViewsCreated) { 9651b3cc47f54072105c161d6ed557550e0e149b8bbmindyp LogUtils.d(LOG_TAG, "ignoring webContentGeometryChange because views" 9661b3cc47f54072105c161d6ed557550e0e149b8bbmindyp + " are gone, %s", ConversationViewFragment.this); 96746dfba6160b55a582b344328067e3dafeb881dd9Andy Huang return; 96846dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 969adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang mConversationContainer.onGeometryChange( 970adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang parsePositions(overlayTopStrs, overlayBottomStrs)); 9711b3cc47f54072105c161d6ed557550e0e149b8bbmindyp if (mDiff != 0) { 9721b3cc47f54072105c161d6ed557550e0e149b8bbmindyp // SCROLL! 9731b3cc47f54072105c161d6ed557550e0e149b8bbmindyp int scale = (int) (mWebView.getScale() / mWebView.getInitialScale()); 9741b3cc47f54072105c161d6ed557550e0e149b8bbmindyp if (scale > 1) { 9751b3cc47f54072105c161d6ed557550e0e149b8bbmindyp mWebView.scrollBy(0, (mDiff * (scale - 1))); 9761b3cc47f54072105c161d6ed557550e0e149b8bbmindyp } 9771b3cc47f54072105c161d6ed557550e0e149b8bbmindyp mDiff = 0; 9781b3cc47f54072105c161d6ed557550e0e149b8bbmindyp } 9799a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang } catch (Throwable t) { 9809a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang LogUtils.e(LOG_TAG, t, "Error in MailJsBridge.onWebContentGeometryChange"); 98151067133f35911bf4b43f97be52b00e5bd9f07abAndy Huang } 9829a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang } 9839a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang }); 98446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 98551067133f35911bf4b43f97be52b00e5bd9f07abAndy Huang 98646dfba6160b55a582b344328067e3dafeb881dd9Andy Huang @SuppressWarnings("unused") 987974c966c14000d42f089e2021b098d5cf61f9245Mindy Pereira @JavascriptInterface 98846dfba6160b55a582b344328067e3dafeb881dd9Andy Huang public String getTempMessageBodies() { 98946dfba6160b55a582b344328067e3dafeb881dd9Andy Huang try { 99046dfba6160b55a582b344328067e3dafeb881dd9Andy Huang if (!mViewsCreated) { 99146dfba6160b55a582b344328067e3dafeb881dd9Andy Huang return ""; 992f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang } 99346dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 99446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang final String s = mTempBodiesHtml; 99546dfba6160b55a582b344328067e3dafeb881dd9Andy Huang mTempBodiesHtml = null; 99646dfba6160b55a582b344328067e3dafeb881dd9Andy Huang return s; 99746dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } catch (Throwable t) { 99846dfba6160b55a582b344328067e3dafeb881dd9Andy Huang LogUtils.e(LOG_TAG, t, "Error in MailJsBridge.getTempMessageBodies"); 99946dfba6160b55a582b344328067e3dafeb881dd9Andy Huang return ""; 100046dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 1001f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang } 1002f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 1003014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang @SuppressWarnings("unused") 1004014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang @JavascriptInterface 1005014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang public String getMessageBody(String domId) { 1006014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang try { 1007014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang final MessageCursor cursor = getMessageCursor(); 1008014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang if (!mViewsCreated || cursor == null) { 1009014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang return ""; 1010014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang } 1011014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang 1012014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang int pos = -1; 1013014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang while (cursor.moveToPosition(++pos)) { 1014014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang final ConversationMessage msg = cursor.getMessage(); 1015014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang if (TextUtils.equals(domId, mTemplates.getMessageDomId(msg))) { 1016014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang return msg.getBodyAsHtml(); 1017014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang } 1018014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang } 1019014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang 1020014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang return ""; 1021014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang 1022014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang } catch (Throwable t) { 1023014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang LogUtils.e(LOG_TAG, t, "Error in MailJsBridge.getMessageBody"); 1024014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang return ""; 1025014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang } 1026014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang } 1027014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang 10283bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp @SuppressWarnings("unused") 1029974c966c14000d42f089e2021b098d5cf61f9245Mindy Pereira @JavascriptInterface 10303bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp public void onContentReady() { 10319a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang getHandler().post(new FragmentRunnable("onContentReady") { 10329a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang @Override 10339a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang public void go() { 10349a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang try { 103563b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang if (mWebViewLoadStartMs != 0) { 103663b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang LogUtils.i(LOG_TAG, "IN CVF.onContentReady, f=%s vis=%s t=%sms", 103763b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang ConversationViewFragment.this, 103863b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang isUserVisible(), 103963b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang (SystemClock.uptimeMillis() - mWebViewLoadStartMs)); 104063b3c6725d60386f564ab53e3ba6495f0c158d9bAndy Huang } 10417d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang revealConversation(); 10429a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang } catch (Throwable t) { 10439a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang LogUtils.e(LOG_TAG, t, "Error in MailJsBridge.onContentReady"); 10449a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang // Still try to show the conversation. 10459a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang revealConversation(); 10463bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp } 10479a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang } 10489a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang }); 10493bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp } 1050e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang 1051e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang @SuppressWarnings("unused") 1052e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang @JavascriptInterface 1053e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang public float getScrollYPercent() { 1054e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang try { 1055e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang return mWebViewYPercent; 1056e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang } catch (Throwable t) { 1057e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang LogUtils.e(LOG_TAG, t, "Error in MailJsBridge.getScrollYPercent"); 1058e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang return 0f; 1059e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang } 1060e964eeec3a088b3f4c29b68d41f99e43a321ac52Andy Huang } 1061674afa42682908640167fc6109b76f6f843e6fbeMindy Pereira } 1062f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 106347aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang private class NewMessagesInfo { 106447aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang int count; 106506c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang int countFromSelf; 106647aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang String senderAddress; 106747aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang 106847aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang /** 106947aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang * Return the display text for the new message notification overlay. It will be formatted 107047aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang * appropriately for a single new message vs. multiple new messages. 107147aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang * 107247aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang * @return display text 107347aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang */ 107447aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang public String getNotificationText() { 1075ad0c30d0517e06c472c76b11795092f5e67d8f5amindyp Resources res = getResources(); 107647aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang if (count > 1) { 1077ad0c30d0517e06c472c76b11795092f5e67d8f5amindyp return res.getString(R.string.new_incoming_messages_many, count); 107847aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang } else { 10791617481de7c1f9b8dfcd25ba9828e933cf9d5490Andy Huang final Address addr = getAddress(senderAddress); 1080ad0c30d0517e06c472c76b11795092f5e67d8f5amindyp return res.getString(R.string.new_incoming_messages_one, 1081ad0c30d0517e06c472c76b11795092f5e67d8f5amindyp TextUtils.isEmpty(addr.getName()) ? addr.getAddress() : addr.getName()); 108247aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang } 108347aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang } 108447aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang } 108547aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang 1086f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 1087014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang public void onMessageCursorLoadFinished(Loader<Cursor> loader, MessageCursor newCursor, 1088014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang MessageCursor oldCursor) { 1089f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp /* 1090f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * what kind of changes affect the MessageCursor? 1. new message(s) 2. 1091f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * read/unread state change 3. deleted message, either regular or draft 1092f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * 4. updated message, either from self or from others, updated in 1093f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * content or state or sender 5. star/unstar of message (technically 1094f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * similar to #1) 6. other label change Use MessageCursor.hashCode() to 1095f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * sort out interesting vs. no-op cursor updates. 1096f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp */ 1097014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang 1098233d435cba55eda0df580d3549b8a4e3b8d789a8Andy Huang if (oldCursor != null && !oldCursor.isClosed()) { 1099014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang final NewMessagesInfo info = getNewIncomingMessagesInfo(newCursor); 1100014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang 1101014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang if (info.count > 0) { 1102014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang // don't immediately render new incoming messages from other 1103014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang // senders 1104014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang // (to avoid a new message from losing the user's focus) 1105014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang LogUtils.i(LOG_TAG, "CONV RENDER: conversation updated" 11069d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang + ", holding cursor for new incoming message (%s)", this); 1107014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang showNewMessageNotification(info); 1108014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang return; 1109014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang } 1110014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang 111106c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang final int oldState = oldCursor.getStateHashCode(); 111206c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang final boolean changed = newCursor.getStateHashCode() != oldState; 1113233d435cba55eda0df580d3549b8a4e3b8d789a8Andy Huang 1114014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang if (!changed) { 1115014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang final boolean processedInPlace = processInPlaceUpdates(newCursor, oldCursor); 1116014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang if (processedInPlace) { 11179d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang LogUtils.i(LOG_TAG, "CONV RENDER: processed update(s) in place (%s)", this); 11181ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang } else { 1119f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp LogUtils.i(LOG_TAG, "CONV RENDER: uninteresting update" 11209d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang + ", ignoring this conversation update (%s)", this); 11211ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang } 11221ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang return; 112306c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang } else if (info.countFromSelf == 1) { 112406c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang // Special-case the very common case of a new cursor that is the same as the old 112506c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang // one, except that there is a new message from yourself. This happens upon send. 112606c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang final boolean sameExceptNewLast = newCursor.getStateHashCode(1) == oldState; 112706c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang if (sameExceptNewLast) { 112806c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang LogUtils.i(LOG_TAG, "CONV RENDER: update is a single new message from self" 112906c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang + " (%s)", this); 113006c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang newCursor.moveToLast(); 113106c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang processNewOutgoingMessage(newCursor.getMessage()); 113206c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang return; 113306c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang } 11341ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang } 11356766b6e5468d2f1935587b3bc1f8e65be94cd6fbAndy Huang // cursors are different, and not due to an incoming message. fall 11366766b6e5468d2f1935587b3bc1f8e65be94cd6fbAndy Huang // through and render. 11376766b6e5468d2f1935587b3bc1f8e65be94cd6fbAndy Huang LogUtils.i(LOG_TAG, "CONV RENDER: conversation updated" 11386766b6e5468d2f1935587b3bc1f8e65be94cd6fbAndy Huang + ", but not due to incoming message. rendering. (%s)", this); 113906c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang 114006c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang if (DEBUG_DUMP_CURSOR_CONTENTS) { 114106c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang LogUtils.i(LOG_TAG, "old cursor: %s", oldCursor.getDebugDump()); 114206c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang LogUtils.i(LOG_TAG, "new cursor: %s", newCursor.getDebugDump()); 114306c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang } 11446766b6e5468d2f1935587b3bc1f8e65be94cd6fbAndy Huang } else { 11456766b6e5468d2f1935587b3bc1f8e65be94cd6fbAndy Huang LogUtils.i(LOG_TAG, "CONV RENDER: initial render. (%s)", this); 1146b8331b4565566ca733997398e8c07a26cd2bee98Andy Huang } 1147b8331b4565566ca733997398e8c07a26cd2bee98Andy Huang 11484071c2f73218ce75750345557bb31a9110737841Mark Wei // if layout hasn't happened, delay render 11494071c2f73218ce75750345557bb31a9110737841Mark Wei // This is needed in addition to the showConversation() delay to speed 11504071c2f73218ce75750345557bb31a9110737841Mark Wei // up rotation and restoration. 11514071c2f73218ce75750345557bb31a9110737841Mark Wei if (mConversationContainer.getWidth() == 0) { 11524071c2f73218ce75750345557bb31a9110737841Mark Wei mNeedRender = true; 11534071c2f73218ce75750345557bb31a9110737841Mark Wei mConversationContainer.addOnLayoutChangeListener(this); 11544071c2f73218ce75750345557bb31a9110737841Mark Wei } else { 11554071c2f73218ce75750345557bb31a9110737841Mark Wei renderConversation(newCursor); 11564071c2f73218ce75750345557bb31a9110737841Mark Wei } 1157b8331b4565566ca733997398e8c07a26cd2bee98Andy Huang } 1158b8331b4565566ca733997398e8c07a26cd2bee98Andy Huang 1159f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private NewMessagesInfo getNewIncomingMessagesInfo(MessageCursor newCursor) { 1160f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp final NewMessagesInfo info = new NewMessagesInfo(); 1161b8331b4565566ca733997398e8c07a26cd2bee98Andy Huang 1162f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp int pos = -1; 1163f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp while (newCursor.moveToPosition(++pos)) { 1164f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp final Message m = newCursor.getMessage(); 1165f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (!mViewState.contains(m)) { 1166f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp LogUtils.i(LOG_TAG, "conversation diff: found new msg: %s", m.uri); 1167f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 1168f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp final Address from = getAddress(m.from); 1169f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // distinguish ours from theirs 1170f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // new messages from the account owner should not trigger a 1171f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // notification 1172f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (mAccount.ownsFromAddress(from.getAddress())) { 1173f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp LogUtils.i(LOG_TAG, "found message from self: %s", m.uri); 117406c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang info.countFromSelf++; 1175f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp continue; 1176f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 1177b8331b4565566ca733997398e8c07a26cd2bee98Andy Huang 1178f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp info.count++; 1179f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp info.senderAddress = m.from; 1180b8331b4565566ca733997398e8c07a26cd2bee98Andy Huang } 1181b8331b4565566ca733997398e8c07a26cd2bee98Andy Huang } 1182f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return info; 1183b8331b4565566ca733997398e8c07a26cd2bee98Andy Huang } 1184b8331b4565566ca733997398e8c07a26cd2bee98Andy Huang 1185014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang private boolean processInPlaceUpdates(MessageCursor newCursor, MessageCursor oldCursor) { 1186014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang final Set<String> idsOfChangedBodies = Sets.newHashSet(); 11876b3d0d9ab407c3d8b6bcb73bddbfd23f2513bb83Andy Huang final List<Integer> changedOverlayPositions = Lists.newArrayList(); 11886b3d0d9ab407c3d8b6bcb73bddbfd23f2513bb83Andy Huang 1189014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang boolean changed = false; 1190014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang 1191014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang int pos = 0; 1192014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang while (true) { 1193014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang if (!newCursor.moveToPosition(pos) || !oldCursor.moveToPosition(pos)) { 1194014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang break; 1195014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang } 1196014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang 1197014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang final ConversationMessage newMsg = newCursor.getMessage(); 1198014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang final ConversationMessage oldMsg = oldCursor.getMessage(); 1199014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang 12002a1e8e30bdd02dc08bdf2f878201b279f60d5142Andy Huang if (!TextUtils.equals(newMsg.from, oldMsg.from) || 12012a1e8e30bdd02dc08bdf2f878201b279f60d5142Andy Huang newMsg.isSending != oldMsg.isSending) { 12026b3d0d9ab407c3d8b6bcb73bddbfd23f2513bb83Andy Huang mAdapter.updateItemsForMessage(newMsg, changedOverlayPositions); 12032a1e8e30bdd02dc08bdf2f878201b279f60d5142Andy Huang LogUtils.i(LOG_TAG, "msg #%d (%d): detected from/sending change. isSending=%s", 12042a1e8e30bdd02dc08bdf2f878201b279f60d5142Andy Huang pos, newMsg.id, newMsg.isSending); 1205014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang } 1206014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang 1207014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang // update changed message bodies in-place 1208014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang if (!TextUtils.equals(newMsg.bodyHtml, oldMsg.bodyHtml) || 1209014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang !TextUtils.equals(newMsg.bodyText, oldMsg.bodyText)) { 1210014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang // maybe just set a flag to notify JS to re-request changed bodies 1211014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang idsOfChangedBodies.add('"' + mTemplates.getMessageDomId(newMsg) + '"'); 1212014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang LogUtils.i(LOG_TAG, "msg #%d (%d): detected body change", pos, newMsg.id); 1213014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang } 1214014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang 1215014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang pos++; 1216014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang } 1217014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang 12186b3d0d9ab407c3d8b6bcb73bddbfd23f2513bb83Andy Huang 12196b3d0d9ab407c3d8b6bcb73bddbfd23f2513bb83Andy Huang if (!changedOverlayPositions.isEmpty()) { 122006c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang // notify once after the entire adapter is updated 12216b3d0d9ab407c3d8b6bcb73bddbfd23f2513bb83Andy Huang mConversationContainer.onOverlayModelUpdate(changedOverlayPositions); 12226b3d0d9ab407c3d8b6bcb73bddbfd23f2513bb83Andy Huang changed = true; 122306c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang } 122406c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang 1225014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang if (!idsOfChangedBodies.isEmpty()) { 1226014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang mWebView.loadUrl(String.format("javascript:replaceMessageBodies([%s]);", 1227014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang TextUtils.join(",", idsOfChangedBodies))); 1228014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang changed = true; 1229014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang } 1230014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang 1231014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang return changed; 1232014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang } 1233014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang 123406c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang private void processNewOutgoingMessage(ConversationMessage msg) { 123506c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang mTemplates.reset(); 123606c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang // this method will add some items to mAdapter, but we deliberately want to avoid notifying 123706c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang // adapter listeners (i.e. ConversationContainer) until onWebContentGeometryChange is next 123806c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang // called, to prevent N+1 headers rendering with N message bodies. 123906c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang renderMessage(msg, true /* expanded */, msg.alwaysShowImages); 124006c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang mTempBodiesHtml = mTemplates.emit(); 124106c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang 124206c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang mViewState.setExpansionState(msg, ExpansionState.EXPANDED); 124306c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang // FIXME: should the provider set this as initial state? 124406c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang mViewState.setReadState(msg, false /* read */); 124506c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang 124691d782abc8015bd651fb5d0252b4d1ef369ec57bAndy Huang // From now until the updated spacer geometry is returned, the adapter items are mismatched 124791d782abc8015bd651fb5d0252b4d1ef369ec57bAndy Huang // with the existing spacers. Do not let them layout. 124891d782abc8015bd651fb5d0252b4d1ef369ec57bAndy Huang mConversationContainer.invalidateSpacerGeometry(); 124991d782abc8015bd651fb5d0252b4d1ef369ec57bAndy Huang 125006c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang mWebView.loadUrl("javascript:appendMessageHtml();"); 125106c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang } 125206c0362f59437f3ea2b5832272fb66158bb4b8c0Andy Huang 1253cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook private class SetCookieTask extends AsyncTask<Void, Void, Void> { 1254cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook final String mUri; 1255b8361c9f8938b74977316319885998aae09aab77Paul Westbrook final Uri mAccountCookieQueryUri; 1256b8361c9f8938b74977316319885998aae09aab77Paul Westbrook final ContentResolver mResolver; 1257cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook 1258b8361c9f8938b74977316319885998aae09aab77Paul Westbrook SetCookieTask(Context context, Uri baseUri, Uri accountCookieQueryUri) { 1259b8361c9f8938b74977316319885998aae09aab77Paul Westbrook mUri = baseUri.toString(); 1260b8361c9f8938b74977316319885998aae09aab77Paul Westbrook mAccountCookieQueryUri = accountCookieQueryUri; 1261b8361c9f8938b74977316319885998aae09aab77Paul Westbrook mResolver = context.getContentResolver(); 1262cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook } 1263cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook 1264cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook @Override 1265cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook public Void doInBackground(Void... args) { 1266b8361c9f8938b74977316319885998aae09aab77Paul Westbrook // First query for the coookie string from the UI provider 1267b8361c9f8938b74977316319885998aae09aab77Paul Westbrook final Cursor cookieCursor = mResolver.query(mAccountCookieQueryUri, 1268b8361c9f8938b74977316319885998aae09aab77Paul Westbrook UIProvider.ACCOUNT_COOKIE_PROJECTION, null, null, null); 1269b8361c9f8938b74977316319885998aae09aab77Paul Westbrook if (cookieCursor == null) { 1270b8361c9f8938b74977316319885998aae09aab77Paul Westbrook return null; 1271b8361c9f8938b74977316319885998aae09aab77Paul Westbrook } 1272b8361c9f8938b74977316319885998aae09aab77Paul Westbrook 1273b8361c9f8938b74977316319885998aae09aab77Paul Westbrook try { 1274b8361c9f8938b74977316319885998aae09aab77Paul Westbrook if (cookieCursor.moveToFirst()) { 1275b8361c9f8938b74977316319885998aae09aab77Paul Westbrook final String cookie = cookieCursor.getString( 1276b8361c9f8938b74977316319885998aae09aab77Paul Westbrook cookieCursor.getColumnIndex(UIProvider.AccountCookieColumns.COOKIE)); 1277b8361c9f8938b74977316319885998aae09aab77Paul Westbrook 1278b8361c9f8938b74977316319885998aae09aab77Paul Westbrook if (cookie != null) { 1279b8361c9f8938b74977316319885998aae09aab77Paul Westbrook final CookieSyncManager csm = 1280b8361c9f8938b74977316319885998aae09aab77Paul Westbrook CookieSyncManager.createInstance(getContext()); 1281b8361c9f8938b74977316319885998aae09aab77Paul Westbrook CookieManager.getInstance().setCookie(mUri, cookie); 1282b8361c9f8938b74977316319885998aae09aab77Paul Westbrook csm.sync(); 1283b8361c9f8938b74977316319885998aae09aab77Paul Westbrook } 1284b8361c9f8938b74977316319885998aae09aab77Paul Westbrook } 1285b8361c9f8938b74977316319885998aae09aab77Paul Westbrook 1286b8361c9f8938b74977316319885998aae09aab77Paul Westbrook } finally { 1287b8361c9f8938b74977316319885998aae09aab77Paul Westbrook cookieCursor.close(); 1288b8361c9f8938b74977316319885998aae09aab77Paul Westbrook } 1289b8361c9f8938b74977316319885998aae09aab77Paul Westbrook 1290b8361c9f8938b74977316319885998aae09aab77Paul Westbrook 1291cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook return null; 1292cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook } 1293cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook } 129436280f3c2939bb2dacb4077bd528900346ff4bb9mindyp 129526d4d2d9c43c499f458808f050ec73ea3c28dec4mindyp @Override 129636280f3c2939bb2dacb4077bd528900346ff4bb9mindyp public void onConversationUpdated(Conversation conv) { 129736280f3c2939bb2dacb4077bd528900346ff4bb9mindyp final ConversationViewHeader headerView = (ConversationViewHeader) mConversationContainer 129836280f3c2939bb2dacb4077bd528900346ff4bb9mindyp .findViewById(R.id.conversation_header); 1299b2b98ba97983f225eec19dc9bc5333e6ef80ee15mindyp mConversation = conv; 13009e0b236be55cf9dab8f1670a9d91ed16e055a9d0mindyp if (headerView != null) { 13019e0b236be55cf9dab8f1670a9d91ed16e055a9d0mindyp headerView.onConversationUpdated(conv); 13029e0b236be55cf9dab8f1670a9d91ed16e055a9d0mindyp } 130336280f3c2939bb2dacb4077bd528900346ff4bb9mindyp } 13044071c2f73218ce75750345557bb31a9110737841Mark Wei 13054071c2f73218ce75750345557bb31a9110737841Mark Wei @Override 13064071c2f73218ce75750345557bb31a9110737841Mark Wei public void onLayoutChange(View v, int left, int top, int right, 13074071c2f73218ce75750345557bb31a9110737841Mark Wei int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { 13084071c2f73218ce75750345557bb31a9110737841Mark Wei boolean sizeChanged = mNeedRender 13094071c2f73218ce75750345557bb31a9110737841Mark Wei && mConversationContainer.getWidth() != 0; 13104071c2f73218ce75750345557bb31a9110737841Mark Wei if (sizeChanged) { 13114071c2f73218ce75750345557bb31a9110737841Mark Wei mNeedRender = false; 13124071c2f73218ce75750345557bb31a9110737841Mark Wei mConversationContainer.removeOnLayoutChangeListener(this); 13134071c2f73218ce75750345557bb31a9110737841Mark Wei renderConversation(getMessageCursor()); 13144071c2f73218ce75750345557bb31a9110737841Mark Wei } 13154071c2f73218ce75750345557bb31a9110737841Mark Wei } 13161b3cc47f54072105c161d6ed557550e0e149b8bbmindyp 13171b3cc47f54072105c161d6ed557550e0e149b8bbmindyp @Override 13181b3cc47f54072105c161d6ed557550e0e149b8bbmindyp public void setMessageDetailsExpanded(MessageHeaderItem i, boolean expanded, 13191b3cc47f54072105c161d6ed557550e0e149b8bbmindyp int heightBefore) { 13201b3cc47f54072105c161d6ed557550e0e149b8bbmindyp mDiff = (expanded ? 1 : -1) * Math.abs(i.getHeight() - heightBefore); 13211b3cc47f54072105c161d6ed557550e0e149b8bbmindyp } 13229b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira} 1323