AbstractConversationViewFragment.java revision 9a8bc1ead816f0398bdbeac1e744ed458028b5c4
1f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp/* 2f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * Copyright (C) 2012 Google Inc. 3f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * Licensed to The Android Open Source Project. 4f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * 5f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * Licensed under the Apache License, Version 2.0 (the "License"); 6f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * you may not use this file except in compliance with the License. 7f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * You may obtain a copy of the License at 8f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * 9f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * http://www.apache.org/licenses/LICENSE-2.0 10f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * 11f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * Unless required by applicable law or agreed to in writing, software 12f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * distributed under the License is distributed on an "AS IS" BASIS, 13f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * See the License for the specific language governing permissions and 15f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * limitations under the License. 16f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp */ 17f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 18f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyppackage com.android.mail.ui; 19f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 20ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindypimport android.animation.Animator; 219d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huangimport android.animation.AnimatorInflater; 227d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huangimport android.animation.AnimatorListenerAdapter; 23f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport android.app.Activity; 24f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport android.app.Fragment; 25f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport android.app.LoaderManager; 26542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrookimport android.content.ActivityNotFoundException; 27f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport android.content.Context; 28f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport android.content.CursorLoader; 29542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrookimport android.content.Intent; 30f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport android.content.Loader; 31542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrookimport android.content.pm.ActivityInfo; 32542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrookimport android.content.pm.PackageManager; 33542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrookimport android.content.pm.ResolveInfo; 34ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindypimport android.content.res.Resources; 35f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport android.database.Cursor; 36f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport android.database.DataSetObservable; 37f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport android.database.DataSetObserver; 38f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport android.net.Uri; 39f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport android.os.Bundle; 40ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindypimport android.os.Handler; 41542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrookimport android.provider.Browser; 42f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport android.view.Menu; 43f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport android.view.MenuInflater; 44f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport android.view.MenuItem; 45ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindypimport android.view.View; 46542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrookimport android.webkit.WebView; 47542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrookimport android.webkit.WebViewClient; 48f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 49f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport com.android.mail.ContactInfo; 50f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport com.android.mail.ContactInfoSource; 51f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport com.android.mail.FormattedDateBuilder; 52f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport com.android.mail.R; 53f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport com.android.mail.SenderInfoLoader; 54f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport com.android.mail.browse.ConversationViewAdapter.ConversationAccountController; 55f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport com.android.mail.browse.ConversationViewHeader.ConversationViewHeaderCallbacks; 569d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huangimport com.android.mail.browse.MessageCursor; 57f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport com.android.mail.browse.MessageCursor.ConversationController; 58f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport com.android.mail.browse.MessageHeaderView.MessageHeaderViewCallbacks; 59f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport com.android.mail.providers.Account; 60f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport com.android.mail.providers.AccountObserver; 61f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport com.android.mail.providers.Address; 62f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport com.android.mail.providers.Conversation; 63f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport com.android.mail.providers.Folder; 64f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport com.android.mail.providers.ListParams; 65f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport com.android.mail.providers.UIProvider; 66f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport com.android.mail.utils.LogTag; 67f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport com.android.mail.utils.LogUtils; 68f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport com.android.mail.utils.Utils; 69f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport com.google.common.collect.ImmutableMap; 70f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport com.google.common.collect.Maps; 71f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 724d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrookimport java.util.Arrays; 73542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrookimport java.util.List; 74f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport java.util.Map; 75f4fce1227d8b49f45e6569f1590565f2df9e8d6emindypimport java.util.Set; 76f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 77f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyppublic abstract class AbstractConversationViewFragment extends Fragment implements 78f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp ConversationController, ConversationAccountController, MessageHeaderViewCallbacks, 79f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp ConversationViewHeaderCallbacks { 80f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 81f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private static final String ARG_ACCOUNT = "account"; 82f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public static final String ARG_CONVERSATION = "conversation"; 83f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private static final String ARG_FOLDER = "folder"; 84f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private static final String LOG_TAG = LogTag.getLogTag(); 85f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp protected static final int MESSAGE_LOADER = 0; 86f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp protected static final int CONTACT_LOADER = 1; 8732d911f491afad153983f7519267ddb764927355mindyp private static int sMinDelay = -1; 8832d911f491afad153983f7519267ddb764927355mindyp private static int sMinShowTime = -1; 89f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp protected ControllableActivity mActivity; 90f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private final MessageLoaderCallbacks mMessageLoaderCallbacks = new MessageLoaderCallbacks(); 91f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp protected FormattedDateBuilder mDateBuilder; 92f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private final ContactLoaderCallbacks mContactLoaderCallbacks = new ContactLoaderCallbacks(); 93f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private MenuItem mChangeFoldersMenuItem; 94f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp protected Conversation mConversation; 95f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp protected Folder mFolder; 96f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp protected String mBaseUri; 97f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp protected Account mAccount; 98f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp protected final Map<String, Address> mAddressCache = Maps.newHashMap(); 99f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp protected boolean mEnableContentReadySignal; 100f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private MessageCursor mCursor; 101f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private Context mContext; 1029d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang /** 1039d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang * A backwards-compatible version of {{@link #getUserVisibleHint()}. Like the framework flag, 1049d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang * this flag is saved and restored. 1059d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang */ 1069d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang private boolean mUserVisible; 107ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp private View mProgressView; 108ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp private View mBackgroundView; 109ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp private final Handler mHandler = new Handler(); 1104d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook 1114d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook /** 1124d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook * Parcelable state of the conversation view. Can safely be used without null checking any time 1139d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang * after {@link #onCreate(Bundle)}. 1144d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook */ 1154d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook protected ConversationViewState mViewState; 1164d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook 1174d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook /** 1184d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook * Handles a deferred 'mark read' operation, necessary when the conversation view has finished 1194d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook * loading before the conversation cursor. Normally null unless this situation occurs. 1204d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook * When finally able to 'mark read', this observer will also be unregistered and cleaned up. 1214d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook */ 1224d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook private MarkReadObserver mMarkReadObserver; 1234d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook 12432d911f491afad153983f7519267ddb764927355mindyp private long mLoadingShownTime = -1; 12532d911f491afad153983f7519267ddb764927355mindyp 1269a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang private final Runnable mDelayedShow = new FragmentRunnable("mDelayedShow") { 127ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp @Override 1289a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang public void go() { 12932d911f491afad153983f7519267ddb764927355mindyp mLoadingShownTime = System.currentTimeMillis(); 1302f2ad3f5ecc0185dfa09091062322dbaeb14c7edAndy Huang mProgressView.setVisibility(View.VISIBLE); 131ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp } 132ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp }; 133ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp 134f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private final AccountObserver mAccountObserver = new AccountObserver() { 135f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 136f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void onChanged(Account newAccount) { 137adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang final Account oldAccount = mAccount; 138f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mAccount = newAccount; 139adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang onAccountChanged(newAccount, oldAccount); 140f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 141f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp }; 142f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 1439d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang private static final String BUNDLE_VIEW_STATE = 1449d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang AbstractConversationViewFragment.class.getName() + "viewstate"; 1459a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang /** 1469a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang * We save the user visible flag so the various transitions that occur during rotation do not 1479a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang * cause unnecessary visibility change. 1489a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang */ 1499d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang private static final String BUNDLE_USER_VISIBLE = 1509d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang AbstractConversationViewFragment.class.getName() + "uservisible"; 1519d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 152f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public static Bundle makeBasicArgs(Account account, Folder folder) { 153f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp Bundle args = new Bundle(); 154f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp args.putParcelable(ARG_ACCOUNT, account); 155f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp args.putParcelable(ARG_FOLDER, folder); 156f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return args; 157f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 158f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 159f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp /** 160f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * Constructor needs to be public to handle orientation changes and activity 161f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * lifecycle events. 162f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp */ 163f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public AbstractConversationViewFragment() { 164f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp super(); 165f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 166f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 167f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp /** 168f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * Subclasses must override, since this depends on how many messages are 169f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * shown in the conversation view. 170f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp */ 171f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp protected abstract void markUnread(); 172f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 173f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp /** 174f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * Subclasses must override this, since they may want to display a single or 175f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * many messages related to this conversation. 176f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp */ 177014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang protected abstract void onMessageCursorLoadFinished(Loader<Cursor> loader, 178014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang MessageCursor newCursor, MessageCursor oldCursor); 179f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 180f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp /** 181f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * Subclasses must override this, since they may want to display a single or 182f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * many messages related to this conversation. 183f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp */ 184f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 185f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public abstract void onConversationViewHeaderHeightChange(int newHeight); 186f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 187f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public abstract void onUserVisibleHintChanged(); 188f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 189f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp /** 190f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * Subclasses must override this. 191f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp */ 192adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang protected abstract void onAccountChanged(Account newAccount, Account oldAccount); 193f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 194f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 195f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void onCreate(Bundle savedState) { 196f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp super.onCreate(savedState); 197f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 198f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp final Bundle args = getArguments(); 199f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mAccount = args.getParcelable(ARG_ACCOUNT); 200f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mConversation = args.getParcelable(ARG_CONVERSATION); 201f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mFolder = args.getParcelable(ARG_FOLDER); 202ba4cce62e2de2b818190b81bb07ecc5e94544165Paul Westbrook 203ba4cce62e2de2b818190b81bb07ecc5e94544165Paul Westbrook // Since the uri specified in the conversation base uri may not be unique, we specify a 204ba4cce62e2de2b818190b81bb07ecc5e94544165Paul Westbrook // base uri that us guaranteed to be unique for this conversation. 205ba4cce62e2de2b818190b81bb07ecc5e94544165Paul Westbrook mBaseUri = "x-thread://" + mAccount.name + "/" + mConversation.id; 206ba4cce62e2de2b818190b81bb07ecc5e94544165Paul Westbrook 207f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // On JB or newer, we use the 'webkitAnimationStart' DOM event to signal load complete 208f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // Below JB, try to speed up initial render by having the webview do supplemental draws to 209f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // custom a software canvas. 210f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // TODO(mindyp): 211f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp //PAGE READINESS SIGNAL FOR JELLYBEAN AND NEWER 212f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // Notify the app on 'webkitAnimationStart' of a simple dummy element with a simple no-op 213f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // animation that immediately runs on page load. The app uses this as a signal that the 214f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // content is loaded and ready to draw, since WebView delays firing this event until the 215f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // layers are composited and everything is ready to draw. 216f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // This signal does not seem to be reliable, so just use the old method for now. 217f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mEnableContentReadySignal = false; //Utils.isRunningJellybeanOrLater(); 218f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp LogUtils.d(LOG_TAG, "onCreate in ConversationViewFragment (this=%s)", this); 219f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // Not really, we just want to get a crack to store a reference to the change_folder item 220f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp setHasOptionsMenu(true); 2219d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 2229d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang if (savedState != null) { 2239d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang mViewState = savedState.getParcelable(BUNDLE_VIEW_STATE); 2249d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang mUserVisible = savedState.getBoolean(BUNDLE_USER_VISIBLE); 2259d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } else { 2269d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang mViewState = getNewViewState(); 2279d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 228f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 229f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 230ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp public void instantiateProgressIndicators(View rootView) { 231ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp mBackgroundView = rootView.findViewById(R.id.background_view); 232ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp mProgressView = rootView.findViewById(R.id.loading_progress); 233ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp } 234ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp 235ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp protected void dismissLoadingStatus() { 2367d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang dismissLoadingStatus(null); 2377d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang } 2387d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang 2397d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang /** 2407d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang * Begin the fade-out animation to hide the Progress overlay, either immediately or after some 2417d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang * timeout (to ensure that the progress minimum time elapses). 2427d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang * 2437d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang * @param doAfter an optional Runnable action to execute after the animation completes 2447d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang */ 2457d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang protected void dismissLoadingStatus(final Runnable doAfter) { 24632d911f491afad153983f7519267ddb764927355mindyp if (mLoadingShownTime == -1) { 247ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp // The runnable hasn't run yet, so just remove it. 248ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp mHandler.removeCallbacks(mDelayedShow); 2497d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang dismiss(doAfter); 250ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp return; 251ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp } 25232d911f491afad153983f7519267ddb764927355mindyp final long diff = Math.abs(System.currentTimeMillis() - mLoadingShownTime); 25332d911f491afad153983f7519267ddb764927355mindyp if (diff > sMinShowTime) { 2547d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang dismiss(doAfter); 25532d911f491afad153983f7519267ddb764927355mindyp } else { 2569a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang mHandler.postDelayed(new FragmentRunnable("dismissLoadingStatus") { 2577d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang @Override 2589a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang public void go() { 2597d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang dismiss(doAfter); 2607d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang } 2617d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang }, Math.abs(sMinShowTime - diff)); 26232d911f491afad153983f7519267ddb764927355mindyp } 26332d911f491afad153983f7519267ddb764927355mindyp } 26432d911f491afad153983f7519267ddb764927355mindyp 2657d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang private void dismiss(final Runnable doAfter) { 26632d911f491afad153983f7519267ddb764927355mindyp // Reset loading shown time. 26732d911f491afad153983f7519267ddb764927355mindyp mLoadingShownTime = -1; 2689a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang mProgressView.setVisibility(View.GONE); 269ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp if (mBackgroundView.getVisibility() == View.VISIBLE) { 2709a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang animateDismiss(doAfter); 271ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp } else { 27230bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang if (doAfter != null) { 27330bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang doAfter.run(); 27430bcfe7b69f9d8e0cad2ee62dae9d3571cd0d88bAndy Huang } 275ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp } 276ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp } 277ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp 2789a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang private void animateDismiss(final Runnable doAfter) { 2799a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang // the animation can only work (and is only worth doing) if this fragment is added 2809a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang // reasons it may not be added: fragment is being destroyed, or in the process of being 2819a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang // restored 2829a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang if (!isAdded()) { 2839a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang mBackgroundView.setVisibility(View.GONE); 2849a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang return; 2859a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang } 2869a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang 2879a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang mBackgroundView.setLayerType(View.LAYER_TYPE_HARDWARE, null); 2889a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang mBackgroundView.buildLayer(); 2899a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang final Animator animator = AnimatorInflater.loadAnimator(getContext(), R.anim.fade_out); 2909a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang animator.setTarget(mBackgroundView); 2919a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang animator.addListener(new AnimatorListenerAdapter() { 2929a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang @Override 2939a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang public void onAnimationEnd(Animator animation) { 2949a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang mBackgroundView.setVisibility(View.GONE); 2959a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang mBackgroundView.setLayerType(View.LAYER_TYPE_NONE, null); 2969a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang if (doAfter != null) { 2979a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang doAfter.run(); 2989a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang } 2999a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang } 3009a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang }); 3019a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang animator.start(); 3029a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang } 3039a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang 304f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 305f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void onActivityCreated(Bundle savedInstanceState) { 306f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp super.onActivityCreated(savedInstanceState); 3078fe8ed43ef6e08b09d12b0827607d15699da3c06Vikram Aggarwal final Activity activity = getActivity(); 308f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (!(activity instanceof ControllableActivity)) { 309f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp LogUtils.wtf(LOG_TAG, "ConversationViewFragment expects only a ControllableActivity to" 310f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp + "create it. Cannot proceed."); 311f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 3128fe8ed43ef6e08b09d12b0827607d15699da3c06Vikram Aggarwal if (activity == null || activity.isFinishing()) { 313f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // Activity is finishing, just bail. 314f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return; 315f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 3168fe8ed43ef6e08b09d12b0827607d15699da3c06Vikram Aggarwal mActivity = (ControllableActivity) activity; 317f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mContext = activity.getApplicationContext(); 318f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mDateBuilder = new FormattedDateBuilder((Context) mActivity); 319f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mAccount = mAccountObserver.initialize(mActivity.getAccountController()); 320f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 321f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 322f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 323f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public ConversationUpdater getListController() { 324f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp final ControllableActivity activity = (ControllableActivity) getActivity(); 325f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return activity != null ? activity.getConversationUpdater() : null; 326f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 327f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 328ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp 329ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp protected void showLoadingStatus() { 33032d911f491afad153983f7519267ddb764927355mindyp if (!mUserVisible) { 33132d911f491afad153983f7519267ddb764927355mindyp return; 33232d911f491afad153983f7519267ddb764927355mindyp } 333ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp if (sMinDelay == -1) { 33432d911f491afad153983f7519267ddb764927355mindyp Resources res = getContext().getResources(); 33532d911f491afad153983f7519267ddb764927355mindyp sMinDelay = res.getInteger(R.integer.conversationview_show_loading_delay); 33632d911f491afad153983f7519267ddb764927355mindyp sMinShowTime = res.getInteger(R.integer.conversationview_min_show_loading); 337ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp } 33832d911f491afad153983f7519267ddb764927355mindyp // If the loading view isn't already showing, show it and remove any 33932d911f491afad153983f7519267ddb764927355mindyp // pending calls to show the loading screen. 34032d911f491afad153983f7519267ddb764927355mindyp mBackgroundView.setVisibility(View.VISIBLE); 341ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp mHandler.removeCallbacks(mDelayedShow); 342ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp mHandler.postDelayed(mDelayedShow, sMinDelay); 343ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp } 344ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp 345f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public Context getContext() { 346f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return mContext; 347f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 348f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 349f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public Conversation getConversation() { 350f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return mConversation; 351f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 352f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 353f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 354f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public MessageCursor getMessageCursor() { 355f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return mCursor; 356f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 357f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 358ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp public Handler getHandler() { 359ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp return mHandler; 360ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp } 361ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp 362f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public MessageLoaderCallbacks getMessageLoaderCallbacks() { 363f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return mMessageLoaderCallbacks; 364f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 365f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 366f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public ContactLoaderCallbacks getContactInfoSource() { 367f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return mContactLoaderCallbacks; 368f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 369f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 370f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 371f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public Account getAccount() { 372f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return mAccount; 373f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 374f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 375f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 376f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 377f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp super.onCreateOptionsMenu(menu, inflater); 378f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mChangeFoldersMenuItem = menu.findItem(R.id.change_folder); 379f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 380f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 381f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 382f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public boolean onOptionsItemSelected(MenuItem item) { 383f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp boolean handled = false; 384f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp switch (item.getItemId()) { 385f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp case R.id.inside_conversation_unread: 386f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp markUnread(); 387f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp handled = true; 388f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp break; 389f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 390f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return handled; 391f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 392f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 393f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // BEGIN conversation header callbacks 394f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 395f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void onFoldersClicked() { 396f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (mChangeFoldersMenuItem == null) { 397f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp LogUtils.e(LOG_TAG, "unable to open 'change folders' dialog for a conversation"); 398f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return; 399f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 400f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mActivity.onOptionsItemSelected(mChangeFoldersMenuItem); 401f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 402f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 403f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 404f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public String getSubjectRemainder(String subject) { 405f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp final SubjectDisplayChanger sdc = mActivity.getSubjectDisplayChanger(); 406f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (sdc == null) { 407f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return subject; 408f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 409f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return sdc.getUnshownSubject(subject); 410f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 411f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // END conversation header callbacks 412f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 413f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 4149d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang public void onSaveInstanceState(Bundle outState) { 4159d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang if (mViewState != null) { 4169d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang outState.putParcelable(BUNDLE_VIEW_STATE, mViewState); 4179d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 4189d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang outState.putBoolean(BUNDLE_USER_VISIBLE, mUserVisible); 4199d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 4209d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 4219d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang @Override 422f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void onDestroyView() { 423f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp super.onDestroyView(); 424f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mAccountObserver.unregisterAndDestroy(); 4254d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook if (mMarkReadObserver != null) { 4264d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook mActivity.getConversationUpdater().unregisterConversationListObserver( 4274d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook mMarkReadObserver); 4284d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook mMarkReadObserver = null; 4294d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook } 430f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 431f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 432f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp /** 433f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * {@link #setUserVisibleHint(boolean)} only works on API >= 15, so implement our own for 434f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * reliability on older platforms. 435f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp */ 436f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void setExtraUserVisibleHint(boolean isVisibleToUser) { 437f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp LogUtils.v(LOG_TAG, "in CVF.setHint, val=%s (%s)", isVisibleToUser, this); 438f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (mUserVisible != isVisibleToUser) { 439f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mUserVisible = isVisibleToUser; 440bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp MessageCursor cursor = getMessageCursor(); 4410b9b48c1ac510d6d0d043a86354880ceb57a8175mindyp if (mUserVisible && (cursor != null && cursor.isLoaded() && cursor.getCount() == 0)) { 442bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp // Pop back to conversation list and show error. 443bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp onError(); 444bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp return; 445bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp } 446f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp onUserVisibleHintChanged(); 447f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 448f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 449f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 4509d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang public boolean isUserVisible() { 4519d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang return mUserVisible; 4529d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 4539d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 454f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private class MessageLoaderCallbacks implements LoaderManager.LoaderCallbacks<Cursor> { 455f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 456f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 457f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public Loader<Cursor> onCreateLoader(int id, Bundle args) { 458f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return new MessageLoader(mActivity.getActivityContext(), mConversation, 459f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp AbstractConversationViewFragment.this); 460f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 461f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 462f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 463f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void onLoadFinished(Loader<Cursor> loader, Cursor data) { 464f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // ignore truly duplicate results 465f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // this can happen when restoring after rotation 466f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (mCursor == data) { 467f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return; 468f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } else { 4696766b6e5468d2f1935587b3bc1f8e65be94cd6fbAndy Huang final MessageCursor messageCursor = (MessageCursor) data; 470f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 471f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)) { 472f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp LogUtils.d(LOG_TAG, "LOADED CONVERSATION= %s", messageCursor.getDebugDump()); 473f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 474f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 475f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // When the last cursor had message(s), and the new version has 476f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // no messages, we need to exit conversation view. 477f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (messageCursor.getCount() == 0 && mCursor != null) { 478f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (mUserVisible) { 479bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp onError(); 480f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } else { 481f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // we expect that the pager adapter will remove this 482f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // conversation fragment on its own due to a separate 483f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // conversation cursor update (we might get here if the 484f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // message list update fires first. nothing to do 485f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // because we expect to be torn down soon.) 486f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp LogUtils.i(LOG_TAG, "CVF: offscreen conv has no messages, ignoring update" 487f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp + " in anticipation of conv cursor update. c=%s", mConversation.uri); 488f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 489233d435cba55eda0df580d3549b8a4e3b8d789a8Andy Huang // existing mCursor will imminently be closed, must stop referencing it 490233d435cba55eda0df580d3549b8a4e3b8d789a8Andy Huang // since we expect to be kicked out soon, it doesn't matter what mCursor 491233d435cba55eda0df580d3549b8a4e3b8d789a8Andy Huang // becomes 492233d435cba55eda0df580d3549b8a4e3b8d789a8Andy Huang mCursor = null; 493f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return; 494f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 495f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 496f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // ignore cursors that are still loading results 497f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (!messageCursor.isLoaded()) { 498233d435cba55eda0df580d3549b8a4e3b8d789a8Andy Huang // existing mCursor will imminently be closed, must stop referencing it 499233d435cba55eda0df580d3549b8a4e3b8d789a8Andy Huang // in this case, the new cursor is also no good, and since don't expect to get 500233d435cba55eda0df580d3549b8a4e3b8d789a8Andy Huang // here except in initial load situations, it's safest to just ensure the 501233d435cba55eda0df580d3549b8a4e3b8d789a8Andy Huang // reference is null 502233d435cba55eda0df580d3549b8a4e3b8d789a8Andy Huang mCursor = null; 503f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return; 504f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 505014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang final MessageCursor oldCursor = mCursor; 5066766b6e5468d2f1935587b3bc1f8e65be94cd6fbAndy Huang mCursor = messageCursor; 507014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang onMessageCursorLoadFinished(loader, mCursor, oldCursor); 508f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 509f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 510f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 511f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 512f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void onLoaderReset(Loader<Cursor> loader) { 513f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mCursor = null; 514f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 515f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 516f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 517f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 518bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp private void onError() { 519bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp // need to exit this view- conversation may have been 520bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp // deleted, or for whatever reason is now invalid (e.g. 521bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp // discard single draft) 522bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp // 523bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp // N.B. this may involve a fragment transaction, which 524bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp // FragmentManager will refuse to execute directly 525bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp // within onLoadFinished. Make sure the controller knows. 526bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp LogUtils.i(LOG_TAG, "CVF: visible conv has no messages, exiting conv mode"); 527bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp // TODO(mindyp): handle ERROR status by showing an error 528bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp // message to the user that there are no messages in 529bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp // this conversation 5309a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang mHandler.post(new FragmentRunnable("onError") { 531bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp 532bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp @Override 5339a8bc1ead816f0398bdbeac1e744ed458028b5c4Andy Huang public void go() { 534bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp mActivity.getListHandler() 535bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp .onConversationSelected(null, true /* inLoaderCallbacks */); 536bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp } 537bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp 538bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp }); 539bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp } 540bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp 5414d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook protected void onConversationSeen() { 5424d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook // Ignore unsafe calls made after a fragment is detached from an activity 5434d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook final ControllableActivity activity = (ControllableActivity) getActivity(); 5444d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook if (activity == null) { 5454d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook LogUtils.w(LOG_TAG, "ignoring onConversationSeen for conv=%s", mConversation.id); 5464d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook return; 5474d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook } 5484d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook 5494d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook mViewState.setInfoForConversation(mConversation); 5504d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook 5514d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook // mark viewed/read if not previously marked viewed by this conversation view, 5524d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook // or if unread messages still exist in the message list cursor 5534d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook // we don't want to keep marking viewed on rotation or restore 5544d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook // but we do want future re-renders to mark read (e.g. "New message from X" case) 5554d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook MessageCursor cursor = getMessageCursor(); 5564d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook if (!mConversation.isViewed() || (cursor != null && !cursor.isConversationRead())) { 5574d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook final ConversationUpdater listController = activity.getConversationUpdater(); 5584d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook // The conversation cursor may not have finished loading by now (when launched via 5594d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook // notification), so watch for when it finishes and mark it read then. 5604d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook if (listController.getConversationListCursor() == null) { 5614d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook LogUtils.i(LOG_TAG, "deferring conv mark read on open for id=%d", 5624d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook mConversation.id); 5634d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook mMarkReadObserver = new MarkReadObserver(listController); 5644d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook listController.registerConversationListObserver(mMarkReadObserver); 5654d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook } else { 5664d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook markReadOnSeen(listController); 5674d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook } 5684d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook } 5694d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook 5704d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook activity.getListHandler().onConversationSeen(mConversation); 5714d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook } 5724d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook 5734d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook protected void markReadOnSeen(ConversationUpdater listController) { 5744d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook // Mark the conversation viewed and read. 5754d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook listController.markConversationsRead(Arrays.asList(mConversation), true /* read */, 5764d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook true /* viewed */); 5774d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook 5784d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook // and update the Message objects in the cursor so the next time a cursor update happens 5794d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook // with these messages marked read, we know to ignore it 5804d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook MessageCursor cursor = getMessageCursor(); 5814d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook if (cursor != null) { 5824d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook cursor.markMessagesRead(); 5834d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook } 5844d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook } 5854d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook 5864d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook protected ConversationViewState getNewViewState() { 5874d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook return new ConversationViewState(); 5884d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook } 5894d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook 590f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private static class MessageLoader extends CursorLoader { 591f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private boolean mDeliveredFirstResults = false; 592f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private final Conversation mConversation; 593f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private final ConversationController mController; 594f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 595f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public MessageLoader(Context c, Conversation conv, ConversationController controller) { 596f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp super(c, conv.messageListUri, UIProvider.MESSAGE_PROJECTION, null, null, null); 597f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mConversation = conv; 598f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mController = controller; 599f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 600f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 601f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 602f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public Cursor loadInBackground() { 603f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return new MessageCursor(super.loadInBackground(), mConversation, mController); 604f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 605f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 606f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 607f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void deliverResult(Cursor result) { 608f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // We want to deliver these results, and then we want to make sure 609f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // that any subsequent 610f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // queries do not hit the network 611f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp super.deliverResult(result); 612f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 613f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (!mDeliveredFirstResults) { 614f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mDeliveredFirstResults = true; 615f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp Uri uri = getUri(); 616f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 617f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // Create a ListParams that tells the provider to not hit the 618f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // network 619f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp final ListParams listParams = new ListParams(ListParams.NO_LIMIT, 620f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp false /* useNetwork */); 621f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 622f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // Build the new uri with this additional parameter 623f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp uri = uri 624f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp .buildUpon() 625f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp .appendQueryParameter(UIProvider.LIST_PARAMS_QUERY_PARAMETER, 626f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp listParams.serialize()).build(); 627f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp setUri(uri); 628f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 629f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 630f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 631f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 632f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp /** 633f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * Inner class to to asynchronously load contact data for all senders in the conversation, 634f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * and notify observers when the data is ready. 635f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * 636f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp */ 637f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp protected class ContactLoaderCallbacks implements ContactInfoSource, 638f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp LoaderManager.LoaderCallbacks<ImmutableMap<String, ContactInfo>> { 639f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 640f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private Set<String> mSenders; 641f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private ImmutableMap<String, ContactInfo> mContactInfoMap; 642f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private DataSetObservable mObservable = new DataSetObservable(); 643f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 644f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void setSenders(Set<String> emailAddresses) { 645f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mSenders = emailAddresses; 646f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 647f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 648f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 649f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public Loader<ImmutableMap<String, ContactInfo>> onCreateLoader(int id, Bundle args) { 650f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return new SenderInfoLoader(mActivity.getActivityContext(), mSenders); 651f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 652f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 653f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 654f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void onLoadFinished(Loader<ImmutableMap<String, ContactInfo>> loader, 655f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp ImmutableMap<String, ContactInfo> data) { 656f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mContactInfoMap = data; 657f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mObservable.notifyChanged(); 658f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 659f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 660f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 661f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void onLoaderReset(Loader<ImmutableMap<String, ContactInfo>> loader) { 662f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 663f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 664f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 665f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public ContactInfo getContactInfo(String email) { 666f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (mContactInfoMap == null) { 667f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return null; 668f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 669f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return mContactInfoMap.get(email); 670f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 671f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 672f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 673f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void registerObserver(DataSetObserver observer) { 674f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mObservable.registerObserver(observer); 675f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 676f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 677f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 678f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void unregisterObserver(DataSetObserver observer) { 679f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mObservable.unregisterObserver(observer); 680f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 681542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook } 682542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook 683542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook protected class AbstractConversationWebViewClient extends WebViewClient { 684542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook @Override 685542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook public boolean shouldOverrideUrlLoading(WebView view, String url) { 686542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook final Activity activity = getActivity(); 687542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook if (activity == null) { 688542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook return false; 689542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook } 690542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook 691542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook boolean result = false; 692542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook final Intent intent; 693542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook Uri uri = Uri.parse(url); 694542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook if (!Utils.isEmpty(mAccount.viewIntentProxyUri)) { 695542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook intent = generateProxyIntent(uri); 696542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook } else { 697542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook intent = new Intent(Intent.ACTION_VIEW, uri); 698542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook intent.putExtra(Browser.EXTRA_APPLICATION_ID, activity.getPackageName()); 699542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook } 700f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 701542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook try { 702542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 703542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook activity.startActivity(intent); 704542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook result = true; 705542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook } catch (ActivityNotFoundException ex) { 706542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook // If no application can handle the URL, assume that the 707542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook // caller can handle it. 708542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook } 709542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook 710542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook return result; 711542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook } 712542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook 713542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook private Intent generateProxyIntent(Uri uri) { 714542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook final Intent intent = new Intent(Intent.ACTION_VIEW, mAccount.viewIntentProxyUri); 715542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook intent.putExtra(UIProvider.ViewProxyExtras.EXTRA_ORIGINAL_URI, uri); 716542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook intent.putExtra(UIProvider.ViewProxyExtras.EXTRA_ACCOUNT, mAccount); 717542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook 718542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook final Context context = getContext(); 719542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook PackageManager manager = null; 720542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook // We need to catch the exception to make CanvasConversationHeaderView 721542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook // test pass. Bug: http://b/issue?id=3470653. 722542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook try { 723542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook manager = context.getPackageManager(); 724542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook } catch (UnsupportedOperationException e) { 725542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook LogUtils.e(LOG_TAG, e, "Error getting package manager"); 726542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook } 727542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook 728542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook if (manager != null) { 729542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook // Try and resolve the intent, to find an activity from this package 730542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook final List<ResolveInfo> resolvedActivities = manager.queryIntentActivities( 731542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook intent, PackageManager.MATCH_DEFAULT_ONLY); 732542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook 733542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook final String packageName = context.getPackageName(); 734542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook 735542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook // Now try and find one that came from this package, if one is not found, the UI 736542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook // provider must have specified an intent that is to be handled by a different apk. 737542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook // In that case, the class name will not be set on the intent, so the default 738542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook // intent resolution will be used. 739542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook for (ResolveInfo resolveInfo: resolvedActivities) { 740542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook final ActivityInfo activityInfo = resolveInfo.activityInfo; 741542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook if (packageName.equals(activityInfo.packageName)) { 742542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook intent.setClassName(activityInfo.packageName, activityInfo.name); 743542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook break; 744542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook } 745542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook } 746542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook } 747542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook 748542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook return intent; 749542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook } 750f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 751542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook 7524d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook private class MarkReadObserver extends DataSetObserver { 7534d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook private final ConversationUpdater mListController; 7544d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook 7554d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook private MarkReadObserver(ConversationUpdater listController) { 7564d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook mListController = listController; 7574d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook } 7584d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook 7594d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook @Override 7604d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook public void onChanged() { 7614d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook if (mListController.getConversationListCursor() == null) { 7624d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook // nothing yet, keep watching 7634d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook return; 7644d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook } 7654d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook // done loading, safe to mark read now 7664d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook mListController.unregisterConversationListObserver(this); 7674d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook mMarkReadObserver = null; 7684d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook LogUtils.i(LOG_TAG, "running deferred conv mark read on open, id=%d", mConversation.id); 7694d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook markReadOnSeen(mListController); 7704d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook } 7714d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook } 7724d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook 77326d4d2d9c43c499f458808f050ec73ea3c28dec4mindyp public abstract void onConversationUpdated(Conversation conversation); 77426d4d2d9c43c499f458808f050ec73ea3c28dec4mindyp 7759d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang /** 7769d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang * Small Runnable-like wrapper that first checks that the Fragment is in a good state before 7779d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang * doing any work. Ideal for use with a {@link Handler}. 7789d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang */ 7799d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang protected abstract class FragmentRunnable implements Runnable { 7809d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 7819d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang private final String mOpName; 7829d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 7839d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang public FragmentRunnable(String opName) { 7849d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang mOpName = opName; 7859d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 7869d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 7879d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang public abstract void go(); 7889d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 7899d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang @Override 7909d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang public void run() { 7919d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang if (!isAdded()) { 7929d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang LogUtils.i(LOG_TAG, "Unable to run op='%s' b/c fragment is not attached: %s", 7939d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang mOpName, AbstractConversationViewFragment.this); 7949d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang return; 7959d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 7969d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang go(); 7979d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 7989d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 7999d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 8009d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 801f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp} 802