ConversationViewFragment.java revision afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9f
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 219b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereiraimport android.content.Context; 228e915724b6e4374da9b70161ee0a55f0c763e563Mindy Pereiraimport android.content.Loader; 239b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereiraimport android.database.Cursor; 24cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrookimport android.os.AsyncTask; 259b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereiraimport android.os.Bundle; 263bcf180f8104bc27319086a9a6ece5a3c2917c37mindypimport android.os.SystemClock; 2747aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huangimport android.text.TextUtils; 289b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereiraimport android.view.LayoutInflater; 299b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereiraimport android.view.View; 309b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereiraimport android.view.ViewGroup; 31f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangimport android.webkit.ConsoleMessage; 32cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrookimport android.webkit.CookieManager; 33cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrookimport android.webkit.CookieSyncManager; 34974c966c14000d42f089e2021b098d5cf61f9245Mindy Pereiraimport android.webkit.JavascriptInterface; 35f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangimport android.webkit.WebChromeClient; 36f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangimport android.webkit.WebSettings; 3717a9cde3b0e28fc98fdeda19de81e18056eb09dbAndy Huangimport android.webkit.WebView; 3817a9cde3b0e28fc98fdeda19de81e18056eb09dbAndy Huangimport android.webkit.WebViewClient; 3947aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huangimport android.widget.TextView; 409b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira 4159e0b18db1bd06cfb74693d7dbb0cb48112a69b1Andy Huangimport com.android.mail.FormattedDateBuilder; 429b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereiraimport com.android.mail.R; 435ff63747a1b5c6e2197528972cbc3ba808b09d8dAndy Huangimport com.android.mail.browse.ConversationContainer; 4446dfba6160b55a582b344328067e3dafeb881dd9Andy Huangimport com.android.mail.browse.ConversationOverlayItem; 457bdc3750454efe59617b7df945eadd7e59bee954Andy Huangimport com.android.mail.browse.ConversationViewAdapter; 4656d83850db72592a16f4e6ee9e0d59b60ec0824aMark Weiimport com.android.mail.browse.ScrollIndicatorsView; 4728b7aee7fa1f7d096d33fc823a88a64f7a3fa79dAndy Huangimport com.android.mail.browse.ConversationViewAdapter.ConversationAccountController; 4846dfba6160b55a582b344328067e3dafeb881dd9Andy Huangimport com.android.mail.browse.ConversationViewAdapter.MessageFooterItem; 497bdc3750454efe59617b7df945eadd7e59bee954Andy Huangimport com.android.mail.browse.ConversationViewAdapter.MessageHeaderItem; 5046dfba6160b55a582b344328067e3dafeb881dd9Andy Huangimport com.android.mail.browse.ConversationViewAdapter.SuperCollapsedBlockItem; 515ff63747a1b5c6e2197528972cbc3ba808b09d8dAndy Huangimport com.android.mail.browse.ConversationViewHeader; 525ff63747a1b5c6e2197528972cbc3ba808b09d8dAndy Huangimport com.android.mail.browse.ConversationWebView; 53dde3f9ff76c1a7f9a6e9fa959a6422a8a8d82e81mindypimport com.android.mail.browse.ConversationWebView.ContentSizeChangeListener; 547bdc3750454efe59617b7df945eadd7e59bee954Andy Huangimport com.android.mail.browse.MessageCursor; 55cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huangimport com.android.mail.browse.MessageCursor.ConversationController; 5628b7aee7fa1f7d096d33fc823a88a64f7a3fa79dAndy Huangimport com.android.mail.browse.MessageCursor.ConversationMessage; 5759e0b18db1bd06cfb74693d7dbb0cb48112a69b1Andy Huangimport com.android.mail.browse.MessageHeaderView; 583233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huangimport com.android.mail.browse.MessageHeaderView.MessageHeaderViewCallbacks; 5946dfba6160b55a582b344328067e3dafeb881dd9Andy Huangimport com.android.mail.browse.SuperCollapsedBlock; 600b7ed6fae6e36f2abc4ca916764177d1879731b4Andy Huangimport com.android.mail.browse.WebViewContextMenu; 619b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereiraimport com.android.mail.providers.Account; 6265fe28fa88daad08f3be4c084ca5b4eaa366d1a7Andy Huangimport com.android.mail.providers.Address; 639b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereiraimport com.android.mail.providers.Conversation; 64f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huangimport com.android.mail.providers.Message; 65cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huangimport com.android.mail.ui.ConversationViewState.ExpansionState; 66b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrookimport com.android.mail.utils.LogTag; 679b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereiraimport com.android.mail.utils.LogUtils; 682e9acfe527426c00b5df2ca66189262dc40c8575Andy Huangimport com.android.mail.utils.Utils; 6946dfba6160b55a582b344328067e3dafeb881dd9Andy Huangimport com.google.common.collect.Lists; 70b8331b4565566ca733997398e8c07a26cd2bee98Andy Huangimport com.google.common.collect.Sets; 7165fe28fa88daad08f3be4c084ca5b4eaa366d1a7Andy Huang 7246dfba6160b55a582b344328067e3dafeb881dd9Andy Huangimport java.util.List; 73b8331b4565566ca733997398e8c07a26cd2bee98Andy Huangimport java.util.Set; 749b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira 75f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 769b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira/** 779b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira * The conversation view UI component. 789b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira */ 79f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyppublic final class ConversationViewFragment extends AbstractConversationViewFragment implements 8046dfba6160b55a582b344328067e3dafeb881dd9Andy Huang MessageHeaderViewCallbacks, 81cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang SuperCollapsedBlock.OnClickListener, 8228b7aee7fa1f7d096d33fc823a88a64f7a3fa79dAndy Huang ConversationController, 8328b7aee7fa1f7d096d33fc823a88a64f7a3fa79dAndy Huang ConversationAccountController { 848e915724b6e4374da9b70161ee0a55f0c763e563Mindy Pereira 85b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrook private static final String LOG_TAG = LogTag.getLogTag(); 86632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang public static final String LAYOUT_TAG = "ConvLayout"; 879b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira 883bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp /** Do not auto load data when create this {@link ConversationView}. */ 893bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp public static final int NO_AUTO_LOAD = 0; 903bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp /** Auto load data but do not show any animation. */ 913bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp public static final int AUTO_LOAD_BACKGROUND = 1; 923bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp /** Auto load data and show animation. */ 933bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp public static final int AUTO_LOAD_VISIBLE = 2; 943bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp 95f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang private ConversationContainer mConversationContainer; 969b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira 97f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang private ConversationWebView mWebView; 989b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira 9956d83850db72592a16f4e6ee9e0d59b60ec0824aMark Wei private ScrollIndicatorsView mScrollIndicators; 10056d83850db72592a16f4e6ee9e0d59b60ec0824aMark Wei 10147aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang private View mNewMessageBar; 10247aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang 103f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang private HtmlConversationTemplates mTemplates; 104f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 105f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang private final MailJsBridge mJsBridge = new MailJsBridge(); 106f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 10717a9cde3b0e28fc98fdeda19de81e18056eb09dbAndy Huang private final WebViewClient mWebViewClient = new ConversationWebViewClient(); 10817a9cde3b0e28fc98fdeda19de81e18056eb09dbAndy Huang 1097bdc3750454efe59617b7df945eadd7e59bee954Andy Huang private ConversationViewAdapter mAdapter; 11051067133f35911bf4b43f97be52b00e5bd9f07abAndy Huang 11151067133f35911bf4b43f97be52b00e5bd9f07abAndy Huang private boolean mViewsCreated; 11251067133f35911bf4b43f97be52b00e5bd9f07abAndy Huang 11346dfba6160b55a582b344328067e3dafeb881dd9Andy Huang /** 11446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang * Temporary string containing the message bodies of the messages within a super-collapsed 11546dfba6160b55a582b344328067e3dafeb881dd9Andy Huang * block, for one-time use during block expansion. We cannot easily pass the body HTML 11646dfba6160b55a582b344328067e3dafeb881dd9Andy Huang * into JS without problematic escaping, so hold onto it momentarily and signal JS to fetch it 11746dfba6160b55a582b344328067e3dafeb881dd9Andy Huang * using {@link MailJsBridge}. 11846dfba6160b55a582b344328067e3dafeb881dd9Andy Huang */ 11946dfba6160b55a582b344328067e3dafeb881dd9Andy Huang private String mTempBodiesHtml; 12046dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 121632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang private int mMaxAutoLoadMessages; 122632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 123632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang private boolean mDeferredConversationLoad; 124632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 1253bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp private boolean mEnableContentReadySignal; 12628b7aee7fa1f7d096d33fc823a88a64f7a3fa79dAndy Huang 127dde3f9ff76c1a7f9a6e9fa959a6422a8a8d82e81mindyp private ContentSizeChangeListener mWebViewSizeChangeListener; 128dde3f9ff76c1a7f9a6e9fa959a6422a8a8d82e81mindyp 129839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang private static final String BUNDLE_VIEW_STATE = "viewstate"; 130f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 131bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang private static final boolean DEBUG_DUMP_CONVERSATION_HTML = false; 13247aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang private static final boolean DISABLE_OFFSCREEN_LOADING = false; 1333bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp protected static final String AUTO_LOAD_KEY = "auto-load"; 134bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang 1356c51158ad3269f157424e6c7bd488425c98da08fVikram Aggarwal /** 1366c51158ad3269f157424e6c7bd488425c98da08fVikram Aggarwal * Constructor needs to be public to handle orientation changes and activity lifecycle events. 1376c51158ad3269f157424e6c7bd488425c98da08fVikram Aggarwal */ 138f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang public ConversationViewFragment() { 1396c51158ad3269f157424e6c7bd488425c98da08fVikram Aggarwal super(); 1409b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira } 1419b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira 1429b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira /** 1439b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira * Creates a new instance of {@link ConversationViewFragment}, initialized 144632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * to display a conversation with other parameters inherited/copied from an existing bundle, 145632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang * typically one created using {@link #makeBasicArgs}. 146632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang */ 147632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang public static ConversationViewFragment newInstance(Bundle existingArgs, 148632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang Conversation conversation) { 149632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang ConversationViewFragment f = new ConversationViewFragment(); 150632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang Bundle args = new Bundle(existingArgs); 151632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang args.putParcelable(ARG_CONVERSATION, conversation); 152632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang f.setArguments(args); 153632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang return f; 154632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 155632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 156f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 157f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void onAccountChanged() { 158f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // settings may have been updated; refresh views that are known to 159f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // depend on settings 160f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mConversationContainer.getSnapHeader().onAccountChanged(); 161f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mAdapter.notifyDataSetChanged(); 162632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 163632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 1649b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira @Override 1659b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira public void onActivityCreated(Bundle savedInstanceState) { 166632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang LogUtils.d(LOG_TAG, "IN CVF.onActivityCreated, this=%s subj=%s", this, 167632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang mConversation.subject); 1689b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira super.onActivityCreated(savedInstanceState); 169f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp Context context = getContext(); 170f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mTemplates = new HtmlConversationTemplates(context); 17159e0b18db1bd06cfb74693d7dbb0cb48112a69b1Andy Huang 172f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp final FormattedDateBuilder dateBuilder = new FormattedDateBuilder(context); 17359e0b18db1bd06cfb74693d7dbb0cb48112a69b1Andy Huang 1748081df46ef5a7794374e41cd1836e778a2da9b31Paul Westbrook mAdapter = new ConversationViewAdapter(mActivity, this, 175f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp getLoaderManager(), this, getContactInfoSource(), this, 1768081df46ef5a7794374e41cd1836e778a2da9b31Paul Westbrook this, mAddressCache, dateBuilder); 17751067133f35911bf4b43f97be52b00e5bd9f07abAndy Huang mConversationContainer.setOverlayAdapter(mAdapter); 17851067133f35911bf4b43f97be52b00e5bd9f07abAndy Huang 17959e0b18db1bd06cfb74693d7dbb0cb48112a69b1Andy Huang // set up snap header (the adapter usually does this with the other ones) 18059e0b18db1bd06cfb74693d7dbb0cb48112a69b1Andy Huang final MessageHeaderView snapHeader = mConversationContainer.getSnapHeader(); 18128b7aee7fa1f7d096d33fc823a88a64f7a3fa79dAndy Huang snapHeader.initialize(dateBuilder, this, mAddressCache); 18259e0b18db1bd06cfb74693d7dbb0cb48112a69b1Andy Huang snapHeader.setCallbacks(this); 183f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp snapHeader.setContactInfoSource(getContactInfoSource()); 18459e0b18db1bd06cfb74693d7dbb0cb48112a69b1Andy Huang 185632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang mMaxAutoLoadMessages = getResources().getInteger(R.integer.max_auto_load_messages); 186632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 187f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mWebView.setOnCreateContextMenuListener(new WebViewContextMenu(getActivity())); 1880b7ed6fae6e36f2abc4ca916764177d1879731b4Andy Huang 1899b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira showConversation(); 190cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook 191cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook if (mConversation.conversationBaseUri != null && 192cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook !TextUtils.isEmpty(mConversation.conversationCookie)) { 193cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook // Set the cookie for this base url 194cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook new SetCookieTask(mConversation.conversationBaseUri.toString(), 195cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook mConversation.conversationCookie).execute(); 196cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook } 1979b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira } 1989b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira 1999b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira @Override 2009b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira public View onCreateView(LayoutInflater inflater, 2019b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira ViewGroup container, Bundle savedInstanceState) { 202839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang if (savedInstanceState != null) { 203839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang mViewState = savedInstanceState.getParcelable(BUNDLE_VIEW_STATE); 204839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang } else { 2054d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook mViewState = getNewViewState(); 206839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang } 207839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang 208632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang View rootView = inflater.inflate(R.layout.conversation_view, container, false); 209f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang mConversationContainer = (ConversationContainer) rootView 210f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang .findViewById(R.id.conversation_container); 21147aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang 21247aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang mNewMessageBar = mConversationContainer.findViewById(R.id.new_message_notification_bar); 21347aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang mNewMessageBar.setOnClickListener(new View.OnClickListener() { 21447aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang @Override 21547aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang public void onClick(View v) { 21647aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang onNewMessageBarClick(); 21747aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang } 21847aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang }); 21947aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang 220ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp instantiateProgressIndicators(rootView); 2213bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp 2225ff63747a1b5c6e2197528972cbc3ba808b09d8dAndy Huang mWebView = (ConversationWebView) mConversationContainer.findViewById(R.id.webview); 223f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 224f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang mWebView.addJavascriptInterface(mJsBridge, "mail"); 2253bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp // On JB or newer, we use the 'webkitAnimationStart' DOM event to signal load complete 2263bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp // Below JB, try to speed up initial render by having the webview do supplemental draws to 2273bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp // custom a software canvas. 228b941fdb95f61cdde4e1775ac9034343951e8b075mindyp // TODO(mindyp): 229b941fdb95f61cdde4e1775ac9034343951e8b075mindyp //PAGE READINESS SIGNAL FOR JELLYBEAN AND NEWER 230b941fdb95f61cdde4e1775ac9034343951e8b075mindyp // Notify the app on 'webkitAnimationStart' of a simple dummy element with a simple no-op 231b941fdb95f61cdde4e1775ac9034343951e8b075mindyp // animation that immediately runs on page load. The app uses this as a signal that the 232b941fdb95f61cdde4e1775ac9034343951e8b075mindyp // content is loaded and ready to draw, since WebView delays firing this event until the 233b941fdb95f61cdde4e1775ac9034343951e8b075mindyp // layers are composited and everything is ready to draw. 234b941fdb95f61cdde4e1775ac9034343951e8b075mindyp // This signal does not seem to be reliable, so just use the old method for now. 23532d911f491afad153983f7519267ddb764927355mindyp mEnableContentReadySignal = Utils.isRunningJellybeanOrLater(); 236afc9b365dc9199ee9b2a1e598b8f40b3c78b6d9fmindyp mWebView.setUseSoftwareLayer(!mEnableContentReadySignal); 23717a9cde3b0e28fc98fdeda19de81e18056eb09dbAndy Huang mWebView.setWebViewClient(mWebViewClient); 238f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang mWebView.setWebChromeClient(new WebChromeClient() { 239f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang @Override 240f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang public boolean onConsoleMessage(ConsoleMessage consoleMessage) { 241f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang LogUtils.i(LOG_TAG, "JS: %s (%s:%d)", consoleMessage.message(), 242f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang consoleMessage.sourceId(), consoleMessage.lineNumber()); 243f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang return true; 244f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang } 245f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang }); 246f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 2473233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang final WebSettings settings = mWebView.getSettings(); 248f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 24956d83850db72592a16f4e6ee9e0d59b60ec0824aMark Wei mScrollIndicators = (ScrollIndicatorsView) rootView.findViewById(R.id.scroll_indicators); 25056d83850db72592a16f4e6ee9e0d59b60ec0824aMark Wei mScrollIndicators.setSourceView(mWebView); 25156d83850db72592a16f4e6ee9e0d59b60ec0824aMark Wei 252f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang settings.setJavaScriptEnabled(true); 253f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang settings.setUseWideViewPort(true); 25423014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang settings.setLoadWithOverviewMode(true); 255f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 256f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang settings.setSupportZoom(true); 257f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang settings.setBuiltInZoomControls(true); 258f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang settings.setDisplayZoomControls(false); 2599b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira 260c319b551bebe40b9607acf0ee1d26a880fde9212Andy Huang final float fontScale = getResources().getConfiguration().fontScale; 261ba283732e7cbcc55a3dbc8ab78950cc38cd078fbAndy Huang final int desiredFontSizePx = getResources() 262ba283732e7cbcc55a3dbc8ab78950cc38cd078fbAndy Huang .getInteger(R.integer.conversation_desired_font_size_px); 263ba283732e7cbcc55a3dbc8ab78950cc38cd078fbAndy Huang final int unstyledFontSizePx = getResources() 264ba283732e7cbcc55a3dbc8ab78950cc38cd078fbAndy Huang .getInteger(R.integer.conversation_unstyled_font_size_px); 265ba283732e7cbcc55a3dbc8ab78950cc38cd078fbAndy Huang 266ba283732e7cbcc55a3dbc8ab78950cc38cd078fbAndy Huang int textZoom = settings.getTextZoom(); 267ba283732e7cbcc55a3dbc8ab78950cc38cd078fbAndy Huang // apply a correction to the default body text style to get regular text to the size we want 268ba283732e7cbcc55a3dbc8ab78950cc38cd078fbAndy Huang textZoom = textZoom * desiredFontSizePx / unstyledFontSizePx; 269ba283732e7cbcc55a3dbc8ab78950cc38cd078fbAndy Huang // then apply any system font scaling 270c319b551bebe40b9607acf0ee1d26a880fde9212Andy Huang textZoom = (int) (textZoom * fontScale); 271c319b551bebe40b9607acf0ee1d26a880fde9212Andy Huang settings.setTextZoom(textZoom); 272c319b551bebe40b9607acf0ee1d26a880fde9212Andy Huang 27351067133f35911bf4b43f97be52b00e5bd9f07abAndy Huang mViewsCreated = true; 27451067133f35911bf4b43f97be52b00e5bd9f07abAndy Huang 2759b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira return rootView; 2769b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira } 2779b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira 2789b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira @Override 279a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang public void onResume() { 280a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang super.onResume(); 281a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang 282a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang // Hacky workaround for http://b/6946182 283a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang Utils.fixSubTreeLayoutIfOrphaned(getView(), "ConversationViewFragment"); 284a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang } 285a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang 286a6e965ef4a7ac2266f0a5509be25ac1e8d272595Andy Huang @Override 287839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang public void onSaveInstanceState(Bundle outState) { 288839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang if (mViewState != null) { 289839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang outState.putParcelable(BUNDLE_VIEW_STATE, mViewState); 290839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang } 291839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang } 292839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang 293839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang @Override 2949b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira public void onDestroyView() { 2959b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira super.onDestroyView(); 29646dfba6160b55a582b344328067e3dafeb881dd9Andy Huang mConversationContainer.setOverlayAdapter(null); 29746dfba6160b55a582b344328067e3dafeb881dd9Andy Huang mAdapter = null; 29851067133f35911bf4b43f97be52b00e5bd9f07abAndy Huang mViewsCreated = false; 2999b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira } 3009b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira 3015ff63747a1b5c6e2197528972cbc3ba808b09d8dAndy Huang @Override 302f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp protected void markUnread() { 303839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang // Ignore unsafe calls made after a fragment is detached from an activity 304839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang final ControllableActivity activity = (ControllableActivity) getActivity(); 305839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang if (activity == null) { 306839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang LogUtils.w(LOG_TAG, "ignoring markUnread for conv=%s", mConversation.id); 307839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang return; 308839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang } 309839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang 31028e31e2417d775b343919cf6018f85871e40a294Andy Huang if (mViewState == null) { 31128e31e2417d775b343919cf6018f85871e40a294Andy Huang LogUtils.i(LOG_TAG, "ignoring markUnread for conv with no view state (%d)", 31228e31e2417d775b343919cf6018f85871e40a294Andy Huang mConversation.id); 31328e31e2417d775b343919cf6018f85871e40a294Andy Huang return; 31428e31e2417d775b343919cf6018f85871e40a294Andy Huang } 315839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang activity.getConversationUpdater().markConversationMessagesUnread(mConversation, 3164a878b606961825fb4fd412b460df75779f09ad2Vikram Aggarwal mViewState.getUnreadMessageUris(), mViewState.getConversationInfo()); 317839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang } 318839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang 319f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 320f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void onUserVisibleHintChanged() { 321f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (mUserVisible && mViewsCreated) { 322f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp Cursor cursor = getMessageCursor(); 323f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (cursor == null && mDeferredConversationLoad) { 324f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // load 325f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp LogUtils.v(LOG_TAG, "Fragment is now user-visible, showing conversation: %s", 326f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mConversation.uri); 327f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp showConversation(); 328f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mDeferredConversationLoad = false; 329f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } else { 330f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp onConversationSeen(); 331632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 33232d911f491afad153983f7519267ddb764927355mindyp } else if (!mUserVisible) { 33332d911f491afad153983f7519267ddb764927355mindyp dismissLoadingStatus(); 334632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 335632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 336632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 3379b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira private void showConversation() { 3383bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp final boolean disableOffscreenLoading = DISABLE_OFFSCREEN_LOADING 3393bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp || (mConversation.isRemote 3403bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp || mConversation.getNumMessages() > mMaxAutoLoadMessages); 34147aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang if (!mUserVisible && disableOffscreenLoading) { 342632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang LogUtils.v(LOG_TAG, "Fragment not user-visible, not showing conversation: %s", 343632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang mConversation.uri); 344632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang mDeferredConversationLoad = true; 345632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang return; 346632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang } 347632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang LogUtils.v(LOG_TAG, 348632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang "Fragment is short or user-visible, immediately rendering conversation: %s", 349632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang mConversation.uri); 3503bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp mWebView.setVisibility(View.VISIBLE); 351f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp getLoaderManager().initLoader(MESSAGE_LOADER, Bundle.EMPTY, getMessageLoaderCallbacks()); 3525460ce477cb1dcc439dd4e5c9e8393470f00f618Andy Huang if (mUserVisible) { 3535460ce477cb1dcc439dd4e5c9e8393470f00f618Andy Huang final SubjectDisplayChanger sdc = mActivity.getSubjectDisplayChanger(); 3545460ce477cb1dcc439dd4e5c9e8393470f00f618Andy Huang if (sdc != null) { 3555460ce477cb1dcc439dd4e5c9e8393470f00f618Andy Huang sdc.setSubject(mConversation.subject); 3565460ce477cb1dcc439dd4e5c9e8393470f00f618Andy Huang } 3575460ce477cb1dcc439dd4e5c9e8393470f00f618Andy Huang } 3583bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp // TODO(mindyp): don't show loading status for a previously rendered 3593bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp // conversation. Ielieve this is better done by making sure don't show loading status 3603bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp // until XX ms have passed without loading completed. 3613bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp showLoadingStatus(); 3628e915724b6e4374da9b70161ee0a55f0c763e563Mindy Pereira } 3638e915724b6e4374da9b70161ee0a55f0c763e563Mindy Pereira 36451067133f35911bf4b43f97be52b00e5bd9f07abAndy Huang private void renderConversation(MessageCursor messageCursor) { 3653bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp final String convHtml = renderMessageBodies(messageCursor, mEnableContentReadySignal); 366bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang 367bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang if (DEBUG_DUMP_CONVERSATION_HTML) { 368bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang java.io.FileWriter fw = null; 369bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang try { 370bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang fw = new java.io.FileWriter("/sdcard/conv" + mConversation.id 371bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang + ".html"); 372bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang fw.write(convHtml); 373bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang } catch (java.io.IOException e) { 374bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang e.printStackTrace(); 375bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang } finally { 376bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang if (fw != null) { 377bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang try { 378bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang fw.close(); 379bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang } catch (java.io.IOException e) { 380bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang e.printStackTrace(); 381bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang } 382bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang } 383bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang } 384bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang } 385bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang 386bd544e3c9c703042b68e6ad611104850ab8e0094Andy Huang mWebView.loadDataWithBaseURL(mBaseUri, convHtml, "text/html", "utf-8", null); 38751067133f35911bf4b43f97be52b00e5bd9f07abAndy Huang } 38851067133f35911bf4b43f97be52b00e5bd9f07abAndy Huang 3897bdc3750454efe59617b7df945eadd7e59bee954Andy Huang /** 3907bdc3750454efe59617b7df945eadd7e59bee954Andy Huang * Populate the adapter with overlay views (message headers, super-collapsed blocks, a 3917bdc3750454efe59617b7df945eadd7e59bee954Andy Huang * conversation header), and return an HTML document with spacer divs inserted for all overlays. 3927bdc3750454efe59617b7df945eadd7e59bee954Andy Huang * 3937bdc3750454efe59617b7df945eadd7e59bee954Andy Huang */ 3943bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp private String renderMessageBodies(MessageCursor messageCursor, 3953bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp boolean enableContentReadySignal) { 396f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang int pos = -1; 397632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 3981ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang LogUtils.d(LOG_TAG, "IN renderMessageBodies, fragment=%s", this); 3997bdc3750454efe59617b7df945eadd7e59bee954Andy Huang boolean allowNetworkImages = false; 4007bdc3750454efe59617b7df945eadd7e59bee954Andy Huang 401c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang // TODO: re-use any existing adapter item state (expanded, details expanded, show pics) 40228b7aee7fa1f7d096d33fc823a88a64f7a3fa79dAndy Huang 4037bdc3750454efe59617b7df945eadd7e59bee954Andy Huang // Walk through the cursor and build up an overlay adapter as you go. 4047bdc3750454efe59617b7df945eadd7e59bee954Andy Huang // Each overlay has an entry in the adapter for easy scroll handling in the container. 4057bdc3750454efe59617b7df945eadd7e59bee954Andy Huang // Items are not necessarily 1:1 in cursor and adapter because of super-collapsed blocks. 4067bdc3750454efe59617b7df945eadd7e59bee954Andy Huang // When adding adapter items, also add their heights to help the container later determine 4077bdc3750454efe59617b7df945eadd7e59bee954Andy Huang // overlay dimensions. 4087bdc3750454efe59617b7df945eadd7e59bee954Andy Huang 409db620fe42dcf1909468822b61238a23103d15376Andy Huang // When re-rendering, prevent ConversationContainer from laying out overlays until after 410db620fe42dcf1909468822b61238a23103d15376Andy Huang // the new spacers are positioned by WebView. 411db620fe42dcf1909468822b61238a23103d15376Andy Huang mConversationContainer.invalidateSpacerGeometry(); 412db620fe42dcf1909468822b61238a23103d15376Andy Huang 4137bdc3750454efe59617b7df945eadd7e59bee954Andy Huang mAdapter.clear(); 4147bdc3750454efe59617b7df945eadd7e59bee954Andy Huang 41547aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang // re-evaluate the message parts of the view state, since the messages may have changed 41647aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang // since the previous render 41747aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang final ConversationViewState prevState = mViewState; 41847aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang mViewState = new ConversationViewState(prevState); 41947aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang 4205ff63747a1b5c6e2197528972cbc3ba808b09d8dAndy Huang // N.B. the units of height for spacers are actually dp and not px because WebView assumes 4212e9acfe527426c00b5df2ca66189262dc40c8575Andy Huang // a pixel is an mdpi pixel, unless you set device-dpi. 4225ff63747a1b5c6e2197528972cbc3ba808b09d8dAndy Huang 4237bdc3750454efe59617b7df945eadd7e59bee954Andy Huang // add a single conversation header item 4247bdc3750454efe59617b7df945eadd7e59bee954Andy Huang final int convHeaderPos = mAdapter.addConversationHeader(mConversation); 42523014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang final int convHeaderPx = measureOverlayHeight(convHeaderPos); 4263233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang 427256b35c0a8287f48c28e0d1ba3fae65790063295Andy Huang final int sideMarginPx = getResources().getDimensionPixelOffset( 428256b35c0a8287f48c28e0d1ba3fae65790063295Andy Huang R.dimen.conversation_view_margin_side) + getResources().getDimensionPixelOffset( 429256b35c0a8287f48c28e0d1ba3fae65790063295Andy Huang R.dimen.conversation_message_content_margin_side); 430256b35c0a8287f48c28e0d1ba3fae65790063295Andy Huang 431256b35c0a8287f48c28e0d1ba3fae65790063295Andy Huang mTemplates.startConversation(mWebView.screenPxToWebPx(sideMarginPx), 432256b35c0a8287f48c28e0d1ba3fae65790063295Andy Huang mWebView.screenPxToWebPx(convHeaderPx)); 4333233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang 43446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang int collapsedStart = -1; 435839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang ConversationMessage prevCollapsedMsg = null; 43646dfba6160b55a582b344328067e3dafeb881dd9Andy Huang boolean prevSafeForImages = false; 43746dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 438f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang while (messageCursor.moveToPosition(++pos)) { 439839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang final ConversationMessage msg = messageCursor.getMessage(); 44046dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 4413233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang // TODO: save/restore 'show pics' state 4423233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang final boolean safeForImages = msg.alwaysShowImages /* || savedStateSaysSafe */; 4433233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang allowNetworkImages |= safeForImages; 4442405528bb15245f594277fe7b6efacd6a18ce69eAndy Huang 44508098ec4c894d9a15dfe800ad2397494e7e0a79aPaul Westbrook final Integer savedExpanded = prevState.getExpansionState(msg); 44608098ec4c894d9a15dfe800ad2397494e7e0a79aPaul Westbrook final int expandedState; 447839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang if (savedExpanded != null) { 4481ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang if (ExpansionState.isSuperCollapsed(savedExpanded) && messageCursor.isLast()) { 4491ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang // override saved state when this is now the new last message 4501ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang // this happens to the second-to-last message when you discard a draft 4511ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang expandedState = ExpansionState.EXPANDED; 4521ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang } else { 4531ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang expandedState = savedExpanded; 4541ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang } 455839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang } else { 456cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang // new messages that are not expanded default to being eligible for super-collapse 45708098ec4c894d9a15dfe800ad2397494e7e0a79aPaul Westbrook expandedState = (!msg.read || msg.starred || messageCursor.isLast()) ? 458cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang ExpansionState.EXPANDED : ExpansionState.SUPER_COLLAPSED; 459839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang } 46008098ec4c894d9a15dfe800ad2397494e7e0a79aPaul Westbrook mViewState.setExpansionState(msg, expandedState); 461839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang 462839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang // save off "read" state from the cursor 463839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang // later, the view may not match the cursor (e.g. conversation marked read on open) 464423bea25992492efea7d414819729f9eae7ce72eAndy Huang // however, if a previous state indicated this message was unread, trust that instead 465423bea25992492efea7d414819729f9eae7ce72eAndy Huang // so "mark unread" marks all originally unread messages 466423bea25992492efea7d414819729f9eae7ce72eAndy Huang mViewState.setReadState(msg, msg.read && !prevState.isUnread(msg)); 467c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang 468cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang // We only want to consider this for inclusion in the super collapsed block if 469cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang // 1) The we don't have previous state about this message (The first time that the 470cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang // user opens a conversation) 471cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang // 2) The previously saved state for this message indicates that this message is 472cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang // in the super collapsed block. 473cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang if (ExpansionState.isSuperCollapsed(expandedState)) { 474cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang // contribute to a super-collapsed block that will be emitted just before the 475cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang // next expanded header 476cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang if (collapsedStart < 0) { 477cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang collapsedStart = pos; 47846dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 479cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang prevCollapsedMsg = msg; 480cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang prevSafeForImages = safeForImages; 481cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang continue; 48246dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 4837bdc3750454efe59617b7df945eadd7e59bee954Andy Huang 48446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang // resolve any deferred decisions on previous collapsed items 48546dfba6160b55a582b344328067e3dafeb881dd9Andy Huang if (collapsedStart >= 0) { 48646dfba6160b55a582b344328067e3dafeb881dd9Andy Huang if (pos - collapsedStart == 1) { 48746dfba6160b55a582b344328067e3dafeb881dd9Andy Huang // special-case for a single collapsed message: no need to super-collapse it 48846dfba6160b55a582b344328067e3dafeb881dd9Andy Huang renderMessage(prevCollapsedMsg, false /* expanded */, 48946dfba6160b55a582b344328067e3dafeb881dd9Andy Huang prevSafeForImages); 49046dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } else { 49146dfba6160b55a582b344328067e3dafeb881dd9Andy Huang renderSuperCollapsedBlock(collapsedStart, pos - 1); 49246dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 49346dfba6160b55a582b344328067e3dafeb881dd9Andy Huang prevCollapsedMsg = null; 49446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang collapsedStart = -1; 49546dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 4962405528bb15245f594277fe7b6efacd6a18ce69eAndy Huang 49708098ec4c894d9a15dfe800ad2397494e7e0a79aPaul Westbrook renderMessage(msg, ExpansionState.isExpanded(expandedState), safeForImages); 498f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang } 4993233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang 5003233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang mWebView.getSettings().setBlockNetworkImage(!allowNetworkImages); 5013233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang 502cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook // If the conversation has specified a base uri, use it here, use mBaseUri 503cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook final String conversationBaseUri = mConversation.conversationBaseUri != null ? 504cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook mConversation.conversationBaseUri.toString() : mBaseUri; 505cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook return mTemplates.endConversation(mBaseUri, conversationBaseUri, 320, 5063bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp mWebView.getViewportWidth(), enableContentReadySignal); 507f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang } 508f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 50946dfba6160b55a582b344328067e3dafeb881dd9Andy Huang private void renderSuperCollapsedBlock(int start, int end) { 51046dfba6160b55a582b344328067e3dafeb881dd9Andy Huang final int blockPos = mAdapter.addSuperCollapsedBlock(start, end); 51123014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang final int blockPx = measureOverlayHeight(blockPos); 51223014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang mTemplates.appendSuperCollapsedHtml(start, mWebView.screenPxToWebPx(blockPx)); 51346dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 51446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 515839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang private void renderMessage(ConversationMessage msg, boolean expanded, 516839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang boolean safeForImages) { 51746dfba6160b55a582b344328067e3dafeb881dd9Andy Huang final int headerPos = mAdapter.addMessageHeader(msg, expanded); 51846dfba6160b55a582b344328067e3dafeb881dd9Andy Huang final MessageHeaderItem headerItem = (MessageHeaderItem) mAdapter.getItem(headerPos); 51946dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 52046dfba6160b55a582b344328067e3dafeb881dd9Andy Huang final int footerPos = mAdapter.addMessageFooter(headerItem); 52146dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 52246dfba6160b55a582b344328067e3dafeb881dd9Andy Huang // Measure item header and footer heights to allocate spacers in HTML 52346dfba6160b55a582b344328067e3dafeb881dd9Andy Huang // But since the views themselves don't exist yet, render each item temporarily into 52446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang // a host view for measurement. 52523014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang final int headerPx = measureOverlayHeight(headerPos); 52623014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang final int footerPx = measureOverlayHeight(footerPos); 52746dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 528256b35c0a8287f48c28e0d1ba3fae65790063295Andy Huang mTemplates.appendMessageHtml(msg, expanded, safeForImages, 52923014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang mWebView.screenPxToWebPx(headerPx), mWebView.screenPxToWebPx(footerPx)); 53046dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 53146dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 53246dfba6160b55a582b344328067e3dafeb881dd9Andy Huang private String renderCollapsedHeaders(MessageCursor cursor, 53346dfba6160b55a582b344328067e3dafeb881dd9Andy Huang SuperCollapsedBlockItem blockToReplace) { 53446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang final List<ConversationOverlayItem> replacements = Lists.newArrayList(); 53546dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 53646dfba6160b55a582b344328067e3dafeb881dd9Andy Huang mTemplates.reset(); 53746dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 5382b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei // In devices with non-integral density multiplier, screen pixels translate to non-integral 5392b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei // web pixels. Keep track of the error that occurs when we cast all heights to int 5402b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei float error = 0f; 54146dfba6160b55a582b344328067e3dafeb881dd9Andy Huang for (int i = blockToReplace.getStart(), end = blockToReplace.getEnd(); i <= end; i++) { 54246dfba6160b55a582b344328067e3dafeb881dd9Andy Huang cursor.moveToPosition(i); 543839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang final ConversationMessage msg = cursor.getMessage(); 54446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang final MessageHeaderItem header = mAdapter.newMessageHeaderItem(msg, 54546dfba6160b55a582b344328067e3dafeb881dd9Andy Huang false /* expanded */); 54646dfba6160b55a582b344328067e3dafeb881dd9Andy Huang final MessageFooterItem footer = mAdapter.newMessageFooterItem(header); 54746dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 54823014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang final int headerPx = measureOverlayHeight(header); 54923014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang final int footerPx = measureOverlayHeight(footer); 5502b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei error += mWebView.screenPxToWebPxError(headerPx) 5512b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei + mWebView.screenPxToWebPxError(footerPx); 5522b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei 5532b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei // When the error becomes greater than 1 pixel, make the next header 1 pixel taller 5542b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei int correction = 0; 5552b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei if (error >= 1) { 5562b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei correction = 1; 5572b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei error -= 1; 5582b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei } 55946dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 560256b35c0a8287f48c28e0d1ba3fae65790063295Andy Huang mTemplates.appendMessageHtml(msg, false /* expanded */, msg.alwaysShowImages, 5612b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei mWebView.screenPxToWebPx(headerPx) + correction, 5622b24e995cfcdb6ab0579b2fbcccb399a53632395Mark Wei mWebView.screenPxToWebPx(footerPx)); 56346dfba6160b55a582b344328067e3dafeb881dd9Andy Huang replacements.add(header); 56446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang replacements.add(footer); 565839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang 56608098ec4c894d9a15dfe800ad2397494e7e0a79aPaul Westbrook mViewState.setExpansionState(msg, ExpansionState.COLLAPSED); 56746dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 56846dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 56946dfba6160b55a582b344328067e3dafeb881dd9Andy Huang mAdapter.replaceSuperCollapsedBlock(blockToReplace, replacements); 57046dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 57146dfba6160b55a582b344328067e3dafeb881dd9Andy Huang return mTemplates.emit(); 57246dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 57346dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 57446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang private int measureOverlayHeight(int position) { 57546dfba6160b55a582b344328067e3dafeb881dd9Andy Huang return measureOverlayHeight(mAdapter.getItem(position)); 57646dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 57746dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 5787bdc3750454efe59617b7df945eadd7e59bee954Andy Huang /** 579b8331b4565566ca733997398e8c07a26cd2bee98Andy Huang * Measure the height of an adapter view by rendering an adapter item into a temporary 58046dfba6160b55a582b344328067e3dafeb881dd9Andy Huang * host view, and asking the view to immediately measure itself. This method will reuse 5817bdc3750454efe59617b7df945eadd7e59bee954Andy Huang * a previous adapter view from {@link ConversationContainer}'s scrap views if one was generated 5827bdc3750454efe59617b7df945eadd7e59bee954Andy Huang * earlier. 5837bdc3750454efe59617b7df945eadd7e59bee954Andy Huang * <p> 58446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang * After measuring the height, this method also saves the height in the 58546dfba6160b55a582b344328067e3dafeb881dd9Andy Huang * {@link ConversationOverlayItem} for later use in overlay positioning. 5867bdc3750454efe59617b7df945eadd7e59bee954Andy Huang * 58746dfba6160b55a582b344328067e3dafeb881dd9Andy Huang * @param convItem adapter item with data to render and measure 58823014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang * @return height of the rendered view in screen px 5897bdc3750454efe59617b7df945eadd7e59bee954Andy Huang */ 59046dfba6160b55a582b344328067e3dafeb881dd9Andy Huang private int measureOverlayHeight(ConversationOverlayItem convItem) { 5917bdc3750454efe59617b7df945eadd7e59bee954Andy Huang final int type = convItem.getType(); 5927bdc3750454efe59617b7df945eadd7e59bee954Andy Huang 5937bdc3750454efe59617b7df945eadd7e59bee954Andy Huang final View convertView = mConversationContainer.getScrapView(type); 594b8331b4565566ca733997398e8c07a26cd2bee98Andy Huang final View hostView = mAdapter.getView(convItem, convertView, mConversationContainer, 595b8331b4565566ca733997398e8c07a26cd2bee98Andy Huang true /* measureOnly */); 5967bdc3750454efe59617b7df945eadd7e59bee954Andy Huang if (convertView == null) { 5977bdc3750454efe59617b7df945eadd7e59bee954Andy Huang mConversationContainer.addScrapView(type, hostView); 5987bdc3750454efe59617b7df945eadd7e59bee954Andy Huang } 5997bdc3750454efe59617b7df945eadd7e59bee954Andy Huang 6009875bb4fc00347fc76f432a6e9ec5e6987679ca9Andy Huang final int heightPx = mConversationContainer.measureOverlay(hostView); 6017bdc3750454efe59617b7df945eadd7e59bee954Andy Huang convItem.setHeight(heightPx); 6029875bb4fc00347fc76f432a6e9ec5e6987679ca9Andy Huang convItem.markMeasurementValid(); 6037bdc3750454efe59617b7df945eadd7e59bee954Andy Huang 60423014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang return heightPx; 6057bdc3750454efe59617b7df945eadd7e59bee954Andy Huang } 6067bdc3750454efe59617b7df945eadd7e59bee954Andy Huang 6075ff63747a1b5c6e2197528972cbc3ba808b09d8dAndy Huang @Override 6085ff63747a1b5c6e2197528972cbc3ba808b09d8dAndy Huang public void onConversationViewHeaderHeightChange(int newHeight) { 6095ff63747a1b5c6e2197528972cbc3ba808b09d8dAndy Huang // TODO: propagate the new height to the header's HTML spacer. This can happen when labels 6105ff63747a1b5c6e2197528972cbc3ba808b09d8dAndy Huang // are added/removed 6115ff63747a1b5c6e2197528972cbc3ba808b09d8dAndy Huang } 6125ff63747a1b5c6e2197528972cbc3ba808b09d8dAndy Huang 6133233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang // END conversation header callbacks 6143233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang 6153233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang // START message header callbacks 6163233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang @Override 617c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang public void setMessageSpacerHeight(MessageHeaderItem item, int newSpacerHeightPx) { 618c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang mConversationContainer.invalidateSpacerGeometry(); 619c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang 620c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang // update message HTML spacer height 62123014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang final int h = mWebView.screenPxToWebPx(newSpacerHeightPx); 62223014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang LogUtils.i(LAYOUT_TAG, "setting HTML spacer h=%dwebPx (%dscreenPx)", h, 62323014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang newSpacerHeightPx); 6245349ce14695dc98615108eb26f96806921b0abbaVikram Aggarwal mWebView.loadUrl(String.format("javascript:setMessageHeaderSpacerHeight('%s', %s);", 62523014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang mTemplates.getMessageDomId(item.message), h)); 6263233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang } 6273233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang 6283233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang @Override 629c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang public void setMessageExpanded(MessageHeaderItem item, int newSpacerHeightPx) { 630c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang mConversationContainer.invalidateSpacerGeometry(); 631c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang 632c7543579c6a97c0ae3341578332f56d4d226f34cAndy Huang // show/hide the HTML message body and update the spacer height 63323014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang final int h = mWebView.screenPxToWebPx(newSpacerHeightPx); 63423014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang LogUtils.i(LAYOUT_TAG, "setting HTML spacer expanded=%s h=%dwebPx (%dscreenPx)", 63523014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang item.isExpanded(), h, newSpacerHeightPx); 6365349ce14695dc98615108eb26f96806921b0abbaVikram Aggarwal mWebView.loadUrl(String.format("javascript:setMessageBodyVisible('%s', %s, %s);", 63723014705ca9872cd5004a1aa76e83ae260165ecaAndy Huang mTemplates.getMessageDomId(item.message), item.isExpanded(), h)); 638839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang 63908098ec4c894d9a15dfe800ad2397494e7e0a79aPaul Westbrook mViewState.setExpansionState(item.message, 64008098ec4c894d9a15dfe800ad2397494e7e0a79aPaul Westbrook item.isExpanded() ? ExpansionState.EXPANDED : ExpansionState.COLLAPSED); 6413233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang } 6423233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang 6433233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang @Override 6443233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang public void showExternalResources(Message msg) { 6453233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang mWebView.getSettings().setBlockNetworkImage(false); 6463233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang mWebView.loadUrl("javascript:unblockImages('" + mTemplates.getMessageDomId(msg) + "');"); 6473233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang } 6483233bff8ae08a56543c9f5abf1bc6ab38f0574ceAndy Huang // END message header callbacks 6495ff63747a1b5c6e2197528972cbc3ba808b09d8dAndy Huang 65046dfba6160b55a582b344328067e3dafeb881dd9Andy Huang @Override 65146dfba6160b55a582b344328067e3dafeb881dd9Andy Huang public void onSuperCollapsedClick(SuperCollapsedBlockItem item) { 652f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp MessageCursor cursor = getMessageCursor(); 653f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (cursor == null || !mViewsCreated) { 65446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang return; 65546dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 65646dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 657f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mTempBodiesHtml = renderCollapsedHeaders(cursor, item); 65846dfba6160b55a582b344328067e3dafeb881dd9Andy Huang mWebView.loadUrl("javascript:replaceSuperCollapsedBlock(" + item.getStart() + ")"); 65946dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 66046dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 66147aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang private void showNewMessageNotification(NewMessagesInfo info) { 66247aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang final TextView descriptionView = (TextView) mNewMessageBar.findViewById( 66347aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang R.id.new_message_description); 66447aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang descriptionView.setText(info.getNotificationText()); 66547aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang mNewMessageBar.setVisibility(View.VISIBLE); 66647aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang } 66747aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang 66847aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang private void onNewMessageBarClick() { 66947aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang mNewMessageBar.setVisibility(View.GONE); 67047aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang 671f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp renderConversation(getMessageCursor()); // mCursor is already up-to-date 672f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // per onLoadFinished() 6735fbda023f1b0570e192e03a33834244a05edf200Andy Huang } 6745fbda023f1b0570e192e03a33834244a05edf200Andy Huang 675b5078b287b1cec38817e342ff054ea901d199329Andy Huang private static int[] parseInts(final String[] stringArray) { 676b5078b287b1cec38817e342ff054ea901d199329Andy Huang final int len = stringArray.length; 677b5078b287b1cec38817e342ff054ea901d199329Andy Huang final int[] ints = new int[len]; 678b5078b287b1cec38817e342ff054ea901d199329Andy Huang for (int i = 0; i < len; i++) { 679b5078b287b1cec38817e342ff054ea901d199329Andy Huang ints[i] = Integer.parseInt(stringArray[i]); 680b5078b287b1cec38817e342ff054ea901d199329Andy Huang } 681b5078b287b1cec38817e342ff054ea901d199329Andy Huang return ints; 682b5078b287b1cec38817e342ff054ea901d199329Andy Huang } 683b5078b287b1cec38817e342ff054ea901d199329Andy Huang 68447aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang @Override 68547aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang public String toString() { 68647aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang // log extra info at DEBUG level or finer 68747aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang final String s = super.toString(); 68847aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang if (!LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG) || mConversation == null) { 68947aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang return s; 69047aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang } 69147aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang return "(" + s + " subj=" + mConversation.subject + ")"; 69247aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang } 69347aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang 6941617481de7c1f9b8dfcd25ba9828e933cf9d5490Andy Huang private Address getAddress(String rawFrom) { 6951617481de7c1f9b8dfcd25ba9828e933cf9d5490Andy Huang Address addr = mAddressCache.get(rawFrom); 6961617481de7c1f9b8dfcd25ba9828e933cf9d5490Andy Huang if (addr == null) { 6971617481de7c1f9b8dfcd25ba9828e933cf9d5490Andy Huang addr = Address.getEmailAddress(rawFrom); 6981617481de7c1f9b8dfcd25ba9828e933cf9d5490Andy Huang mAddressCache.put(rawFrom, addr); 6991617481de7c1f9b8dfcd25ba9828e933cf9d5490Andy Huang } 7001617481de7c1f9b8dfcd25ba9828e933cf9d5490Andy Huang return addr; 7011617481de7c1f9b8dfcd25ba9828e933cf9d5490Andy Huang } 7021617481de7c1f9b8dfcd25ba9828e933cf9d5490Andy Huang 70328b7aee7fa1f7d096d33fc823a88a64f7a3fa79dAndy Huang @Override 70428b7aee7fa1f7d096d33fc823a88a64f7a3fa79dAndy Huang public Account getAccount() { 70528b7aee7fa1f7d096d33fc823a88a64f7a3fa79dAndy Huang return mAccount; 70628b7aee7fa1f7d096d33fc823a88a64f7a3fa79dAndy Huang } 70728b7aee7fa1f7d096d33fc823a88a64f7a3fa79dAndy Huang 708542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook private class ConversationWebViewClient extends AbstractConversationWebViewClient { 70917a9cde3b0e28fc98fdeda19de81e18056eb09dbAndy Huang @Override 71017a9cde3b0e28fc98fdeda19de81e18056eb09dbAndy Huang public void onPageFinished(WebView view, String url) { 711de56e970299fb413e4136dc12a7b08c5cc89e1c1Andy Huang // Ignore unsafe calls made after a fragment is detached from an activity 712de56e970299fb413e4136dc12a7b08c5cc89e1c1Andy Huang final ControllableActivity activity = (ControllableActivity) getActivity(); 713de56e970299fb413e4136dc12a7b08c5cc89e1c1Andy Huang if (activity == null || !mViewsCreated) { 714de56e970299fb413e4136dc12a7b08c5cc89e1c1Andy Huang LogUtils.i(LOG_TAG, "ignoring CVF.onPageFinished, url=%s fragment=%s", url, 715b95da853ffbff343c8ca11470de2f0047e0807a6Andy Huang ConversationViewFragment.this); 716b95da853ffbff343c8ca11470de2f0047e0807a6Andy Huang return; 717b95da853ffbff343c8ca11470de2f0047e0807a6Andy Huang } 718b95da853ffbff343c8ca11470de2f0047e0807a6Andy Huang 71947aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang LogUtils.i(LOG_TAG, "IN CVF.onPageFinished, url=%s fragment=%s act=%s", url, 72047aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang ConversationViewFragment.this, getActivity()); 721632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang 72217a9cde3b0e28fc98fdeda19de81e18056eb09dbAndy Huang super.onPageFinished(view, url); 72317a9cde3b0e28fc98fdeda19de81e18056eb09dbAndy Huang 72417a9cde3b0e28fc98fdeda19de81e18056eb09dbAndy Huang // TODO: save off individual message unread state (here, or in onLoadFinished?) so 72517a9cde3b0e28fc98fdeda19de81e18056eb09dbAndy Huang // 'mark unread' restores the original unread state for each individual message 72617a9cde3b0e28fc98fdeda19de81e18056eb09dbAndy Huang 727632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang if (mUserVisible) { 728632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang onConversationSeen(); 72951067133f35911bf4b43f97be52b00e5bd9f07abAndy Huang } 7303bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp if (!mEnableContentReadySignal) { 7313bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp notifyConversationLoaded(mConversation); 7323bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp dismissLoadingStatus(); 7333bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp } 734089f2628927ed7630187f29165e6e6d9f6615891Paul Westbrook // We are not able to use the loader manager unless this fragment is added to the 735089f2628927ed7630187f29165e6e6d9f6615891Paul Westbrook // activity 736089f2628927ed7630187f29165e6e6d9f6615891Paul Westbrook if (isAdded()) { 737089f2628927ed7630187f29165e6e6d9f6615891Paul Westbrook final Set<String> emailAddresses = Sets.newHashSet(); 738089f2628927ed7630187f29165e6e6d9f6615891Paul Westbrook for (Address addr : mAddressCache.values()) { 739089f2628927ed7630187f29165e6e6d9f6615891Paul Westbrook emailAddresses.add(addr.getAddress()); 740089f2628927ed7630187f29165e6e6d9f6615891Paul Westbrook } 741f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp ContactLoaderCallbacks callbacks = getContactInfoSource(); 742f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp getContactInfoSource().setSenders(emailAddresses); 743f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp getLoaderManager().restartLoader(CONTACT_LOADER, Bundle.EMPTY, callbacks); 744b8331b4565566ca733997398e8c07a26cd2bee98Andy Huang } 74517a9cde3b0e28fc98fdeda19de81e18056eb09dbAndy Huang } 74617a9cde3b0e28fc98fdeda19de81e18056eb09dbAndy Huang 747af5d4e0e2a5ce3300fffa0c484431d83adb89ee8Andy Huang @Override 748af5d4e0e2a5ce3300fffa0c484431d83adb89ee8Andy Huang public boolean shouldOverrideUrlLoading(WebView view, String url) { 749542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook return mViewsCreated && super.shouldOverrideUrlLoading(view, url); 750af5d4e0e2a5ce3300fffa0c484431d83adb89ee8Andy Huang } 75117a9cde3b0e28fc98fdeda19de81e18056eb09dbAndy Huang } 75217a9cde3b0e28fc98fdeda19de81e18056eb09dbAndy Huang 753f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang /** 7543bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp * Notifies the {@link ConversationViewable.ConversationCallbacks} that the conversation has 7553bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp * been loaded. 7563bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp */ 7573bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp public void notifyConversationLoaded(Conversation c) { 758dde3f9ff76c1a7f9a6e9fa959a6422a8a8d82e81mindyp if (mWebViewSizeChangeListener == null) { 759dde3f9ff76c1a7f9a6e9fa959a6422a8a8d82e81mindyp mWebViewSizeChangeListener = new ConversationWebView.ContentSizeChangeListener() { 760dde3f9ff76c1a7f9a6e9fa959a6422a8a8d82e81mindyp @Override 761dde3f9ff76c1a7f9a6e9fa959a6422a8a8d82e81mindyp public void onHeightChange(int h) { 762dde3f9ff76c1a7f9a6e9fa959a6422a8a8d82e81mindyp // When WebKit says the DOM height has changed, re-measure 763dde3f9ff76c1a7f9a6e9fa959a6422a8a8d82e81mindyp // bodies and re-position their headers. 764dde3f9ff76c1a7f9a6e9fa959a6422a8a8d82e81mindyp // This is separate from the typical JavaScript DOM change 765dde3f9ff76c1a7f9a6e9fa959a6422a8a8d82e81mindyp // listeners because cases like NARROW_COLUMNS text reflow do not trigger DOM 766dde3f9ff76c1a7f9a6e9fa959a6422a8a8d82e81mindyp // events. 767dde3f9ff76c1a7f9a6e9fa959a6422a8a8d82e81mindyp mWebView.loadUrl("javascript:measurePositions();"); 768dde3f9ff76c1a7f9a6e9fa959a6422a8a8d82e81mindyp } 769dde3f9ff76c1a7f9a6e9fa959a6422a8a8d82e81mindyp }; 770dde3f9ff76c1a7f9a6e9fa959a6422a8a8d82e81mindyp } 771dde3f9ff76c1a7f9a6e9fa959a6422a8a8d82e81mindyp mWebView.setContentSizeChangeListener(mWebViewSizeChangeListener); 7723bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp } 7733bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp 7743bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp /** 7753bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp * Notifies the {@link ConversationViewable.ConversationCallbacks} that the conversation has 7763bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp * failed to load. 7773bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp */ 7783bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp protected void notifyConversationLoadError(Conversation c) { 7793bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp mActivity.onConversationLoadError(); 7803bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp } 7813bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp 7823bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp /** 783f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * NOTE: all public methods must be listed in the proguard flags so that they can be accessed 784f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * via reflection and not stripped. 785f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang * 786f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang */ 787f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang private class MailJsBridge { 788f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 789f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang @SuppressWarnings("unused") 790974c966c14000d42f089e2021b098d5cf61f9245Mindy Pereira @JavascriptInterface 7917bdc3750454efe59617b7df945eadd7e59bee954Andy Huang public void onWebContentGeometryChange(final String[] overlayBottomStrs) { 79246dfba6160b55a582b344328067e3dafeb881dd9Andy Huang try { 793ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp getHandler().post(new Runnable() { 79446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang @Override 79546dfba6160b55a582b344328067e3dafeb881dd9Andy Huang public void run() { 79646dfba6160b55a582b344328067e3dafeb881dd9Andy Huang if (!mViewsCreated) { 79746dfba6160b55a582b344328067e3dafeb881dd9Andy Huang LogUtils.d(LOG_TAG, "ignoring webContentGeometryChange because views" + 79846dfba6160b55a582b344328067e3dafeb881dd9Andy Huang " are gone, %s", ConversationViewFragment.this); 79946dfba6160b55a582b344328067e3dafeb881dd9Andy Huang return; 80046dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 80146dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 80246dfba6160b55a582b344328067e3dafeb881dd9Andy Huang mConversationContainer.onGeometryChange(parseInts(overlayBottomStrs)); 80351067133f35911bf4b43f97be52b00e5bd9f07abAndy Huang } 80446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang }); 80546dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } catch (Throwable t) { 80646dfba6160b55a582b344328067e3dafeb881dd9Andy Huang LogUtils.e(LOG_TAG, t, "Error in MailJsBridge.onWebContentGeometryChange"); 80746dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 80846dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 80951067133f35911bf4b43f97be52b00e5bd9f07abAndy Huang 81046dfba6160b55a582b344328067e3dafeb881dd9Andy Huang @SuppressWarnings("unused") 811974c966c14000d42f089e2021b098d5cf61f9245Mindy Pereira @JavascriptInterface 81246dfba6160b55a582b344328067e3dafeb881dd9Andy Huang public String getTempMessageBodies() { 81346dfba6160b55a582b344328067e3dafeb881dd9Andy Huang try { 81446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang if (!mViewsCreated) { 81546dfba6160b55a582b344328067e3dafeb881dd9Andy Huang return ""; 816f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang } 81746dfba6160b55a582b344328067e3dafeb881dd9Andy Huang 81846dfba6160b55a582b344328067e3dafeb881dd9Andy Huang final String s = mTempBodiesHtml; 81946dfba6160b55a582b344328067e3dafeb881dd9Andy Huang mTempBodiesHtml = null; 82046dfba6160b55a582b344328067e3dafeb881dd9Andy Huang return s; 82146dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } catch (Throwable t) { 82246dfba6160b55a582b344328067e3dafeb881dd9Andy Huang LogUtils.e(LOG_TAG, t, "Error in MailJsBridge.getTempMessageBodies"); 82346dfba6160b55a582b344328067e3dafeb881dd9Andy Huang return ""; 82446dfba6160b55a582b344328067e3dafeb881dd9Andy Huang } 825f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang } 826f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 8273bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp private void showConversation(Conversation conv) { 8283bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp notifyConversationLoaded(conv); 8293bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp dismissLoadingStatus(); 8303bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp } 8313bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp 8323bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp @SuppressWarnings("unused") 833974c966c14000d42f089e2021b098d5cf61f9245Mindy Pereira @JavascriptInterface 8343bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp public void onContentReady() { 8353bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp final Conversation conv = mConversation; 8363bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp try { 837ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp getHandler().post(new Runnable() { 8383bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp @Override 8393bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp public void run() { 8403bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp LogUtils.d(LOG_TAG, "ANIMATION STARTED, ready to draw. t=%s", 8413bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp SystemClock.uptimeMillis()); 8423bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp showConversation(conv); 8433bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp } 8443bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp }); 8453bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp } catch (Throwable t) { 8463bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp LogUtils.e(LOG_TAG, t, "Error in MailJsBridge.onContentReady"); 8473bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp // Still try to show the conversation. 8483bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp showConversation(conv); 8493bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp } 8503bcf180f8104bc27319086a9a6ece5a3c2917c37mindyp } 851674afa42682908640167fc6109b76f6f843e6fbeMindy Pereira } 852f70fc4052b72a850bbb9be585d0f5a4877ee9448Andy Huang 85347aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang private class NewMessagesInfo { 85447aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang int count; 85547aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang String senderAddress; 85647aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang 85747aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang /** 85847aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang * Return the display text for the new message notification overlay. It will be formatted 85947aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang * appropriately for a single new message vs. multiple new messages. 86047aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang * 86147aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang * @return display text 86247aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang */ 86347aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang public String getNotificationText() { 86447aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang final Object param; 86547aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang if (count > 1) { 86647aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang param = count; 86747aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang } else { 8681617481de7c1f9b8dfcd25ba9828e933cf9d5490Andy Huang final Address addr = getAddress(senderAddress); 86947aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang param = TextUtils.isEmpty(addr.getName()) ? addr.getAddress() : addr.getName(); 87047aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang } 87147aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang return getResources().getQuantityString(R.plurals.new_incoming_messages, count, param); 87247aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang } 87347aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang } 87447aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang 875f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 876f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void onMessageCursorLoadFinished(Loader<Cursor> loader, Cursor data, boolean wasNull, 877f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp boolean changed) { 878f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp MessageCursor messageCursor = (MessageCursor) data; 879f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp /* 880f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * what kind of changes affect the MessageCursor? 1. new message(s) 2. 881f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * read/unread state change 3. deleted message, either regular or draft 882f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * 4. updated message, either from self or from others, updated in 883f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * content or state or sender 5. star/unstar of message (technically 884f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * similar to #1) 6. other label change Use MessageCursor.hashCode() to 885f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * sort out interesting vs. no-op cursor updates. 886f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp */ 887f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (!wasNull) { 888f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp final NewMessagesInfo info = getNewIncomingMessagesInfo(messageCursor); 889f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 890f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (info.count > 0 || !changed) { 891f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 892f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (info.count > 0) { 893f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // don't immediately render new incoming messages from other 894f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // senders 895f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // (to avoid a new message from losing the user's focus) 896f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp LogUtils.i(LOG_TAG, "CONV RENDER: conversation updated" 897f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp + ", holding cursor for new incoming message"); 898f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp showNewMessageNotification(info); 8991ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang } else { 900f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp LogUtils.i(LOG_TAG, "CONV RENDER: uninteresting update" 901f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp + ", ignoring this conversation update"); 9021ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang } 9031ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang 904f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // update mCursor reference because the old one is about to be 905f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // closed by CursorLoader 9061ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang return; 9071ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang } 908b8331b4565566ca733997398e8c07a26cd2bee98Andy Huang } 909b8331b4565566ca733997398e8c07a26cd2bee98Andy Huang 910f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // cursors are different, and not due to an incoming message. fall 911f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // through and render. 912f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp LogUtils.i(LOG_TAG, "CONV RENDER: conversation updated" 913f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp + ", but not due to incoming message. rendering."); 91447aa9c991b33c722a6ba1946fc02e0aba17bc1c9Andy Huang 915f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // TODO: if this is not user-visible, delay render until user-visible 916f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // fragment is done. This is needed in addition to the 917f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // showConversation() delay to speed up rotation and restoration. 918f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp renderConversation(messageCursor); 919b8331b4565566ca733997398e8c07a26cd2bee98Andy Huang } 920b8331b4565566ca733997398e8c07a26cd2bee98Andy Huang 921f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private NewMessagesInfo getNewIncomingMessagesInfo(MessageCursor newCursor) { 922f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp final NewMessagesInfo info = new NewMessagesInfo(); 923b8331b4565566ca733997398e8c07a26cd2bee98Andy Huang 924f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp int pos = -1; 925f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp while (newCursor.moveToPosition(++pos)) { 926f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp final Message m = newCursor.getMessage(); 927f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (!mViewState.contains(m)) { 928f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp LogUtils.i(LOG_TAG, "conversation diff: found new msg: %s", m.uri); 929f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 930f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp final Address from = getAddress(m.from); 931f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // distinguish ours from theirs 932f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // new messages from the account owner should not trigger a 933f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // notification 934f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (mAccount.ownsFromAddress(from.getAddress())) { 935f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp LogUtils.i(LOG_TAG, "found message from self: %s", m.uri); 936f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp continue; 937f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 938b8331b4565566ca733997398e8c07a26cd2bee98Andy Huang 939f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp info.count++; 940f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp info.senderAddress = m.from; 941b8331b4565566ca733997398e8c07a26cd2bee98Andy Huang } 942b8331b4565566ca733997398e8c07a26cd2bee98Andy Huang } 943f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return info; 944b8331b4565566ca733997398e8c07a26cd2bee98Andy Huang } 945b8331b4565566ca733997398e8c07a26cd2bee98Andy Huang 946cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook private class SetCookieTask extends AsyncTask<Void, Void, Void> { 947cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook final String mUri; 948cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook final String mCookie; 949cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook 950cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook SetCookieTask(String uri, String cookie) { 951cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook mUri = uri; 952cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook mCookie = cookie; 953cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook } 954cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook 955cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook @Override 956cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook public Void doInBackground(Void... args) { 957cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook final CookieSyncManager csm = 958f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp CookieSyncManager.createInstance(getContext()); 959cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook CookieManager.getInstance().setCookie(mUri, mCookie); 960cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook csm.sync(); 961cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook return null; 962cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook } 963cebcc64fbd69618ff89f9fac0bfe9b9e7d7ce104Paul Westbrook } 96436280f3c2939bb2dacb4077bd528900346ff4bb9mindyp 96526d4d2d9c43c499f458808f050ec73ea3c28dec4mindyp @Override 96636280f3c2939bb2dacb4077bd528900346ff4bb9mindyp public void onConversationUpdated(Conversation conv) { 96736280f3c2939bb2dacb4077bd528900346ff4bb9mindyp final ConversationViewHeader headerView = (ConversationViewHeader) mConversationContainer 96836280f3c2939bb2dacb4077bd528900346ff4bb9mindyp .findViewById(R.id.conversation_header); 969b2b98ba97983f225eec19dc9bc5333e6ef80ee15mindyp mConversation = conv; 9709e0b236be55cf9dab8f1670a9d91ed16e055a9d0mindyp if (headerView != null) { 9719e0b236be55cf9dab8f1670a9d91ed16e055a9d0mindyp headerView.onConversationUpdated(conv); 9729e0b236be55cf9dab8f1670a9d91ed16e055a9d0mindyp } 97336280f3c2939bb2dacb4077bd528900346ff4bb9mindyp } 9749b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira} 975