AbstractConversationViewFragment.java revision 233d435cba55eda0df580d3549b8a4e3b8d789a8
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 1267d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang private final Runnable mDelayedShow = new Runnable() { 127ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp @Override 128ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp public void run() { 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"; 1459d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang private static final String BUNDLE_USER_VISIBLE = 1469d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang AbstractConversationViewFragment.class.getName() + "uservisible"; 1479d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 148f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public static Bundle makeBasicArgs(Account account, Folder folder) { 149f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp Bundle args = new Bundle(); 150f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp args.putParcelable(ARG_ACCOUNT, account); 151f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp args.putParcelable(ARG_FOLDER, folder); 152f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return args; 153f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 154f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 155f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp /** 156f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * Constructor needs to be public to handle orientation changes and activity 157f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * lifecycle events. 158f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp */ 159f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public AbstractConversationViewFragment() { 160f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp super(); 161f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 162f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 163f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp /** 164f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * Subclasses must override, since this depends on how many messages are 165f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * shown in the conversation view. 166f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp */ 167f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp protected abstract void markUnread(); 168f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 169f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp /** 170f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * Subclasses must override this, since they may want to display a single or 171f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * many messages related to this conversation. 172f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp */ 173014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang protected abstract void onMessageCursorLoadFinished(Loader<Cursor> loader, 174014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang MessageCursor newCursor, MessageCursor oldCursor); 175f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 176f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp /** 177f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * Subclasses must override this, since they may want to display a single or 178f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * many messages related to this conversation. 179f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp */ 180f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 181f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public abstract void onConversationViewHeaderHeightChange(int newHeight); 182f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 183f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public abstract void onUserVisibleHintChanged(); 184f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 185f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp /** 186f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * Subclasses must override this. 187f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp */ 188adbf3e8cadb66666f307352b72537fbac57b916fAndy Huang protected abstract void onAccountChanged(Account newAccount, Account oldAccount); 189f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 190f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 191f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void onCreate(Bundle savedState) { 192f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp super.onCreate(savedState); 193f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 194f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp final Bundle args = getArguments(); 195f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mAccount = args.getParcelable(ARG_ACCOUNT); 196f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mConversation = args.getParcelable(ARG_CONVERSATION); 197f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mFolder = args.getParcelable(ARG_FOLDER); 198ba4cce62e2de2b818190b81bb07ecc5e94544165Paul Westbrook 199ba4cce62e2de2b818190b81bb07ecc5e94544165Paul Westbrook // Since the uri specified in the conversation base uri may not be unique, we specify a 200ba4cce62e2de2b818190b81bb07ecc5e94544165Paul Westbrook // base uri that us guaranteed to be unique for this conversation. 201ba4cce62e2de2b818190b81bb07ecc5e94544165Paul Westbrook mBaseUri = "x-thread://" + mAccount.name + "/" + mConversation.id; 202ba4cce62e2de2b818190b81bb07ecc5e94544165Paul Westbrook 203f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // On JB or newer, we use the 'webkitAnimationStart' DOM event to signal load complete 204f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // Below JB, try to speed up initial render by having the webview do supplemental draws to 205f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // custom a software canvas. 206f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // TODO(mindyp): 207f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp //PAGE READINESS SIGNAL FOR JELLYBEAN AND NEWER 208f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // Notify the app on 'webkitAnimationStart' of a simple dummy element with a simple no-op 209f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // animation that immediately runs on page load. The app uses this as a signal that the 210f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // content is loaded and ready to draw, since WebView delays firing this event until the 211f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // layers are composited and everything is ready to draw. 212f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // This signal does not seem to be reliable, so just use the old method for now. 213f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mEnableContentReadySignal = false; //Utils.isRunningJellybeanOrLater(); 214f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp LogUtils.d(LOG_TAG, "onCreate in ConversationViewFragment (this=%s)", this); 215f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // Not really, we just want to get a crack to store a reference to the change_folder item 216f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp setHasOptionsMenu(true); 2179d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 2189d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang if (savedState != null) { 2199d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang mViewState = savedState.getParcelable(BUNDLE_VIEW_STATE); 2209d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang mUserVisible = savedState.getBoolean(BUNDLE_USER_VISIBLE); 2219d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } else { 2229d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang mViewState = getNewViewState(); 2239d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 224f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 225f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 226ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp public void instantiateProgressIndicators(View rootView) { 227ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp mBackgroundView = rootView.findViewById(R.id.background_view); 228ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp mProgressView = rootView.findViewById(R.id.loading_progress); 229ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp } 230ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp 231ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp protected void dismissLoadingStatus() { 2327d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang dismissLoadingStatus(null); 2337d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang } 2347d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang 2357d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang /** 2367d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang * Begin the fade-out animation to hide the Progress overlay, either immediately or after some 2377d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang * timeout (to ensure that the progress minimum time elapses). 2387d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang * 2397d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang * @param doAfter an optional Runnable action to execute after the animation completes 2407d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang */ 2417d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang protected void dismissLoadingStatus(final Runnable doAfter) { 24232d911f491afad153983f7519267ddb764927355mindyp if (mLoadingShownTime == -1) { 243ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp // The runnable hasn't run yet, so just remove it. 244ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp mHandler.removeCallbacks(mDelayedShow); 2457d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang dismiss(doAfter); 246ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp return; 247ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp } 24832d911f491afad153983f7519267ddb764927355mindyp final long diff = Math.abs(System.currentTimeMillis() - mLoadingShownTime); 24932d911f491afad153983f7519267ddb764927355mindyp if (diff > sMinShowTime) { 2507d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang dismiss(doAfter); 25132d911f491afad153983f7519267ddb764927355mindyp } else { 2527d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang mHandler.postDelayed(new Runnable() { 2537d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang @Override 2547d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang public void run() { 2557d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang dismiss(doAfter); 2567d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang } 2577d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang }, Math.abs(sMinShowTime - diff)); 25832d911f491afad153983f7519267ddb764927355mindyp } 25932d911f491afad153983f7519267ddb764927355mindyp } 26032d911f491afad153983f7519267ddb764927355mindyp 2617d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang private void dismiss(final Runnable doAfter) { 26232d911f491afad153983f7519267ddb764927355mindyp // Reset loading shown time. 26332d911f491afad153983f7519267ddb764927355mindyp mLoadingShownTime = -1; 264ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp // Fade out the info view. 265ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp if (mBackgroundView.getVisibility() == View.VISIBLE) { 2667d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang mProgressView.setVisibility(View.GONE); 2677d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang final Animator animator = AnimatorInflater.loadAnimator(getContext(), R.anim.fade_out); 268ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp animator.setTarget(mBackgroundView); 2697d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang animator.addListener(new AnimatorListenerAdapter() { 270ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp @Override 271ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp public void onAnimationEnd(Animator animation) { 272ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp mBackgroundView.setVisibility(View.GONE); 2737d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang if (doAfter != null) { 2747d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang doAfter.run(); 2757d4746ec15076f8982f2dc2a4bdb982b78848433Andy Huang } 276ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp } 277ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp }); 278ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp animator.start(); 279ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp } else { 280ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp mBackgroundView.setVisibility(View.GONE); 281ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp mProgressView.setVisibility(View.GONE); 282ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp } 283ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp } 284ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp 285f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 286f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void onActivityCreated(Bundle savedInstanceState) { 287f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp super.onActivityCreated(savedInstanceState); 2888fe8ed43ef6e08b09d12b0827607d15699da3c06Vikram Aggarwal final Activity activity = getActivity(); 289f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (!(activity instanceof ControllableActivity)) { 290f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp LogUtils.wtf(LOG_TAG, "ConversationViewFragment expects only a ControllableActivity to" 291f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp + "create it. Cannot proceed."); 292f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 2938fe8ed43ef6e08b09d12b0827607d15699da3c06Vikram Aggarwal if (activity == null || activity.isFinishing()) { 294f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // Activity is finishing, just bail. 295f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return; 296f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 2978fe8ed43ef6e08b09d12b0827607d15699da3c06Vikram Aggarwal mActivity = (ControllableActivity) activity; 298f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mContext = activity.getApplicationContext(); 299f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mDateBuilder = new FormattedDateBuilder((Context) mActivity); 300f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mAccount = mAccountObserver.initialize(mActivity.getAccountController()); 301f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 302f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 303f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 304f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public ConversationUpdater getListController() { 305f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp final ControllableActivity activity = (ControllableActivity) getActivity(); 306f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return activity != null ? activity.getConversationUpdater() : null; 307f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 308f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 309ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp 310ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp protected void showLoadingStatus() { 31132d911f491afad153983f7519267ddb764927355mindyp if (!mUserVisible) { 31232d911f491afad153983f7519267ddb764927355mindyp return; 31332d911f491afad153983f7519267ddb764927355mindyp } 314ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp if (sMinDelay == -1) { 31532d911f491afad153983f7519267ddb764927355mindyp Resources res = getContext().getResources(); 31632d911f491afad153983f7519267ddb764927355mindyp sMinDelay = res.getInteger(R.integer.conversationview_show_loading_delay); 31732d911f491afad153983f7519267ddb764927355mindyp sMinShowTime = res.getInteger(R.integer.conversationview_min_show_loading); 318ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp } 31932d911f491afad153983f7519267ddb764927355mindyp // If the loading view isn't already showing, show it and remove any 32032d911f491afad153983f7519267ddb764927355mindyp // pending calls to show the loading screen. 32132d911f491afad153983f7519267ddb764927355mindyp mBackgroundView.setVisibility(View.VISIBLE); 322ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp mHandler.removeCallbacks(mDelayedShow); 323ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp mHandler.postDelayed(mDelayedShow, sMinDelay); 324ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp } 325ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp 326f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public Context getContext() { 327f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return mContext; 328f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 329f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 330f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public Conversation getConversation() { 331f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return mConversation; 332f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 333f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 334f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 335f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public MessageCursor getMessageCursor() { 336f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return mCursor; 337f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 338f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 339ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp public Handler getHandler() { 340ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp return mHandler; 341ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp } 342ff282d0ef252dbdaf6e9f4e2a7fd640287c01e6bmindyp 343f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public MessageLoaderCallbacks getMessageLoaderCallbacks() { 344f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return mMessageLoaderCallbacks; 345f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 346f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 347f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public ContactLoaderCallbacks getContactInfoSource() { 348f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return mContactLoaderCallbacks; 349f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 350f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 351f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 352f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public Account getAccount() { 353f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return mAccount; 354f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 355f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 356f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 357f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 358f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp super.onCreateOptionsMenu(menu, inflater); 359f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mChangeFoldersMenuItem = menu.findItem(R.id.change_folder); 360f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 361f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 362f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 363f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public boolean onOptionsItemSelected(MenuItem item) { 364f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp boolean handled = false; 365f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp switch (item.getItemId()) { 366f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp case R.id.inside_conversation_unread: 367f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp markUnread(); 368f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp handled = true; 369f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp break; 370f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 371f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return handled; 372f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 373f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 374f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // BEGIN conversation header callbacks 375f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 376f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void onFoldersClicked() { 377f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (mChangeFoldersMenuItem == null) { 378f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp LogUtils.e(LOG_TAG, "unable to open 'change folders' dialog for a conversation"); 379f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return; 380f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 381f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mActivity.onOptionsItemSelected(mChangeFoldersMenuItem); 382f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 383f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 384f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 385f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public String getSubjectRemainder(String subject) { 386f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp final SubjectDisplayChanger sdc = mActivity.getSubjectDisplayChanger(); 387f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (sdc == null) { 388f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return subject; 389f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 390f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return sdc.getUnshownSubject(subject); 391f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 392f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // END conversation header callbacks 393f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 394f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 3959d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang public void onSaveInstanceState(Bundle outState) { 3969d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang if (mViewState != null) { 3979d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang outState.putParcelable(BUNDLE_VIEW_STATE, mViewState); 3989d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 3999d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang outState.putBoolean(BUNDLE_USER_VISIBLE, mUserVisible); 4009d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 4019d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 4029d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang @Override 403f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void onDestroyView() { 404f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp super.onDestroyView(); 405f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mAccountObserver.unregisterAndDestroy(); 4064d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook if (mMarkReadObserver != null) { 4074d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook mActivity.getConversationUpdater().unregisterConversationListObserver( 4084d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook mMarkReadObserver); 4094d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook mMarkReadObserver = null; 4104d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook } 411f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 412f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 413f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp /** 414f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * {@link #setUserVisibleHint(boolean)} only works on API >= 15, so implement our own for 415f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * reliability on older platforms. 416f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp */ 417f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void setExtraUserVisibleHint(boolean isVisibleToUser) { 418f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp LogUtils.v(LOG_TAG, "in CVF.setHint, val=%s (%s)", isVisibleToUser, this); 419f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (mUserVisible != isVisibleToUser) { 420f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mUserVisible = isVisibleToUser; 421bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp MessageCursor cursor = getMessageCursor(); 4220b9b48c1ac510d6d0d043a86354880ceb57a8175mindyp if (mUserVisible && (cursor != null && cursor.isLoaded() && cursor.getCount() == 0)) { 423bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp // Pop back to conversation list and show error. 424bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp onError(); 425bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp return; 426bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp } 427f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp onUserVisibleHintChanged(); 428f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 429f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 430f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 4319d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang public boolean isUserVisible() { 4329d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang return mUserVisible; 4339d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 4349d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 435f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private class MessageLoaderCallbacks implements LoaderManager.LoaderCallbacks<Cursor> { 436f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 437f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 438f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public Loader<Cursor> onCreateLoader(int id, Bundle args) { 439f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return new MessageLoader(mActivity.getActivityContext(), mConversation, 440f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp AbstractConversationViewFragment.this); 441f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 442f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 443f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 444f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void onLoadFinished(Loader<Cursor> loader, Cursor data) { 445f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // ignore truly duplicate results 446f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // this can happen when restoring after rotation 447f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (mCursor == data) { 448f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return; 449f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } else { 4506766b6e5468d2f1935587b3bc1f8e65be94cd6fbAndy Huang final MessageCursor messageCursor = (MessageCursor) data; 451f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 452f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)) { 453f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp LogUtils.d(LOG_TAG, "LOADED CONVERSATION= %s", messageCursor.getDebugDump()); 454f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 455f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 456f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // When the last cursor had message(s), and the new version has 457f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // no messages, we need to exit conversation view. 458f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (messageCursor.getCount() == 0 && mCursor != null) { 459f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (mUserVisible) { 460bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp onError(); 461f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } else { 462f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // we expect that the pager adapter will remove this 463f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // conversation fragment on its own due to a separate 464f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // conversation cursor update (we might get here if the 465f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // message list update fires first. nothing to do 466f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // because we expect to be torn down soon.) 467f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp LogUtils.i(LOG_TAG, "CVF: offscreen conv has no messages, ignoring update" 468f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp + " in anticipation of conv cursor update. c=%s", mConversation.uri); 469f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 470233d435cba55eda0df580d3549b8a4e3b8d789a8Andy Huang // existing mCursor will imminently be closed, must stop referencing it 471233d435cba55eda0df580d3549b8a4e3b8d789a8Andy Huang // since we expect to be kicked out soon, it doesn't matter what mCursor 472233d435cba55eda0df580d3549b8a4e3b8d789a8Andy Huang // becomes 473233d435cba55eda0df580d3549b8a4e3b8d789a8Andy Huang mCursor = null; 474f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return; 475f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 476f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 477f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // ignore cursors that are still loading results 478f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (!messageCursor.isLoaded()) { 479233d435cba55eda0df580d3549b8a4e3b8d789a8Andy Huang // existing mCursor will imminently be closed, must stop referencing it 480233d435cba55eda0df580d3549b8a4e3b8d789a8Andy Huang // in this case, the new cursor is also no good, and since don't expect to get 481233d435cba55eda0df580d3549b8a4e3b8d789a8Andy Huang // here except in initial load situations, it's safest to just ensure the 482233d435cba55eda0df580d3549b8a4e3b8d789a8Andy Huang // reference is null 483233d435cba55eda0df580d3549b8a4e3b8d789a8Andy Huang mCursor = null; 484f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return; 485f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 486014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang final MessageCursor oldCursor = mCursor; 4876766b6e5468d2f1935587b3bc1f8e65be94cd6fbAndy Huang mCursor = messageCursor; 488014ea4c15d147794789b9c5bf4e243fa08781ad9Andy Huang onMessageCursorLoadFinished(loader, mCursor, oldCursor); 489f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 490f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 491f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 492f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 493f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void onLoaderReset(Loader<Cursor> loader) { 494f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mCursor = null; 495f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 496f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 497f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 498f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 499bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp private void onError() { 500bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp // need to exit this view- conversation may have been 501bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp // deleted, or for whatever reason is now invalid (e.g. 502bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp // discard single draft) 503bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp // 504bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp // N.B. this may involve a fragment transaction, which 505bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp // FragmentManager will refuse to execute directly 506bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp // within onLoadFinished. Make sure the controller knows. 507bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp LogUtils.i(LOG_TAG, "CVF: visible conv has no messages, exiting conv mode"); 508bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp // TODO(mindyp): handle ERROR status by showing an error 509bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp // message to the user that there are no messages in 510bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp // this conversation 511bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp mHandler.post(new Runnable() { 512bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp 513bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp @Override 514bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp public void run() { 515bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp mActivity.getListHandler() 516bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp .onConversationSelected(null, true /* inLoaderCallbacks */); 517bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp } 518bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp 519bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp }); 520bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp } 521bc4142f40e3bce1b32bcf6e890d5040c214ac2e7mindyp 5224d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook protected void onConversationSeen() { 5234d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook // Ignore unsafe calls made after a fragment is detached from an activity 5244d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook final ControllableActivity activity = (ControllableActivity) getActivity(); 5254d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook if (activity == null) { 5264d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook LogUtils.w(LOG_TAG, "ignoring onConversationSeen for conv=%s", mConversation.id); 5274d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook return; 5284d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook } 5294d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook 5304d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook mViewState.setInfoForConversation(mConversation); 5314d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook 5324d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook // mark viewed/read if not previously marked viewed by this conversation view, 5334d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook // or if unread messages still exist in the message list cursor 5344d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook // we don't want to keep marking viewed on rotation or restore 5354d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook // but we do want future re-renders to mark read (e.g. "New message from X" case) 5364d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook MessageCursor cursor = getMessageCursor(); 5374d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook if (!mConversation.isViewed() || (cursor != null && !cursor.isConversationRead())) { 5384d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook final ConversationUpdater listController = activity.getConversationUpdater(); 5394d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook // The conversation cursor may not have finished loading by now (when launched via 5404d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook // notification), so watch for when it finishes and mark it read then. 5414d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook if (listController.getConversationListCursor() == null) { 5424d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook LogUtils.i(LOG_TAG, "deferring conv mark read on open for id=%d", 5434d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook mConversation.id); 5444d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook mMarkReadObserver = new MarkReadObserver(listController); 5454d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook listController.registerConversationListObserver(mMarkReadObserver); 5464d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook } else { 5474d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook markReadOnSeen(listController); 5484d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook } 5494d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook } 5504d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook 5514d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook activity.getListHandler().onConversationSeen(mConversation); 5524d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook } 5534d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook 5544d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook protected void markReadOnSeen(ConversationUpdater listController) { 5554d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook // Mark the conversation viewed and read. 5564d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook listController.markConversationsRead(Arrays.asList(mConversation), true /* read */, 5574d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook true /* viewed */); 5584d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook 5594d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook // and update the Message objects in the cursor so the next time a cursor update happens 5604d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook // with these messages marked read, we know to ignore it 5614d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook MessageCursor cursor = getMessageCursor(); 5624d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook if (cursor != null) { 5634d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook cursor.markMessagesRead(); 5644d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook } 5654d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook } 5664d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook 5674d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook protected ConversationViewState getNewViewState() { 5684d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook return new ConversationViewState(); 5694d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook } 5704d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook 571f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private static class MessageLoader extends CursorLoader { 572f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private boolean mDeliveredFirstResults = false; 573f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private final Conversation mConversation; 574f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private final ConversationController mController; 575f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 576f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public MessageLoader(Context c, Conversation conv, ConversationController controller) { 577f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp super(c, conv.messageListUri, UIProvider.MESSAGE_PROJECTION, null, null, null); 578f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mConversation = conv; 579f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mController = controller; 580f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 581f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 582f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 583f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public Cursor loadInBackground() { 584f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return new MessageCursor(super.loadInBackground(), mConversation, mController); 585f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 586f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 587f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 588f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void deliverResult(Cursor result) { 589f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // We want to deliver these results, and then we want to make sure 590f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // that any subsequent 591f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // queries do not hit the network 592f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp super.deliverResult(result); 593f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 594f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (!mDeliveredFirstResults) { 595f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mDeliveredFirstResults = true; 596f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp Uri uri = getUri(); 597f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 598f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // Create a ListParams that tells the provider to not hit the 599f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // network 600f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp final ListParams listParams = new ListParams(ListParams.NO_LIMIT, 601f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp false /* useNetwork */); 602f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 603f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp // Build the new uri with this additional parameter 604f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp uri = uri 605f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp .buildUpon() 606f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp .appendQueryParameter(UIProvider.LIST_PARAMS_QUERY_PARAMETER, 607f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp listParams.serialize()).build(); 608f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp setUri(uri); 609f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 610f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 611f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 612f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 613f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp /** 614f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * Inner class to to asynchronously load contact data for all senders in the conversation, 615f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * and notify observers when the data is ready. 616f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp * 617f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp */ 618f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp protected class ContactLoaderCallbacks implements ContactInfoSource, 619f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp LoaderManager.LoaderCallbacks<ImmutableMap<String, ContactInfo>> { 620f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 621f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private Set<String> mSenders; 622f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private ImmutableMap<String, ContactInfo> mContactInfoMap; 623f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp private DataSetObservable mObservable = new DataSetObservable(); 624f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 625f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void setSenders(Set<String> emailAddresses) { 626f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mSenders = emailAddresses; 627f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 628f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 629f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 630f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public Loader<ImmutableMap<String, ContactInfo>> onCreateLoader(int id, Bundle args) { 631f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return new SenderInfoLoader(mActivity.getActivityContext(), mSenders); 632f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 633f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 634f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 635f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void onLoadFinished(Loader<ImmutableMap<String, ContactInfo>> loader, 636f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp ImmutableMap<String, ContactInfo> data) { 637f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mContactInfoMap = data; 638f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mObservable.notifyChanged(); 639f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 640f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 641f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 642f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void onLoaderReset(Loader<ImmutableMap<String, ContactInfo>> loader) { 643f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 644f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 645f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 646f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public ContactInfo getContactInfo(String email) { 647f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp if (mContactInfoMap == null) { 648f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return null; 649f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 650f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp return mContactInfoMap.get(email); 651f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 652f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 653f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 654f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void registerObserver(DataSetObserver observer) { 655f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mObservable.registerObserver(observer); 656f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 657f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 658f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp @Override 659f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp public void unregisterObserver(DataSetObserver observer) { 660f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp mObservable.unregisterObserver(observer); 661f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 662542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook } 663542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook 664542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook protected class AbstractConversationWebViewClient extends WebViewClient { 665542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook @Override 666542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook public boolean shouldOverrideUrlLoading(WebView view, String url) { 667542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook final Activity activity = getActivity(); 668542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook if (activity == null) { 669542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook return false; 670542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook } 671542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook 672542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook boolean result = false; 673542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook final Intent intent; 674542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook Uri uri = Uri.parse(url); 675542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook if (!Utils.isEmpty(mAccount.viewIntentProxyUri)) { 676542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook intent = generateProxyIntent(uri); 677542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook } else { 678542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook intent = new Intent(Intent.ACTION_VIEW, uri); 679542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook intent.putExtra(Browser.EXTRA_APPLICATION_ID, activity.getPackageName()); 680542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook } 681f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp 682542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook try { 683542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 684542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook activity.startActivity(intent); 685542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook result = true; 686542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook } catch (ActivityNotFoundException ex) { 687542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook // If no application can handle the URL, assume that the 688542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook // caller can handle it. 689542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook } 690542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook 691542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook return result; 692542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook } 693542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook 694542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook private Intent generateProxyIntent(Uri uri) { 695542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook final Intent intent = new Intent(Intent.ACTION_VIEW, mAccount.viewIntentProxyUri); 696542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook intent.putExtra(UIProvider.ViewProxyExtras.EXTRA_ORIGINAL_URI, uri); 697542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook intent.putExtra(UIProvider.ViewProxyExtras.EXTRA_ACCOUNT, mAccount); 698542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook 699542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook final Context context = getContext(); 700542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook PackageManager manager = null; 701542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook // We need to catch the exception to make CanvasConversationHeaderView 702542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook // test pass. Bug: http://b/issue?id=3470653. 703542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook try { 704542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook manager = context.getPackageManager(); 705542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook } catch (UnsupportedOperationException e) { 706542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook LogUtils.e(LOG_TAG, e, "Error getting package manager"); 707542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook } 708542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook 709542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook if (manager != null) { 710542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook // Try and resolve the intent, to find an activity from this package 711542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook final List<ResolveInfo> resolvedActivities = manager.queryIntentActivities( 712542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook intent, PackageManager.MATCH_DEFAULT_ONLY); 713542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook 714542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook final String packageName = context.getPackageName(); 715542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook 716542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook // Now try and find one that came from this package, if one is not found, the UI 717542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook // provider must have specified an intent that is to be handled by a different apk. 718542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook // In that case, the class name will not be set on the intent, so the default 719542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook // intent resolution will be used. 720542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook for (ResolveInfo resolveInfo: resolvedActivities) { 721542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook final ActivityInfo activityInfo = resolveInfo.activityInfo; 722542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook if (packageName.equals(activityInfo.packageName)) { 723542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook intent.setClassName(activityInfo.packageName, activityInfo.name); 724542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook break; 725542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook } 726542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook } 727542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook } 728542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook 729542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook return intent; 730542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook } 731f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp } 732542fec98d011c78782b63b33d29cf81044e96f75Paul Westbrook 7334d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook private class MarkReadObserver extends DataSetObserver { 7344d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook private final ConversationUpdater mListController; 7354d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook 7364d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook private MarkReadObserver(ConversationUpdater listController) { 7374d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook mListController = listController; 7384d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook } 7394d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook 7404d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook @Override 7414d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook public void onChanged() { 7424d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook if (mListController.getConversationListCursor() == null) { 7434d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook // nothing yet, keep watching 7444d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook return; 7454d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook } 7464d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook // done loading, safe to mark read now 7474d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook mListController.unregisterConversationListObserver(this); 7484d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook mMarkReadObserver = null; 7494d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook LogUtils.i(LOG_TAG, "running deferred conv mark read on open, id=%d", mConversation.id); 7504d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook markReadOnSeen(mListController); 7514d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook } 7524d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook } 7534d8cad5e37ade03903a23cca8ea3e782af21170fPaul Westbrook 75426d4d2d9c43c499f458808f050ec73ea3c28dec4mindyp public abstract void onConversationUpdated(Conversation conversation); 75526d4d2d9c43c499f458808f050ec73ea3c28dec4mindyp 7569d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang /** 7579d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang * Small Runnable-like wrapper that first checks that the Fragment is in a good state before 7589d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang * doing any work. Ideal for use with a {@link Handler}. 7599d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang */ 7609d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang protected abstract class FragmentRunnable implements Runnable { 7619d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 7629d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang private final String mOpName; 7639d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 7649d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang public FragmentRunnable(String opName) { 7659d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang mOpName = opName; 7669d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 7679d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 7689d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang public abstract void go(); 7699d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 7709d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang @Override 7719d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang public void run() { 7729d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang if (!isAdded()) { 7739d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang LogUtils.i(LOG_TAG, "Unable to run op='%s' b/c fragment is not attached: %s", 7749d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang mOpName, AbstractConversationViewFragment.this); 7759d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang return; 7769d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 7779d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang go(); 7789d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 7799d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 7809d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang } 7819d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang 782f4fce1227d8b49f45e6569f1590565f2df9e8d6emindyp} 783