1b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal/* 2b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal * Copyright (C) 2012 Google Inc. 3b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal * Licensed to The Android Open Source Project. 4b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal * 5b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal * Licensed under the Apache License, Version 2.0 (the "License"); 6b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal * you may not use this file except in compliance with the License. 7b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal * You may obtain a copy of the License at 8b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal * 9b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal * http://www.apache.org/licenses/LICENSE-2.0 10b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal * 11b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal * Unless required by applicable law or agreed to in writing, software 12b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal * distributed under the License is distributed on an "AS IS" BASIS, 13b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal * See the License for the specific language governing permissions and 15b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal * limitations under the License. 16b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal */ 17b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal 18b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwalpackage com.android.mail.ui; 19b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal 205cf73b8acfce82fdc8185313b939d0c35271be3cJin Caoimport android.animation.LayoutTransition; 21b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwalimport android.app.Activity; 225b317924c7c764954870501d99bfc57a243f6319Jin Caoimport android.app.Fragment; 2337a20ca39be842b4b4cb54d5b5ed418d49d950cfVikram Aggarwalimport android.app.LoaderManager; 24b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwalimport android.content.res.Resources; 256d8e7fe77c3a3ecfac7bb3bdf0f87f7f89f66b77Mindy Pereiraimport android.database.DataSetObserver; 26b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwalimport android.os.Bundle; 27b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwalimport android.os.Handler; 28f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedyimport android.os.Parcelable; 295b317924c7c764954870501d99bfc57a243f6319Jin Caoimport android.support.annotation.IdRes; 304bb435caccc8ac148ca925813da601d9368d5066Mindy Pereiraimport android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener; 31d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Caoimport android.view.KeyEvent; 32b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwalimport android.view.LayoutInflater; 33b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwalimport android.view.View; 34b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwalimport android.view.ViewGroup; 35b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwalimport android.widget.AdapterView; 36b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwalimport android.widget.AdapterView.OnItemLongClickListener; 37b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwalimport android.widget.ListView; 38b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwalimport android.widget.TextView; 39b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal 40b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwalimport com.android.mail.ConversationListContext; 41f362695a339276ea1950c274a5bb8516250552e9Marc Blankimport com.android.mail.R; 42761522c1ac7a8fa05acde714f0e26fde47f3415fAndy Huangimport com.android.mail.analytics.Analytics; 4372953f25e00ee8e7b5c7682148430dc82f00a77dJin Caoimport com.android.mail.analytics.AnalyticsTimer; 4480aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwalimport com.android.mail.browse.ConversationCursor; 45d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwalimport com.android.mail.browse.ConversationItemView; 4612fe37aa24c313fd8192dede0770dadf0ec23359Mindy Pereiraimport com.android.mail.browse.ConversationItemViewModel; 476681f6b78557850d9ec76a7a8f9ac1ec930d2513Mindy Pereiraimport com.android.mail.browse.ConversationListFooterView; 48642731419abb9037a729114d32d6ddd15af0a406Alice Yangimport com.android.mail.browse.ToggleableItem; 4980aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwalimport com.android.mail.providers.Account; 507c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwalimport com.android.mail.providers.AccountObserver; 51d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwalimport com.android.mail.providers.Conversation; 524f166dead6323b6fd21c76cef66174c6cd4bf11cMindy Pereiraimport com.android.mail.providers.Folder; 5350ff0e50f1a27144e55e4184f48e433439727e7bVikram Aggarwalimport com.android.mail.providers.FolderObserver; 547d81600cbce3cfd366cbff9ecd1b7317ff957221Vikram Aggarwalimport com.android.mail.providers.Settings; 5580aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwalimport com.android.mail.providers.UIProvider; 564e812f49c5bbba50a60d062d624403952f8d5487Mindy Pereiraimport com.android.mail.providers.UIProvider.AccountCapabilities; 575dce146160e82e2087174e5971365a8ac0183b52Scott Kennedyimport com.android.mail.providers.UIProvider.ConversationListIcon; 584e812f49c5bbba50a60d062d624403952f8d5487Mindy Pereiraimport com.android.mail.providers.UIProvider.FolderCapabilities; 59e58222b9421756e11eb24799c1785a090d0793adMindy Pereiraimport com.android.mail.providers.UIProvider.Swipe; 609365a826b46b0e274df88e92534f7d871eef2aa2mindypimport com.android.mail.ui.SwipeableListView.ListItemSwipedListener; 619365a826b46b0e274df88e92534f7d871eef2aa2mindypimport com.android.mail.ui.SwipeableListView.ListItemsRemovedListener; 627c411aafd9ef059f881b7532426e61d1def54a07Andrew Sappersteinimport com.android.mail.ui.SwipeableListView.SwipeListener; 63b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwalimport com.android.mail.ui.ViewMode.ModeChangeListener; 64b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrookimport com.android.mail.utils.LogTag; 65b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwalimport com.android.mail.utils.LogUtils; 66fa131a2ff399fd1d544f759b063268fb4e8a3e70Vikram Aggarwalimport com.android.mail.utils.Utils; 676c570db71b8adc43d7dc2f31e6fa63cf50c1426eAndrew Sappersteinimport com.google.common.collect.ImmutableList; 68b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal 69f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereiraimport java.util.Collection; 707c8325de829ca029ce4547e4f0fa266124301367Scott Kennedyimport java.util.List; 71fe06bea4de68a072b0e5a0fc7cefcdfa96143a8fMindy Pereira 72d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Caoimport static android.view.View.OnKeyListener; 73d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao 74b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal/** 75b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal * The conversation list UI component. 76b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal */ 775b317924c7c764954870501d99bfc57a243f6319Jin Caopublic final class ConversationListFragment extends Fragment implements 787c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein OnItemLongClickListener, ModeChangeListener, ListItemSwipedListener, OnRefreshListener, 795b317924c7c764954870501d99bfc57a243f6319Jin Cao SwipeListener, OnKeyListener, AdapterView.OnItemClickListener { 80649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal /** Key used to pass data to {@link ConversationListFragment}. */ 81b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal private static final String CONVERSATION_LIST_KEY = "conversation-list"; 82649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal /** Key used to keep track of the scroll state of the list. */ 83b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal private static final String LIST_STATE_KEY = "list-state"; 84649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal 85b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrook private static final String LOG_TAG = LogTag.getLogTag(); 86a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal /** Key used to save the ListView choice mode, since ListView doesn't save it automatically! */ 87a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal private static final String CHOICE_MODE_KEY = "choice-mode-key"; 88b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal 89d7a12cd5b5c2639b653d8c671b04844ef02ee01dVikram Aggarwal // True if we are on a tablet device 90d7a12cd5b5c2639b653d8c671b04844ef02ee01dVikram Aggarwal private static boolean mTabletDevice; 91d7a12cd5b5c2639b653d8c671b04844ef02ee01dVikram Aggarwal 92aaca613cd34f6169e215016195f1953a69df913aMartin Hibdon // Delay before displaying the loading view. 930ef5cb2852616e82be9a0f23b431d4983f4513a8Andy Huang private static int LOADING_DELAY_MS; 944cdc4857b1b42659158b585648a58c3260a9536fMartin Hibdon // Minimum amount of time to keep the loading view displayed. 950ef5cb2852616e82be9a0f23b431d4983f4513a8Andy Huang private static int MINIMUM_LOADING_DURATION; 96aaca613cd34f6169e215016195f1953a69df913aMartin Hibdon 97b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal /** 989365a826b46b0e274df88e92534f7d871eef2aa2mindyp * Frequency of update of timestamps. Initialized in 999365a826b46b0e274df88e92534f7d871eef2aa2mindyp * {@link #onCreate(Bundle)} and final afterwards. 100b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal */ 101b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal private static int TIMESTAMP_UPDATE_INTERVAL = 0; 102b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal 10380aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwal private ControllableActivity mActivity; 10480aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwal 105b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal // Control state. 106b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal private ConversationListCallbacks mCallbacks; 10780aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwal 108b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal private final Handler mHandler = new Handler(); 10980aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwal 110b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal // The internal view objects. 111f6a6b50c6b45f36b9317e1688f8b87310a67b037Mindy Pereira private SwipeableListView mListView; 112b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal 113af9dc29f589c6ee771db22e3a2923b8bae46aafcJin Cao private View mSearchHeaderView; 114b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal private TextView mSearchResultCountTextView; 11580aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwal 11680aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwal /** 11780aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwal * Current Account being viewed 11880aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwal */ 119d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal private Account mAccount; 12080aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwal /** 12130fd47bf1947da5ad813cb957b6cbe569dce563aMindy Pereira * Current folder being viewed. 12280aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwal */ 1234f166dead6323b6fd21c76cef66174c6cd4bf11cMindy Pereira private Folder mFolder; 12480aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwal 125b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal /** 126b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal * A simple method to update the timestamps of conversations periodically. 127b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal */ 128b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal private Runnable mUpdateTimestampsRunnable = null; 129b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal 130b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal private ConversationListContext mViewContext; 131b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal 13280aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwal private AnimatedAdapter mListAdapter; 1337d602884ed70d07a42f1adf999fdf51b258f23f9Vikram Aggarwal 1346681f6b78557850d9ec76a7a8f9ac1ec930d2513Mindy Pereira private ConversationListFooterView mFooterView; 135a44b0eda75782a95cbf16e8d14e8080325028657Andrew Sapperstein private ConversationListEmptyView mEmptyView; 136aaca613cd34f6169e215016195f1953a69df913aMartin Hibdon private View mLoadingView; 137c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein private ErrorListener mErrorListener; 13850ff0e50f1a27144e55e4184f48e433439727e7bVikram Aggarwal private FolderObserver mFolderObserver; 139a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal private DataSetObserver mConversationCursorObserver; 140d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal 1414f1cc0ba5e7bb4083ca9542d6fcb58aae9ba7611Vikram Aggarwal private ConversationSelectionSet mSelectedSet; 1427c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal private final AccountObserver mAccountObserver = new AccountObserver() { 1437c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal @Override 1447c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal public void onChanged(Account newAccount) { 1457c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal mAccount = newAccount; 1467c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal setSwipeAction(); 1477c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal } 1487c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal }; 1499365a826b46b0e274df88e92534f7d871eef2aa2mindyp private ConversationUpdater mUpdater; 15081a4f08b1657bb297cd977e3df92660fab05a1abVikram Aggarwal /** Hash of the Conversation Cursor we last obtained from the controller. */ 15181a4f08b1657bb297cd977e3df92660fab05a1abVikram Aggarwal private int mConversationCursorHash; 152343cf45d9913daed981da891538934995d0dd83fJin Cao // The number of items in the last known ConversationCursor 153343cf45d9913daed981da891538934995d0dd83fJin Cao private int mConversationCursorLastCount; 15452da8ba2b62cd3479be1ed9d447cb3b64d388395Jin Cao // State variable to keep track if we just loaded a new list, used for analytics only 1558bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao // True if NO DATA has returned, false if we either partially or fully loaded the data 156faa9198087c44f2c46af40c8030b26d69617215dJin Cao private boolean mInitialCursorLoading; 1574f1cc0ba5e7bb4083ca9542d6fcb58aae9ba7611Vikram Aggarwal 1585b317924c7c764954870501d99bfc57a243f6319Jin Cao private @IdRes int mNextFocusLeftId; 1590b69338a45faa422ccba8faf64c9816c55d33e4aJin Cao // Tracks if a onKey event was initiated from the listview (received ACTION_DOWN before 1600b69338a45faa422ccba8faf64c9816c55d33e4aJin Cao // ACTION_UP). If not, the listview only receives ACTION_UP. 1610b69338a45faa422ccba8faf64c9816c55d33e4aJin Cao private boolean mKeyInitiatedFromList; 1625b317924c7c764954870501d99bfc57a243f6319Jin Cao 1631fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy /** Duration, in milliseconds, of the CAB mode (peek icon) animation. */ 1641fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy private static long sSelectionModeAnimationDuration = -1; 1651fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy 166856e1313e7ee92319f78ad4fdca18584235c612bJin Cao // Let's ensure that we are only showing one out of the three views at once 167856e1313e7ee92319f78ad4fdca18584235c612bJin Cao private void showListView() { 168856e1313e7ee92319f78ad4fdca18584235c612bJin Cao mListView.setVisibility(View.VISIBLE); 16990c61672da50a00663c54fa2effbbe5febd041c9Jin Cao mEmptyView.setVisibility(View.INVISIBLE); 17090c61672da50a00663c54fa2effbbe5febd041c9Jin Cao mLoadingView.setVisibility(View.INVISIBLE); 171856e1313e7ee92319f78ad4fdca18584235c612bJin Cao } 172856e1313e7ee92319f78ad4fdca18584235c612bJin Cao 173856e1313e7ee92319f78ad4fdca18584235c612bJin Cao private void showEmptyView() { 1743f386e86d1f211054f9b428b63e8cd8216d3e51bJin Cao mEmptyView.setupEmptyView( 1753f386e86d1f211054f9b428b63e8cd8216d3e51bJin Cao mFolder, mViewContext.searchQuery, mListAdapter.getBidiFormatter()); 17690c61672da50a00663c54fa2effbbe5febd041c9Jin Cao mListView.setVisibility(View.INVISIBLE); 177856e1313e7ee92319f78ad4fdca18584235c612bJin Cao mEmptyView.setVisibility(View.VISIBLE); 17890c61672da50a00663c54fa2effbbe5febd041c9Jin Cao mLoadingView.setVisibility(View.INVISIBLE); 179856e1313e7ee92319f78ad4fdca18584235c612bJin Cao } 180856e1313e7ee92319f78ad4fdca18584235c612bJin Cao 181856e1313e7ee92319f78ad4fdca18584235c612bJin Cao private void showLoadingView() { 18290c61672da50a00663c54fa2effbbe5febd041c9Jin Cao mListView.setVisibility(View.INVISIBLE); 18390c61672da50a00663c54fa2effbbe5febd041c9Jin Cao mEmptyView.setVisibility(View.INVISIBLE); 184856e1313e7ee92319f78ad4fdca18584235c612bJin Cao mLoadingView.setVisibility(View.VISIBLE); 185856e1313e7ee92319f78ad4fdca18584235c612bJin Cao } 186856e1313e7ee92319f78ad4fdca18584235c612bJin Cao 1874cdc4857b1b42659158b585648a58c3260a9536fMartin Hibdon private final Runnable mLoadingViewRunnable = new FragmentRunnable("LoadingRunnable", this) { 188aaca613cd34f6169e215016195f1953a69df913aMartin Hibdon @Override 189aaca613cd34f6169e215016195f1953a69df913aMartin Hibdon public void go() { 19052da8ba2b62cd3479be1ed9d447cb3b64d388395Jin Cao if (!isCursorReadyToShow()) { 1914cdc4857b1b42659158b585648a58c3260a9536fMartin Hibdon mCanTakeDownLoadingView = false; 19290c61672da50a00663c54fa2effbbe5febd041c9Jin Cao showLoadingView(); 1934cdc4857b1b42659158b585648a58c3260a9536fMartin Hibdon mHandler.removeCallbacks(mHideLoadingRunnable); 1944cdc4857b1b42659158b585648a58c3260a9536fMartin Hibdon mHandler.postDelayed(mHideLoadingRunnable, MINIMUM_LOADING_DURATION); 195aaca613cd34f6169e215016195f1953a69df913aMartin Hibdon } 196aaca613cd34f6169e215016195f1953a69df913aMartin Hibdon mLoadingViewPending = false; 197aaca613cd34f6169e215016195f1953a69df913aMartin Hibdon } 198aaca613cd34f6169e215016195f1953a69df913aMartin Hibdon }; 199aaca613cd34f6169e215016195f1953a69df913aMartin Hibdon 2004cdc4857b1b42659158b585648a58c3260a9536fMartin Hibdon private final Runnable mHideLoadingRunnable = new FragmentRunnable("CancelLoading", this) { 2014cdc4857b1b42659158b585648a58c3260a9536fMartin Hibdon @Override 2024cdc4857b1b42659158b585648a58c3260a9536fMartin Hibdon public void go() { 2034cdc4857b1b42659158b585648a58c3260a9536fMartin Hibdon mCanTakeDownLoadingView = true; 20452da8ba2b62cd3479be1ed9d447cb3b64d388395Jin Cao if (isCursorReadyToShow()) { 2054cdc4857b1b42659158b585648a58c3260a9536fMartin Hibdon hideLoadingViewAndShowContents(); 2064cdc4857b1b42659158b585648a58c3260a9536fMartin Hibdon } 2074cdc4857b1b42659158b585648a58c3260a9536fMartin Hibdon } 2084cdc4857b1b42659158b585648a58c3260a9536fMartin Hibdon }; 2094cdc4857b1b42659158b585648a58c3260a9536fMartin Hibdon 2107f4170af9fdfccf9a289ccfbb6221977bfb14cf4Jin Cao // Keep track of if we are waiting for the loading view. This variable is also used to check 2117f4170af9fdfccf9a289ccfbb6221977bfb14cf4Jin Cao // if the cursor corresponding to the current folder loaded (either partially or completely). 212aaca613cd34f6169e215016195f1953a69df913aMartin Hibdon private boolean mLoadingViewPending; 2134cdc4857b1b42659158b585648a58c3260a9536fMartin Hibdon private boolean mCanTakeDownLoadingView; 214aaca613cd34f6169e215016195f1953a69df913aMartin Hibdon 2156c51158ad3269f157424e6c7bd488425c98da08fVikram Aggarwal /** 216f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy * If <code>true</code>, we have restored (or attempted to restore) the list's scroll position 217f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy * from when we were last on this conversation list. 218f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy */ 219f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy private boolean mScrollPositionRestored = false; 220b6910bdf08ce931a1df5d392272014e32a2cf8b6Andrew Sapperstein private MailSwipeRefreshLayout mSwipeRefreshWidget; 221f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy 222f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy /** 2239365a826b46b0e274df88e92534f7d871eef2aa2mindyp * Constructor needs to be public to handle orientation changes and activity 2249365a826b46b0e274df88e92534f7d871eef2aa2mindyp * lifecycle events. 2256c51158ad3269f157424e6c7bd488425c98da08fVikram Aggarwal */ 226efc9f120f7f4d9b45c3bf7cb355740956fa5c7c8Paul Westbrook public ConversationListFragment() { 227d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal super(); 228d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal } 229d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal 2307c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein @Override 2317c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein public void onBeginSwipe() { 2327c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein mSwipeRefreshWidget.setEnabled(false); 2337c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein } 2347c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein 2357c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein @Override 2367c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein public void onEndSwipe() { 2377c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein mSwipeRefreshWidget.setEnabled(true); 2387c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein } 2397c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein 240a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal private class ConversationCursorObserver extends DataSetObserver { 24170a70c92bca006af84177d025c32deee3aa6da3fMindy Pereira @Override 24270a70c92bca006af84177d025c32deee3aa6da3fMindy Pereira public void onChanged() { 24370a70c92bca006af84177d025c32deee3aa6da3fMindy Pereira onConversationListStatusUpdated(); 24470a70c92bca006af84177d025c32deee3aa6da3fMindy Pereira } 24570a70c92bca006af84177d025c32deee3aa6da3fMindy Pereira } 24670a70c92bca006af84177d025c32deee3aa6da3fMindy Pereira 2477d602884ed70d07a42f1adf999fdf51b258f23f9Vikram Aggarwal /** 2489365a826b46b0e274df88e92534f7d871eef2aa2mindyp * Creates a new instance of {@link ConversationListFragment}, initialized 2499365a826b46b0e274df88e92534f7d871eef2aa2mindyp * to display conversation list context. 250b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal */ 251b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal public static ConversationListFragment newInstance(ConversationListContext viewContext) { 252e2be97a58fbae98b95e51735c6468d121e465355Paul Westbrook final ConversationListFragment fragment = new ConversationListFragment(); 253e2be97a58fbae98b95e51735c6468d121e465355Paul Westbrook final Bundle args = new Bundle(1); 254b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal args.putBundle(CONVERSATION_LIST_KEY, viewContext.toBundle()); 255b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal fragment.setArguments(args); 256b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal return fragment; 257b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal } 258b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal 259b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal /** 2609365a826b46b0e274df88e92534f7d871eef2aa2mindyp * Show the header if the current conversation list is showing search 2619365a826b46b0e274df88e92534f7d871eef2aa2mindyp * results. 262b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal */ 26368f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira private void updateSearchResultHeader(int count) { 264af9dc29f589c6ee771db22e3a2923b8bae46aafcJin Cao if (mActivity == null || mSearchHeaderView == null) { 26571a8f297b1d43b9dd0c3b39af820e8a84ab4185fMindy Pereira return; 26671a8f297b1d43b9dd0c3b39af820e8a84ab4185fMindy Pereira } 267af9dc29f589c6ee771db22e3a2923b8bae46aafcJin Cao mSearchResultCountTextView.setText( 268af9dc29f589c6ee771db22e3a2923b8bae46aafcJin Cao getResources().getString(R.string.search_results_loaded, count)); 26980aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwal } 27080aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwal 271b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal @Override 2720509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal public void onActivityCreated(Bundle savedState) { 2730509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal super.onActivityCreated(savedState); 274aaca613cd34f6169e215016195f1953a69df913aMartin Hibdon mLoadingViewPending = false; 2754cdc4857b1b42659158b585648a58c3260a9536fMartin Hibdon mCanTakeDownLoadingView = true; 2761fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy if (sSelectionModeAnimationDuration < 0) { 2771fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy sSelectionModeAnimationDuration = getResources().getInteger( 2781fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy R.integer.conv_item_view_cab_anim_duration); 2791fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy } 2801fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy 2819365a826b46b0e274df88e92534f7d871eef2aa2mindyp // Strictly speaking, we get back an android.app.Activity from 2829365a826b46b0e274df88e92534f7d871eef2aa2mindyp // getActivity. However, the 2839365a826b46b0e274df88e92534f7d871eef2aa2mindyp // only activity creating a ConversationListContext is a MailActivity 2849365a826b46b0e274df88e92534f7d871eef2aa2mindyp // which is of type 2859365a826b46b0e274df88e92534f7d871eef2aa2mindyp // ControllableActivity, so this cast should be safe. If this cast 2869365a826b46b0e274df88e92534f7d871eef2aa2mindyp // fails, some other 2879365a826b46b0e274df88e92534f7d871eef2aa2mindyp // activity is creating ConversationListFragments. This activity must be 2889365a826b46b0e274df88e92534f7d871eef2aa2mindyp // of type 28980aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwal // ControllableActivity. 29080aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwal final Activity activity = getActivity(); 2919365a826b46b0e274df88e92534f7d871eef2aa2mindyp if (!(activity instanceof ControllableActivity)) { 2929365a826b46b0e274df88e92534f7d871eef2aa2mindyp LogUtils.e(LOG_TAG, "ConversationListFragment expects only a ControllableActivity to" 2939365a826b46b0e274df88e92534f7d871eef2aa2mindyp + "create it. Cannot proceed."); 29480aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwal } 29580aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwal mActivity = (ControllableActivity) activity; 2969365a826b46b0e274df88e92534f7d871eef2aa2mindyp // Since we now have a controllable activity, load the account from it, 2979365a826b46b0e274df88e92534f7d871eef2aa2mindyp // and register for 2987c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal // future account changes. 2997c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal mAccount = mAccountObserver.initialize(mActivity.getAccountController()); 3007d602884ed70d07a42f1adf999fdf51b258f23f9Vikram Aggarwal mCallbacks = mActivity.getListHandler(); 301c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein mErrorListener = mActivity.getErrorListener(); 302278fd226160fa486db17cc7e55deebe5d7a2ae0fMindy Pereira // Start off with the current state of the folder being viewed. 303af9dc29f589c6ee771db22e3a2923b8bae46aafcJin Cao final LayoutInflater inflater = LayoutInflater.from(mActivity.getActivityContext()); 304af9dc29f589c6ee771db22e3a2923b8bae46aafcJin Cao mFooterView = (ConversationListFooterView) inflater.inflate( 305af9dc29f589c6ee771db22e3a2923b8bae46aafcJin Cao R.layout.conversation_list_footer_view, null); 3064969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook mFooterView.setClickListener(mActivity); 30781a4f08b1657bb297cd977e3df92660fab05a1abVikram Aggarwal final ConversationCursor conversationCursor = getConversationListCursor(); 30837a20ca39be842b4b4cb54d5b5ed418d49d950cfVikram Aggarwal final LoaderManager manager = getLoaderManager(); 30937a20ca39be842b4b4cb54d5b5ed418d49d950cfVikram Aggarwal 310c5567737cb40ca156ea89f169a23008524ebd1a4Alice Yang // TODO: These special views are always created, doesn't matter whether they will 311c5567737cb40ca156ea89f169a23008524ebd1a4Alice Yang // be shown or not, as we add more views this will get more expensive. Given these are 312c5567737cb40ca156ea89f169a23008524ebd1a4Alice Yang // tips that are only shown once to the user, we should consider creating these on demand. 3137c8325de829ca029ce4547e4f0fa266124301367Scott Kennedy final ConversationListHelper helper = mActivity.getConversationListHelper(); 3142102b2c87a6cb39bc63fb7eff13dda64da56b72eMark Wei final List<ConversationSpecialItemView> specialItemViews = helper != null ? 3152102b2c87a6cb39bc63fb7eff13dda64da56b72eMark Wei ImmutableList.copyOf(helper.makeConversationListSpecialViews( 316103319aaed26bce257de55b2fe93d4f78d3c59b9Scott Kennedy activity, mActivity, mAccount)) 3172102b2c87a6cb39bc63fb7eff13dda64da56b72eMark Wei : null; 3187c8325de829ca029ce4547e4f0fa266124301367Scott Kennedy if (specialItemViews != null) { 3197c8325de829ca029ce4547e4f0fa266124301367Scott Kennedy // Attach to the LoaderManager 3207c8325de829ca029ce4547e4f0fa266124301367Scott Kennedy for (final ConversationSpecialItemView view : specialItemViews) { 32132ddb84f2d4f53bfc4b53656461068c0c69d6325Scott Kennedy view.bindFragment(manager, savedState); 3227c8325de829ca029ce4547e4f0fa266124301367Scott Kennedy } 3237c8325de829ca029ce4547e4f0fa266124301367Scott Kennedy } 3247c8325de829ca029ce4547e4f0fa266124301367Scott Kennedy 3257c8325de829ca029ce4547e4f0fa266124301367Scott Kennedy mListAdapter = new AnimatedAdapter(mActivity.getApplicationContext(), conversationCursor, 3266af70772b2037d3527337f220fc053af73d4c81dScott Kennedy mActivity.getSelectedSet(), mActivity, mListView, specialItemViews); 3276681f6b78557850d9ec76a7a8f9ac1ec930d2513Mindy Pereira mListAdapter.addFooter(mFooterView); 328eb145d45c17b08ce09131e84b96350925b3db81aJin Cao // Show search result header only if we are in search mode 329eb145d45c17b08ce09131e84b96350925b3db81aJin Cao final boolean showSearchHeader = ConversationListContext.isSearchResult(mViewContext); 330eb145d45c17b08ce09131e84b96350925b3db81aJin Cao if (showSearchHeader) { 331eb145d45c17b08ce09131e84b96350925b3db81aJin Cao mSearchHeaderView = inflater.inflate(R.layout.search_results_view, null); 332eb145d45c17b08ce09131e84b96350925b3db81aJin Cao mSearchResultCountTextView = (TextView) 333eb145d45c17b08ce09131e84b96350925b3db81aJin Cao mSearchHeaderView.findViewById(R.id.search_result_count_view); 334eb145d45c17b08ce09131e84b96350925b3db81aJin Cao mListAdapter.addHeader(mSearchHeaderView); 335eb145d45c17b08ce09131e84b96350925b3db81aJin Cao } 336eb145d45c17b08ce09131e84b96350925b3db81aJin Cao 3376f4a6af149e1f0ee71db356205b810becbc5de2cMindy Pereira mListView.setAdapter(mListAdapter); 3384f1cc0ba5e7bb4083ca9542d6fcb58aae9ba7611Vikram Aggarwal mSelectedSet = mActivity.getSelectedSet(); 3394f1cc0ba5e7bb4083ca9542d6fcb58aae9ba7611Vikram Aggarwal mListView.setSelectionSet(mSelectedSet); 34037a20ca39be842b4b4cb54d5b5ed418d49d950cfVikram Aggarwal mListAdapter.setFooterVisibility(false); 34150ff0e50f1a27144e55e4184f48e433439727e7bVikram Aggarwal mFolderObserver = new FolderObserver(){ 34250ff0e50f1a27144e55e4184f48e433439727e7bVikram Aggarwal @Override 34350ff0e50f1a27144e55e4184f48e433439727e7bVikram Aggarwal public void onChanged(Folder newFolder) { 34450ff0e50f1a27144e55e4184f48e433439727e7bVikram Aggarwal onFolderUpdated(newFolder); 34550ff0e50f1a27144e55e4184f48e433439727e7bVikram Aggarwal } 34650ff0e50f1a27144e55e4184f48e433439727e7bVikram Aggarwal }; 34750ff0e50f1a27144e55e4184f48e433439727e7bVikram Aggarwal mFolderObserver.initialize(mActivity.getFolderController()); 348a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal mConversationCursorObserver = new ConversationCursorObserver(); 3499365a826b46b0e274df88e92534f7d871eef2aa2mindyp mUpdater = mActivity.getConversationUpdater(); 350a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal mUpdater.registerConversationListObserver(mConversationCursorObserver); 351bcb16b98140a83a4db3c51568d21c839595f73dfVikram Aggarwal mTabletDevice = Utils.useTabletUI(mActivity.getApplicationContext().getResources()); 3529365a826b46b0e274df88e92534f7d871eef2aa2mindyp // The onViewModeChanged callback doesn't get called when the mode 3539365a826b46b0e274df88e92534f7d871eef2aa2mindyp // object is created, so 354fa131a2ff399fd1d544f759b063268fb4e8a3e70Vikram Aggarwal // force setting the mode manually this time around. 355f96ec32ba61bac2a7635f6ef6e04a78cc63cfff8Mindy Pereira onViewModeChanged(mActivity.getViewMode().getMode()); 356b5901be36e247573e8162714e8e006c4dbc35d4aMindy Pereira mActivity.getViewMode().addListener(this); 3571bc9a559c8e02c45cb57f2dd8ac3cc871bd2961fPaul Westbrook 358b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal if (mActivity.isFinishing()) { 359b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal // Activity is finishing, just bail. 360b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal return; 361b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal } 36281a4f08b1657bb297cd977e3df92660fab05a1abVikram Aggarwal mConversationCursorHash = (conversationCursor == null) ? 0 : conversationCursor.hashCode(); 36381a4f08b1657bb297cd977e3df92660fab05a1abVikram Aggarwal // Belt and suspenders here; make sure we do any necessary sync of the 36481a4f08b1657bb297cd977e3df92660fab05a1abVikram Aggarwal // ConversationCursor 36581a4f08b1657bb297cd977e3df92660fab05a1abVikram Aggarwal if (conversationCursor != null && conversationCursor.isRefreshReady()) { 36681a4f08b1657bb297cd977e3df92660fab05a1abVikram Aggarwal conversationCursor.sync(); 36781a4f08b1657bb297cd977e3df92660fab05a1abVikram Aggarwal } 36881a4f08b1657bb297cd977e3df92660fab05a1abVikram Aggarwal 3690509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal // On a phone we never highlight a conversation, so the default is to select none. 3700509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal // On a tablet, we highlight a SINGLE conversation in landscape conversation view. 3710509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal int choice = getDefaultChoiceMode(mTabletDevice); 3720509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal if (savedState != null) { 3730509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal // Restore the choice mode if it was set earlier, or NONE if creating a fresh view. 3740509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal // Choice mode here represents the current conversation only. CAB mode does not rely on 3750509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal // the platform: checked state is a local variable {@link ConversationItemView#mChecked} 3760509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal choice = savedState.getInt(CHOICE_MODE_KEY, choice); 3770509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal if (savedState.containsKey(LIST_STATE_KEY)) { 3780509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal // TODO: find a better way to unset the selected item when restoring 3790509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal mListView.clearChoices(); 3800509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal } 3810509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal } 3820509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal setChoiceMode(choice); 3830509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal 384b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal // Show list and start loading list. 385b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal showList(); 3864765c5c90802d0abcf2b05c4967e7dd7d2284f9aMindy Pereira ToastBarOperation pendingOp = mActivity.getPendingToastOperation(); 3874765c5c90802d0abcf2b05c4967e7dd7d2284f9aMindy Pereira if (pendingOp != null) { 3884765c5c90802d0abcf2b05c4967e7dd7d2284f9aMindy Pereira // Clear the pending operation 3894765c5c90802d0abcf2b05c4967e7dd7d2284f9aMindy Pereira mActivity.setPendingToastOperation(null); 3904765c5c90802d0abcf2b05c4967e7dd7d2284f9aMindy Pereira mActivity.onUndoAvailable(pendingOp); 3914765c5c90802d0abcf2b05c4967e7dd7d2284f9aMindy Pereira } 392b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal } 393b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal 3940509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal /** 3950509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal * Returns the default choice mode for the list based on whether the list is displayed on tablet 3960509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal * or not. 3970509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal * @param isTablet 3980509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal * @return 3990509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal */ 4000509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal private final static int getDefaultChoiceMode(boolean isTablet) { 4010509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal return isTablet ? ListView.CHOICE_MODE_SINGLE : ListView.CHOICE_MODE_NONE; 4020509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal } 4030509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal 404967ede63e059810d9d4d425c50111d606014c893Mindy Pereira public AnimatedAdapter getAnimatedAdapter() { 405967ede63e059810d9d4d425c50111d606014c893Mindy Pereira return mListAdapter; 406967ede63e059810d9d4d425c50111d606014c893Mindy Pereira } 407967ede63e059810d9d4d425c50111d606014c893Mindy Pereira 408b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal @Override 409d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal public void onCreate(Bundle savedState) { 410d247dc966bca1ed303039c2893c54979d448a336Vikram Aggarwal super.onCreate(savedState); 411b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal 412b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal // Initialize fragment constants from resources 4136c51158ad3269f157424e6c7bd488425c98da08fVikram Aggarwal final Resources res = getResources(); 414b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal TIMESTAMP_UPDATE_INTERVAL = res.getInteger(R.integer.timestamp_update_interval); 4150ef5cb2852616e82be9a0f23b431d4983f4513a8Andy Huang LOADING_DELAY_MS = res.getInteger(R.integer.conversationview_show_loading_delay); 4160ef5cb2852616e82be9a0f23b431d4983f4513a8Andy Huang MINIMUM_LOADING_DURATION = res.getInteger(R.integer.conversationview_min_show_loading); 4179365a826b46b0e274df88e92534f7d871eef2aa2mindyp mUpdateTimestampsRunnable = new Runnable() { 418b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal @Override 419b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal public void run() { 420b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal mListView.invalidateViews(); 421b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal mHandler.postDelayed(mUpdateTimestampsRunnable, TIMESTAMP_UPDATE_INTERVAL); 422b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal } 423b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal }; 424b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal 42580aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwal // Get the context from the arguments 4266c51158ad3269f157424e6c7bd488425c98da08fVikram Aggarwal final Bundle args = getArguments(); 427b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal mViewContext = ConversationListContext.forBundle(args.getBundle(CONVERSATION_LIST_KEY)); 4283982e236fb2dd36d460df4dbf24e07e8ba55b3bdMindy Pereira mAccount = mViewContext.account; 42980ecd892733980aad7efb56e6266356a9da13fa8Paul Westbrook 4309a49c9bc441e6ac009cad72cbca523ce84751f65Vikram Aggarwal setRetainInstance(false); 431b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal } 432b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal 433b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal @Override 4349e4ca7993213d296043d20fe9cf4e166b5d595e8Andy Huang public String toString() { 4359e4ca7993213d296043d20fe9cf4e166b5d595e8Andy Huang final String s = super.toString(); 4369e4ca7993213d296043d20fe9cf4e166b5d595e8Andy Huang if (mViewContext == null) { 4379e4ca7993213d296043d20fe9cf4e166b5d595e8Andy Huang return s; 4389e4ca7993213d296043d20fe9cf4e166b5d595e8Andy Huang } 439c1922a93fb9540d4ff8c28a30d1ae58d3a3d73f9Andy Huang final StringBuilder sb = new StringBuilder(s); 440c1922a93fb9540d4ff8c28a30d1ae58d3a3d73f9Andy Huang sb.setLength(sb.length() - 1); 441c1922a93fb9540d4ff8c28a30d1ae58d3a3d73f9Andy Huang sb.append(" mListAdapter="); 442c1922a93fb9540d4ff8c28a30d1ae58d3a3d73f9Andy Huang sb.append(mListAdapter); 443c1922a93fb9540d4ff8c28a30d1ae58d3a3d73f9Andy Huang sb.append(" folder="); 444c1922a93fb9540d4ff8c28a30d1ae58d3a3d73f9Andy Huang sb.append(mViewContext.folder); 445c1922a93fb9540d4ff8c28a30d1ae58d3a3d73f9Andy Huang sb.append("}"); 446c1922a93fb9540d4ff8c28a30d1ae58d3a3d73f9Andy Huang return sb.toString(); 4479e4ca7993213d296043d20fe9cf4e166b5d595e8Andy Huang } 4489e4ca7993213d296043d20fe9cf4e166b5d595e8Andy Huang 4499e4ca7993213d296043d20fe9cf4e166b5d595e8Andy Huang @Override 4509a49c9bc441e6ac009cad72cbca523ce84751f65Vikram Aggarwal public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) { 451b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal View rootView = inflater.inflate(R.layout.conversation_list, null); 452a44b0eda75782a95cbf16e8d14e8080325028657Andrew Sapperstein mEmptyView = (ConversationListEmptyView) rootView.findViewById(R.id.empty_view); 4530ef5cb2852616e82be9a0f23b431d4983f4513a8Andy Huang mLoadingView = rootView.findViewById(R.id.background_view); 454c96efcc4a2f65b11a9b2057320ca828ee80f32ffAndy Huang mLoadingView.setVisibility(View.GONE); 4550ef5cb2852616e82be9a0f23b431d4983f4513a8Andy Huang mLoadingView.findViewById(R.id.loading_progress).setVisibility(View.VISIBLE); 4565b317924c7c764954870501d99bfc57a243f6319Jin Cao mListView = (SwipeableListView) rootView.findViewById(R.id.conversation_list_view); 457b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal mListView.setHeaderDividersEnabled(false); 458b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal mListView.setOnItemLongClickListener(this); 4594e812f49c5bbba50a60d062d624403952f8d5487Mindy Pereira mListView.enableSwipe(mAccount.supportsCapability(AccountCapabilities.UNDO)); 4607c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein mListView.setListItemSwipedListener(this); 4617c411aafd9ef059f881b7532426e61d1def54a07Andrew Sapperstein mListView.setSwipeListener(this); 462d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao mListView.setOnKeyListener(this); 4635b317924c7c764954870501d99bfc57a243f6319Jin Cao mListView.setOnItemClickListener(this); 4645b317924c7c764954870501d99bfc57a243f6319Jin Cao if (mNextFocusLeftId != 0) { 4655b317924c7c764954870501d99bfc57a243f6319Jin Cao mListView.setNextFocusLeftId(mNextFocusLeftId); 4665b317924c7c764954870501d99bfc57a243f6319Jin Cao } 4672af0cf3f538d035c80c7eedacf97362d9a943fa1Marc Blank 4685cf73b8acfce82fdc8185313b939d0c35271be3cJin Cao // enable animateOnLayout (equivalent of setLayoutTransition) only for >=JB (b/14302062) 4695cf73b8acfce82fdc8185313b939d0c35271be3cJin Cao if (Utils.isRunningJellybeanOrLater()) { 4705cf73b8acfce82fdc8185313b939d0c35271be3cJin Cao ((ViewGroup) rootView.findViewById(R.id.conversation_list_parent_frame)) 4715cf73b8acfce82fdc8185313b939d0c35271be3cJin Cao .setLayoutTransition(new LayoutTransition()); 4725cf73b8acfce82fdc8185313b939d0c35271be3cJin Cao } 4735cf73b8acfce82fdc8185313b939d0c35271be3cJin Cao 474856e1313e7ee92319f78ad4fdca18584235c612bJin Cao // By default let's show the list view 475856e1313e7ee92319f78ad4fdca18584235c612bJin Cao showListView(); 476856e1313e7ee92319f78ad4fdca18584235c612bJin Cao 4770509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal if (savedState != null && savedState.containsKey(LIST_STATE_KEY)) { 4780509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal mListView.onRestoreInstanceState(savedState.getParcelable(LIST_STATE_KEY)); 4799a49c9bc441e6ac009cad72cbca523ce84751f65Vikram Aggarwal } 480b6910bdf08ce931a1df5d392272014e32a2cf8b6Andrew Sapperstein mSwipeRefreshWidget = 481b6910bdf08ce931a1df5d392272014e32a2cf8b6Andrew Sapperstein (MailSwipeRefreshLayout) rootView.findViewById(R.id.swipe_refresh_widget); 4824bb435caccc8ac148ca925813da601d9368d5066Mindy Pereira mSwipeRefreshWidget.setColorScheme(R.color.swipe_refresh_color1, 4834bb435caccc8ac148ca925813da601d9368d5066Mindy Pereira R.color.swipe_refresh_color2, 4844bb435caccc8ac148ca925813da601d9368d5066Mindy Pereira R.color.swipe_refresh_color3, R.color.swipe_refresh_color4); 4854bb435caccc8ac148ca925813da601d9368d5066Mindy Pereira mSwipeRefreshWidget.setOnRefreshListener(this); 486b6910bdf08ce931a1df5d392272014e32a2cf8b6Andrew Sapperstein mSwipeRefreshWidget.setScrollableChild(mListView); 4870d74a662f6e96b5a21e37978eb6e3b46545e3cbfAlice Yang 488b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal return rootView; 489b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal } 490b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal 491a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal /** 492a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal * Sets the choice mode of the list view 493a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal */ 494a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal private final void setChoiceMode(int choiceMode) { 495a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal mListView.setChoiceMode(choiceMode); 496a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal } 497a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal 498a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal /** 499a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal * Tell the list to select nothing. 500a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal */ 501a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal public final void setChoiceNone() { 5020509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal // On a phone, the default choice mode is already none, so nothing to do. 5030509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal if (!mTabletDevice) { 5040509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal return; 5050509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal } 506b0be3fcbc7b9f99cc6fc0a01e517b0f83e67c1f0Andy Huang clearChoicesAndActivated(); 507a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal setChoiceMode(ListView.CHOICE_MODE_NONE); 508a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal } 509a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal 510a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal /** 511a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal * Tell the list to get out of selecting none. 512a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal */ 513a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal public final void revertChoiceMode() { 5140509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal // On a phone, the default choice mode is always none, so nothing to do. 5150509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal if (!mTabletDevice) { 5160509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal return; 5170509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal } 5180509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal setChoiceMode(getDefaultChoiceMode(mTabletDevice)); 519a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal } 520a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal 521b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal @Override 5227517e3b61b898a57f19be0671f70d58a82224643Andy Huang public void onDestroy() { 5237517e3b61b898a57f19be0671f70d58a82224643Andy Huang super.onDestroy(); 5247517e3b61b898a57f19be0671f70d58a82224643Andy Huang } 5257517e3b61b898a57f19be0671f70d58a82224643Andy Huang 5267517e3b61b898a57f19be0671f70d58a82224643Andy Huang @Override 527b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal public void onDestroyView() { 5287517e3b61b898a57f19be0671f70d58a82224643Andy Huang 5297c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal // Clear the list's adapter 5307c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal mListAdapter.destroy(); 531b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal mListView.setAdapter(null); 532b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal 5336c570db71b8adc43d7dc2f31e6fa63cf50c1426eAndrew Sapperstein mActivity.getViewMode().removeListener(this); 53471a8f297b1d43b9dd0c3b39af820e8a84ab4185fMindy Pereira if (mFolderObserver != null) { 53550ff0e50f1a27144e55e4184f48e433439727e7bVikram Aggarwal mFolderObserver.unregisterAndDestroy(); 53671a8f297b1d43b9dd0c3b39af820e8a84ab4185fMindy Pereira mFolderObserver = null; 53771a8f297b1d43b9dd0c3b39af820e8a84ab4185fMindy Pereira } 538a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal if (mConversationCursorObserver != null) { 539a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal mUpdater.unregisterConversationListObserver(mConversationCursorObserver); 540a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal mConversationCursorObserver = null; 54170a70c92bca006af84177d025c32deee3aa6da3fMindy Pereira } 5427c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal mAccountObserver.unregisterAndDestroy(); 5437c8325de829ca029ce4547e4f0fa266124301367Scott Kennedy getAnimatedAdapter().cleanup(); 544b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal super.onDestroyView(); 545b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal } 546b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal 5479730ea0550dacabcd38f0997c02d668058520042Vikram Aggarwal /** 5489365a826b46b0e274df88e92534f7d871eef2aa2mindyp * There are three binary variables, which determine what we do with a 5499365a826b46b0e274df88e92534f7d871eef2aa2mindyp * message. checkbEnabled: Whether check boxes are enabled or not (forced 5509365a826b46b0e274df88e92534f7d871eef2aa2mindyp * true on tablet) cabModeOn: Whether CAB mode is currently on or not. 5519365a826b46b0e274df88e92534f7d871eef2aa2mindyp * pressType: long or short tap (There is a third possibility: phone or 5529365a826b46b0e274df88e92534f7d871eef2aa2mindyp * tablet, but they have <em>identical</em> behavior) The matrix of 5539365a826b46b0e274df88e92534f7d871eef2aa2mindyp * possibilities is: 5549365a826b46b0e274df88e92534f7d871eef2aa2mindyp * <p> 5559365a826b46b0e274df88e92534f7d871eef2aa2mindyp * Long tap: Always toggle selection of conversation. If CAB mode is not 5569365a826b46b0e274df88e92534f7d871eef2aa2mindyp * started, then start it. 5579730ea0550dacabcd38f0997c02d668058520042Vikram Aggarwal * <pre> 5589730ea0550dacabcd38f0997c02d668058520042Vikram Aggarwal * | Checkboxes | No Checkboxes 5599730ea0550dacabcd38f0997c02d668058520042Vikram Aggarwal * ----------+------------+--------------- 5609730ea0550dacabcd38f0997c02d668058520042Vikram Aggarwal * CAB mode | Select | Select 5619730ea0550dacabcd38f0997c02d668058520042Vikram Aggarwal * List mode | Select | Select 5629730ea0550dacabcd38f0997c02d668058520042Vikram Aggarwal * 5639730ea0550dacabcd38f0997c02d668058520042Vikram Aggarwal * </pre> 5649365a826b46b0e274df88e92534f7d871eef2aa2mindyp * 5659730ea0550dacabcd38f0997c02d668058520042Vikram Aggarwal * Reference: http://b/issue?id=6392199 5669730ea0550dacabcd38f0997c02d668058520042Vikram Aggarwal * <p> 5679730ea0550dacabcd38f0997c02d668058520042Vikram Aggarwal * {@inheritDoc} 5689730ea0550dacabcd38f0997c02d668058520042Vikram Aggarwal */ 569b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal @Override 570b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { 5711e231a6ecd156a47fcb533ebd40d9dfe122cb32eMindy Pereira // Ignore anything that is not a conversation item. Could be a footer. 5721e231a6ecd156a47fcb533ebd40d9dfe122cb32eMindy Pereira if (!(view instanceof ConversationItemView)) { 5737c8325de829ca029ce4547e4f0fa266124301367Scott Kennedy return false; 5741e231a6ecd156a47fcb533ebd40d9dfe122cb32eMindy Pereira } 575955a766f6c9c30e06dcd504ebc4dd6db510c2fa6Scott Kennedy return ((ConversationItemView) view).toggleSelectedStateOrBeginDrag(); 576b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal } 577b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal 5784f1cc0ba5e7bb4083ca9542d6fcb58aae9ba7611Vikram Aggarwal /** 5799365a826b46b0e274df88e92534f7d871eef2aa2mindyp * See the comment for 5809365a826b46b0e274df88e92534f7d871eef2aa2mindyp * {@link #onItemLongClick(AdapterView, View, int, long)}. 5819365a826b46b0e274df88e92534f7d871eef2aa2mindyp * <p> 5829365a826b46b0e274df88e92534f7d871eef2aa2mindyp * Short tap behavior: 5839365a826b46b0e274df88e92534f7d871eef2aa2mindyp * 5844f1cc0ba5e7bb4083ca9542d6fcb58aae9ba7611Vikram Aggarwal * <pre> 5854f1cc0ba5e7bb4083ca9542d6fcb58aae9ba7611Vikram Aggarwal * | Checkboxes | No Checkboxes 5864f1cc0ba5e7bb4083ca9542d6fcb58aae9ba7611Vikram Aggarwal * ----------+------------+--------------- 5879ba7fba9213027c6f763001d32d4c88f0d086388Scott Kennedy * CAB mode | Peek | Select 5884f1cc0ba5e7bb4083ca9542d6fcb58aae9ba7611Vikram Aggarwal * List mode | Peek | Peek 5894f1cc0ba5e7bb4083ca9542d6fcb58aae9ba7611Vikram Aggarwal * </pre> 5909365a826b46b0e274df88e92534f7d871eef2aa2mindyp * 5914f1cc0ba5e7bb4083ca9542d6fcb58aae9ba7611Vikram Aggarwal * Reference: http://b/issue?id=6392199 5924f1cc0ba5e7bb4083ca9542d6fcb58aae9ba7611Vikram Aggarwal * <p> 5934f1cc0ba5e7bb4083ca9542d6fcb58aae9ba7611Vikram Aggarwal * {@inheritDoc} 5944f1cc0ba5e7bb4083ca9542d6fcb58aae9ba7611Vikram Aggarwal */ 595b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal @Override 5965b317924c7c764954870501d99bfc57a243f6319Jin Cao public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { 597d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao onListItemSelected(view, position); 598d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao } 599d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao 600d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao private void onListItemSelected(View view, int position) { 6010e8dc84326ad3e6d146a203538665f98dd98f688Scott Kennedy if (view instanceof ToggleableItem) { 6025dce146160e82e2087174e5971365a8ac0183b52Scott Kennedy final boolean showSenderImage = 6035dce146160e82e2087174e5971365a8ac0183b52Scott Kennedy (mAccount.settings.convListIcon == ConversationListIcon.SENDER_IMAGE); 604042a530b2296487fa5899a3e871214ac4a47e3d8Andy Huang final boolean inCabMode = !mSelectedSet.isEmpty(); 605042a530b2296487fa5899a3e871214ac4a47e3d8Andy Huang if (!showSenderImage && inCabMode) { 60637a20ca39be842b4b4cb54d5b5ed418d49d950cfVikram Aggarwal ((ToggleableItem) view).toggleSelectedState(); 60737a20ca39be842b4b4cb54d5b5ed418d49d950cfVikram Aggarwal } else { 608042a530b2296487fa5899a3e871214ac4a47e3d8Andy Huang if (inCabMode) { 609042a530b2296487fa5899a3e871214ac4a47e3d8Andy Huang // this is a peek. 610042a530b2296487fa5899a3e871214ac4a47e3d8Andy Huang Analytics.getInstance().sendEvent("peek", null, null, mSelectedSet.size()); 611042a530b2296487fa5899a3e871214ac4a47e3d8Andy Huang } 61272953f25e00ee8e7b5c7682148430dc82f00a77dJin Cao AnalyticsTimer.getInstance().trackStart(AnalyticsTimer.OPEN_CONV_VIEW_FROM_LIST); 6138afccad8b03701577bb4b734a0f4fe76f6cab487Scott Kennedy viewConversation(position); 61437a20ca39be842b4b4cb54d5b5ed418d49d950cfVikram Aggarwal } 6154f1cc0ba5e7bb4083ca9542d6fcb58aae9ba7611Vikram Aggarwal } else { 61637a20ca39be842b4b4cb54d5b5ed418d49d950cfVikram Aggarwal // Ignore anything that is not a conversation item. Could be a footer. 61737a20ca39be842b4b4cb54d5b5ed418d49d950cfVikram Aggarwal // If we are using a keyboard, the highlighted item is the parent; 61837a20ca39be842b4b4cb54d5b5ed418d49d950cfVikram Aggarwal // otherwise, this is a direct call from the ConverationItemView 61937a20ca39be842b4b4cb54d5b5ed418d49d950cfVikram Aggarwal return; 6204f1cc0ba5e7bb4083ca9542d6fcb58aae9ba7611Vikram Aggarwal } 6219365a826b46b0e274df88e92534f7d871eef2aa2mindyp // When a new list item is clicked, commit any existing leave behind 6228694fe9a90b0f8d6f6dc9b8d3a33c49366454f99mindyp // items. Wait until we have opened the desired conversation to cause 6238694fe9a90b0f8d6f6dc9b8d3a33c49366454f99mindyp // any position changes. 624bcb16b98140a83a4db3c51568d21c839595f73dfVikram Aggarwal commitDestructiveActions(Utils.useTabletUI(mActivity.getActivityContext().getResources())); 625b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal } 626b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal 627b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal @Override 628d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao public boolean onKey(View view, int keyCode, KeyEvent keyEvent) { 629d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao SwipeableListView list = (SwipeableListView) view; 630d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao // Don't need to handle ENTER because it's auto-handled as a "click". 631d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { 632d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao if (keyEvent.getAction() == KeyEvent.ACTION_UP) { 6330b69338a45faa422ccba8faf64c9816c55d33e4aJin Cao if (mKeyInitiatedFromList) { 6340b69338a45faa422ccba8faf64c9816c55d33e4aJin Cao onListItemSelected(list.getSelectedView(), list.getSelectedItemPosition()); 6350b69338a45faa422ccba8faf64c9816c55d33e4aJin Cao } 6360b69338a45faa422ccba8faf64c9816c55d33e4aJin Cao mKeyInitiatedFromList = false; 6370b69338a45faa422ccba8faf64c9816c55d33e4aJin Cao } else if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) { 6380b69338a45faa422ccba8faf64c9816c55d33e4aJin Cao mKeyInitiatedFromList = true; 639d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao } 640d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao return true; 641d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao } else if (keyEvent.getAction() == KeyEvent.ACTION_UP) { 642d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao if (keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { 643d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao final int position = list.getSelectedItemPosition(); 6447ee90b37599cb6717782d40a6d085849918c29dfJin Cao final Object item = getAnimatedAdapter().getItem(position); 6457ee90b37599cb6717782d40a6d085849918c29dfJin Cao if (item != null && item instanceof ConversationCursor) { 6467ee90b37599cb6717782d40a6d085849918c29dfJin Cao final Conversation conv = ((ConversationCursor) item).getConversation(); 647d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao mCallbacks.onConversationFocused(conv); 648d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao } 649d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao } 650d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao } 651d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao return false; 652d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao } 653d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao 654d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao @Override 65508a079c3d2857e365736432b2691187767eb116fScott Kennedy public void onResume() { 65608a079c3d2857e365736432b2691187767eb116fScott Kennedy super.onResume(); 65708a079c3d2857e365736432b2691187767eb116fScott Kennedy 65852da8ba2b62cd3479be1ed9d447cb3b64d388395Jin Cao if (!isCursorReadyToShow()) { 65952da8ba2b62cd3479be1ed9d447cb3b64d388395Jin Cao // If the cursor got reset, let's reset the analytics state variable and show the list 66052da8ba2b62cd3479be1ed9d447cb3b64d388395Jin Cao // view since we are waiting for load again 661fa934276f5d1d5432ac96abd7308c3dd7f3e6d4bJin Cao mInitialCursorLoading = true; 662fa934276f5d1d5432ac96abd7308c3dd7f3e6d4bJin Cao showListView(); 663fa934276f5d1d5432ac96abd7308c3dd7f3e6d4bJin Cao } 664fa934276f5d1d5432ac96abd7308c3dd7f3e6d4bJin Cao 66552da8ba2b62cd3479be1ed9d447cb3b64d388395Jin Cao final ConversationCursor conversationCursor = getConversationListCursor(); 66608a079c3d2857e365736432b2691187767eb116fScott Kennedy if (conversationCursor != null) { 66708a079c3d2857e365736432b2691187767eb116fScott Kennedy conversationCursor.handleNotificationActions(); 668f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy 669f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy restoreLastScrolledPosition(); 67008a079c3d2857e365736432b2691187767eb116fScott Kennedy } 6711fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy 6721fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy mSelectedSet.addObserver(mConversationSetObserver); 67308a079c3d2857e365736432b2691187767eb116fScott Kennedy } 67408a079c3d2857e365736432b2691187767eb116fScott Kennedy 67508a079c3d2857e365736432b2691187767eb116fScott Kennedy @Override 676b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal public void onPause() { 677b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal super.onPause(); 6781fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy 6791fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy mSelectedSet.removeObserver(mConversationSetObserver); 680f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy 681f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy saveLastScrolledPosition(); 682b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal } 683b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal 684b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal @Override 685b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal public void onSaveInstanceState(Bundle outState) { 686b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal super.onSaveInstanceState(outState); 687b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal if (mListView != null) { 688b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal outState.putParcelable(LIST_STATE_KEY, mListView.onSaveInstanceState()); 689a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal outState.putInt(CHOICE_MODE_KEY, mListView.getChoiceMode()); 690b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal } 69132ddb84f2d4f53bfc4b53656461068c0c69d6325Scott Kennedy 69232ddb84f2d4f53bfc4b53656461068c0c69d6325Scott Kennedy if (mListAdapter != null) { 69332ddb84f2d4f53bfc4b53656461068c0c69d6325Scott Kennedy mListAdapter.saveSpecialItemInstanceState(outState); 69432ddb84f2d4f53bfc4b53656461068c0c69d6325Scott Kennedy } 695b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal } 696b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal 697b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal @Override 698b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal public void onStart() { 699b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal super.onStart(); 700b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal mHandler.postDelayed(mUpdateTimestampsRunnable, TIMESTAMP_UPDATE_INTERVAL); 701a90c33b7db2f47cd1e8129dd8c262e9e3e329f71Andy Huang Analytics.getInstance().sendView("ConversationListFragment"); 702b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal } 703b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal 704b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal @Override 705b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal public void onStop() { 706b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal super.onStop(); 707b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal mHandler.removeCallbacks(mUpdateTimestampsRunnable); 708b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal } 709b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal 71080aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwal @Override 711fa131a2ff399fd1d544f759b063268fb4e8a3e70Vikram Aggarwal public void onViewModeChanged(int newMode) { 712fa131a2ff399fd1d544f759b063268fb4e8a3e70Vikram Aggarwal if (mTabletDevice) { 713b0be3fcbc7b9f99cc6fc0a01e517b0f83e67c1f0Andy Huang if (ViewMode.isListMode(newMode)) { 7140509bba9a80de474a24368e1f3baebd3cd32cd5eVikram Aggarwal // There are no selected conversations when in conversation list mode. 715b0be3fcbc7b9f99cc6fc0a01e517b0f83e67c1f0Andy Huang clearChoicesAndActivated(); 716b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal } 717b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal } 718b5901be36e247573e8162714e8e006c4dbc35d4aMindy Pereira if (mFooterView != null) { 719b5901be36e247573e8162714e8e006c4dbc35d4aMindy Pereira mFooterView.onViewModeChanged(newMode); 720b5901be36e247573e8162714e8e006c4dbc35d4aMindy Pereira } 7215b317924c7c764954870501d99bfc57a243f6319Jin Cao 7225b317924c7c764954870501d99bfc57a243f6319Jin Cao // Set default navigation 7235b317924c7c764954870501d99bfc57a243f6319Jin Cao if (ViewMode.isListMode(newMode)) { 7245b317924c7c764954870501d99bfc57a243f6319Jin Cao mListView.setNextFocusRightId(R.id.conversation_list_view); 7255b317924c7c764954870501d99bfc57a243f6319Jin Cao mListView.requestFocus(); 7265b317924c7c764954870501d99bfc57a243f6319Jin Cao } else if (ViewMode.isConversationMode(newMode)) { 7275b317924c7c764954870501d99bfc57a243f6319Jin Cao // This would only happen in two_pane 7285b317924c7c764954870501d99bfc57a243f6319Jin Cao mListView.setNextFocusRightId(R.id.conversation_pager); 7295b317924c7c764954870501d99bfc57a243f6319Jin Cao } 730b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal } 731b5901be36e247573e8162714e8e006c4dbc35d4aMindy Pereira 73248ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang public boolean isAnimating() { 73348ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang final AnimatedAdapter adapter = getAnimatedAdapter(); 734f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang if (adapter != null && adapter.isAnimating()) { 735f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang return true; 736f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang } 737f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang final boolean isScrolling = (mListView != null && mListView.isScrolling()); 738f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang if (isScrolling) { 739f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang LogUtils.i(LOG_TAG, "CLF.isAnimating=true due to scrolling"); 740f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang } 741f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang return isScrolling; 74248ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang } 74348ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang 744b0be3fcbc7b9f99cc6fc0a01e517b0f83e67c1f0Andy Huang private void clearChoicesAndActivated() { 745b0be3fcbc7b9f99cc6fc0a01e517b0f83e67c1f0Andy Huang final int currentSelected = mListView.getCheckedItemPosition(); 746b0be3fcbc7b9f99cc6fc0a01e517b0f83e67c1f0Andy Huang if (currentSelected != ListView.INVALID_POSITION) { 747f0aebd32d750d6e108c8f9b551b1760682123ebbAndy Huang mListView.setItemChecked(mListView.getCheckedItemPosition(), false); 748b0be3fcbc7b9f99cc6fc0a01e517b0f83e67c1f0Andy Huang } 749b0be3fcbc7b9f99cc6fc0a01e517b0f83e67c1f0Andy Huang } 750b0be3fcbc7b9f99cc6fc0a01e517b0f83e67c1f0Andy Huang 751b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal /** 7529365a826b46b0e274df88e92534f7d871eef2aa2mindyp * Handles a request to show a new conversation list, either from a search 7539365a826b46b0e274df88e92534f7d871eef2aa2mindyp * query or for viewing a folder. This will initiate a data load, and hence 7549365a826b46b0e274df88e92534f7d871eef2aa2mindyp * must be called on the UI thread. 755b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal */ 756b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal private void showList() { 757faa9198087c44f2c46af40c8030b26d69617215dJin Cao mInitialCursorLoading = true; 7587591d2fc98c4308567a1d8822b41b48133227b25Andy Huang onFolderUpdated(mActivity.getFolderController().getFolder()); 759ca8ca2def56c0af5aa8237d19815d591ffc020e5mindyp onConversationListStatusUpdated(); 760a90c33b7db2f47cd1e8129dd8c262e9e3e329f71Andy Huang 761a90c33b7db2f47cd1e8129dd8c262e9e3e329f71Andy Huang // try to get an order-of-magnitude sense for message count within folders 762a90c33b7db2f47cd1e8129dd8c262e9e3e329f71Andy Huang // (N.B. this count currently isn't working for search folders, since their counts stream 763a90c33b7db2f47cd1e8129dd8c262e9e3e329f71Andy Huang // in over time in pieces.) 764a90c33b7db2f47cd1e8129dd8c262e9e3e329f71Andy Huang final Folder f = mViewContext.folder; 765a90c33b7db2f47cd1e8129dd8c262e9e3e329f71Andy Huang if (f != null) { 766a90c33b7db2f47cd1e8129dd8c262e9e3e329f71Andy Huang final long countLog; 767a90c33b7db2f47cd1e8129dd8c262e9e3e329f71Andy Huang if (f.totalCount > 0) { 768a90c33b7db2f47cd1e8129dd8c262e9e3e329f71Andy Huang countLog = (long) Math.log10(f.totalCount); 769a90c33b7db2f47cd1e8129dd8c262e9e3e329f71Andy Huang } else { 770a90c33b7db2f47cd1e8129dd8c262e9e3e329f71Andy Huang countLog = 0; 771a90c33b7db2f47cd1e8129dd8c262e9e3e329f71Andy Huang } 772a90c33b7db2f47cd1e8129dd8c262e9e3e329f71Andy Huang Analytics.getInstance().sendEvent("view_folder", f.getTypeDescription(), 773a90c33b7db2f47cd1e8129dd8c262e9e3e329f71Andy Huang Long.toString(countLog), f.totalCount); 774a90c33b7db2f47cd1e8129dd8c262e9e3e329f71Andy Huang } 775b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal } 776b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal 7778afccad8b03701577bb4b734a0f4fe76f6cab487Scott Kennedy /** 7788afccad8b03701577bb4b734a0f4fe76f6cab487Scott Kennedy * View the message at the given position. 7798afccad8b03701577bb4b734a0f4fe76f6cab487Scott Kennedy * 7808afccad8b03701577bb4b734a0f4fe76f6cab487Scott Kennedy * @param position The position of the conversation in the list (as opposed to its position 7818afccad8b03701577bb4b734a0f4fe76f6cab487Scott Kennedy * in the cursor) 7828afccad8b03701577bb4b734a0f4fe76f6cab487Scott Kennedy */ 7838afccad8b03701577bb4b734a0f4fe76f6cab487Scott Kennedy private void viewConversation(final int position) { 7848afccad8b03701577bb4b734a0f4fe76f6cab487Scott Kennedy LogUtils.d(LOG_TAG, "ConversationListFragment.viewConversation(%d)", position); 7851fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy 7868afccad8b03701577bb4b734a0f4fe76f6cab487Scott Kennedy final ConversationCursor cursor = 7878afccad8b03701577bb4b734a0f4fe76f6cab487Scott Kennedy (ConversationCursor) getAnimatedAdapter().getItem(position); 78897b10963491a88254e84924c208844c4a3fdbec4Andy Huang 7898afccad8b03701577bb4b734a0f4fe76f6cab487Scott Kennedy if (cursor == null) { 7908afccad8b03701577bb4b734a0f4fe76f6cab487Scott Kennedy LogUtils.e(LOG_TAG, 7918afccad8b03701577bb4b734a0f4fe76f6cab487Scott Kennedy "unable to open conv at cursor pos=%s cursor=%s getPositionOffset=%s", 7928afccad8b03701577bb4b734a0f4fe76f6cab487Scott Kennedy position, cursor, getAnimatedAdapter().getPositionOffset(position)); 7938afccad8b03701577bb4b734a0f4fe76f6cab487Scott Kennedy return; 7941fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy } 7951fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy 7968afccad8b03701577bb4b734a0f4fe76f6cab487Scott Kennedy final Conversation conv = cursor.getConversation(); 7978afccad8b03701577bb4b734a0f4fe76f6cab487Scott Kennedy /* 7988afccad8b03701577bb4b734a0f4fe76f6cab487Scott Kennedy * The cursor position may be different than the position method parameter because of 7998afccad8b03701577bb4b734a0f4fe76f6cab487Scott Kennedy * special views in the list. 8008afccad8b03701577bb4b734a0f4fe76f6cab487Scott Kennedy */ 8018afccad8b03701577bb4b734a0f4fe76f6cab487Scott Kennedy conv.position = cursor.getPosition(); 8028afccad8b03701577bb4b734a0f4fe76f6cab487Scott Kennedy setSelected(conv.position, true); 8038afccad8b03701577bb4b734a0f4fe76f6cab487Scott Kennedy mCallbacks.onConversationSelected(conv, false /* inLoaderCallbacks */); 8048afccad8b03701577bb4b734a0f4fe76f6cab487Scott Kennedy } 8051fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy 806a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal /** 807a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal * Sets the selected conversation to the position given here. 8080d74a662f6e96b5a21e37978eb6e3b46545e3cbfAlice Yang * @param cursorPosition The position of the conversation in the cursor (as opposed to 8090d74a662f6e96b5a21e37978eb6e3b46545e3cbfAlice Yang * in the list) 810a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal * @param different if the currently selected conversation is different from the one provided 811a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal * here. This is a difference in conversations, not a difference in positions. For example, a 812a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal * conversation at position 2 can move to position 4 as a result of new mail. 813a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal */ 8147c8325de829ca029ce4547e4f0fa266124301367Scott Kennedy public void setSelected(final int cursorPosition, boolean different) { 815a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal if (mListView.getChoiceMode() == ListView.CHOICE_MODE_NONE) { 816a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal return; 817a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal } 8187c8325de829ca029ce4547e4f0fa266124301367Scott Kennedy 8197c8325de829ca029ce4547e4f0fa266124301367Scott Kennedy final int position = 8207c8325de829ca029ce4547e4f0fa266124301367Scott Kennedy cursorPosition + getAnimatedAdapter().getPositionOffset(cursorPosition); 8217c8325de829ca029ce4547e4f0fa266124301367Scott Kennedy 822e0d01fb7379225a0f058999d9da9050f2fcc6945Scott Kennedy setRawSelected(position, different); 823e0d01fb7379225a0f058999d9da9050f2fcc6945Scott Kennedy } 824e0d01fb7379225a0f058999d9da9050f2fcc6945Scott Kennedy 825e0d01fb7379225a0f058999d9da9050f2fcc6945Scott Kennedy /** 826e0d01fb7379225a0f058999d9da9050f2fcc6945Scott Kennedy * Sets the selected conversation to the position given here. 827e0d01fb7379225a0f058999d9da9050f2fcc6945Scott Kennedy * @param position The position of the item in the list 828e0d01fb7379225a0f058999d9da9050f2fcc6945Scott Kennedy * @param different if the currently selected conversation is different from the one provided 829e0d01fb7379225a0f058999d9da9050f2fcc6945Scott Kennedy * here. This is a difference in conversations, not a difference in positions. For example, a 830e0d01fb7379225a0f058999d9da9050f2fcc6945Scott Kennedy * conversation at position 2 can move to position 4 as a result of new mail. 831e0d01fb7379225a0f058999d9da9050f2fcc6945Scott Kennedy */ 832e0d01fb7379225a0f058999d9da9050f2fcc6945Scott Kennedy public void setRawSelected(final int position, final boolean different) { 833e0d01fb7379225a0f058999d9da9050f2fcc6945Scott Kennedy if (mListView.getChoiceMode() == ListView.CHOICE_MODE_NONE) { 834e0d01fb7379225a0f058999d9da9050f2fcc6945Scott Kennedy return; 835e0d01fb7379225a0f058999d9da9050f2fcc6945Scott Kennedy } 836e0d01fb7379225a0f058999d9da9050f2fcc6945Scott Kennedy 837e21f88695fca2fde6d3636ab505ca28f74c8eb42mindyp if (different) { 838e21f88695fca2fde6d3636ab505ca28f74c8eb42mindyp mListView.smoothScrollToPosition(position); 839e21f88695fca2fde6d3636ab505ca28f74c8eb42mindyp } 840e21f88695fca2fde6d3636ab505ca28f74c8eb42mindyp mListView.setItemChecked(position, true); 841e21f88695fca2fde6d3636ab505ca28f74c8eb42mindyp } 842e21f88695fca2fde6d3636ab505ca28f74c8eb42mindyp 8433d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal /** 844a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal * Returns the cursor associated with the conversation list. 845a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal * @return 8463d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal */ 847967ede63e059810d9d4d425c50111d606014c893Mindy Pereira private ConversationCursor getConversationListCursor() { 848967ede63e059810d9d4d425c50111d606014c893Mindy Pereira return mCallbacks != null ? mCallbacks.getConversationListCursor() : null; 849fe06bea4de68a072b0e5a0fc7cefcdfa96143a8fMindy Pereira } 850fe06bea4de68a072b0e5a0fc7cefcdfa96143a8fMindy Pereira 851fe06bea4de68a072b0e5a0fc7cefcdfa96143a8fMindy Pereira /** 8529365a826b46b0e274df88e92534f7d871eef2aa2mindyp * Request a refresh of the list. No sync is carried out and none is 8539365a826b46b0e274df88e92534f7d871eef2aa2mindyp * promised. 85454452ae7a9cdb607ed1db5e74077b5ebd95c46a2Vikram Aggarwal */ 85554452ae7a9cdb607ed1db5e74077b5ebd95c46a2Vikram Aggarwal public void requestListRefresh() { 85654452ae7a9cdb607ed1db5e74077b5ebd95c46a2Vikram Aggarwal mListAdapter.notifyDataSetChanged(); 85754452ae7a9cdb607ed1db5e74077b5ebd95c46a2Vikram Aggarwal } 85854452ae7a9cdb607ed1db5e74077b5ebd95c46a2Vikram Aggarwal 85975daee52f08c9bf66652b9bddf42cfbd1ca71987Vikram Aggarwal /** 8607f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal * Change the UI to delete the conversations provided and then call the 8619365a826b46b0e274df88e92534f7d871eef2aa2mindyp * {@link DestructiveAction} provided here <b>after</b> the UI has been 8629365a826b46b0e274df88e92534f7d871eef2aa2mindyp * updated. 8637f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal * @param conversations 86475daee52f08c9bf66652b9bddf42cfbd1ca71987Vikram Aggarwal * @param action 86575daee52f08c9bf66652b9bddf42cfbd1ca71987Vikram Aggarwal */ 866a8e4318bb9921e2ec6045c5f7187a4b78c55fe10Vikram Aggarwal public void requestDelete(int actionId, final Collection<Conversation> conversations, 867669947bf67a3d0eed377b18099bb873604ad5009Vikram Aggarwal final DestructiveAction action) { 868acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereira for (Conversation conv : conversations) { 869acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereira conv.localDeleteOnUpdate = true; 870acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereira } 871a8e4318bb9921e2ec6045c5f7187a4b78c55fe10Vikram Aggarwal final ListItemsRemovedListener listener = new ListItemsRemovedListener() { 8729365a826b46b0e274df88e92534f7d871eef2aa2mindyp @Override 8739365a826b46b0e274df88e92534f7d871eef2aa2mindyp public void onListItemsRemoved() { 8749365a826b46b0e274df88e92534f7d871eef2aa2mindyp action.performAction(); 8759365a826b46b0e274df88e92534f7d871eef2aa2mindyp } 876a8e4318bb9921e2ec6045c5f7187a4b78c55fe10Vikram Aggarwal }; 8775b317924c7c764954870501d99bfc57a243f6319Jin Cao if (mListView.getSwipeAction() == actionId) { 8785b317924c7c764954870501d99bfc57a243f6319Jin Cao if (!mListView.destroyItems(conversations, listener)) { 879cec3e0b9c90173e0bdb2d072046b38714fc9186ePaul Westbrook // The listView failed to destroy the items, perform the action manually 880cec3e0b9c90173e0bdb2d072046b38714fc9186ePaul Westbrook LogUtils.e(LOG_TAG, "ConversationListFragment.requestDelete: " + 881cec3e0b9c90173e0bdb2d072046b38714fc9186ePaul Westbrook "listView failed to destroy items."); 882cec3e0b9c90173e0bdb2d072046b38714fc9186ePaul Westbrook action.performAction(); 883cec3e0b9c90173e0bdb2d072046b38714fc9186ePaul Westbrook } 884a8e4318bb9921e2ec6045c5f7187a4b78c55fe10Vikram Aggarwal return; 885a8e4318bb9921e2ec6045c5f7187a4b78c55fe10Vikram Aggarwal } 886a8e4318bb9921e2ec6045c5f7187a4b78c55fe10Vikram Aggarwal // Delete the local delete items (all for now) and when done, 887a8e4318bb9921e2ec6045c5f7187a4b78c55fe10Vikram Aggarwal // update... 888a8e4318bb9921e2ec6045c5f7187a4b78c55fe10Vikram Aggarwal mListAdapter.delete(conversations, listener); 889acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereira } 890a3911aafc876fc21cdbc5a997ecea5c1f293df5dMindy Pereira 89111dd5effd79b8078b24bcdb4160e8e12a17317a5Mindy Pereira public void onFolderUpdated(Folder folder) { 89252da8ba2b62cd3479be1ed9d447cb3b64d388395Jin Cao if (!isCursorReadyToShow()) { 893faa9198087c44f2c46af40c8030b26d69617215dJin Cao // Wait a bit before showing either the empty or loading view. If the messages are 894faa9198087c44f2c46af40c8030b26d69617215dJin Cao // actually local, it's disorienting to see this appear on every folder transition. 895faa9198087c44f2c46af40c8030b26d69617215dJin Cao // If they aren't, then it will likely take more than 200 milliseconds to load, and 896faa9198087c44f2c46af40c8030b26d69617215dJin Cao // then we'll see the loading view. 897faa9198087c44f2c46af40c8030b26d69617215dJin Cao if (!mLoadingViewPending) { 898faa9198087c44f2c46af40c8030b26d69617215dJin Cao mHandler.postDelayed(mLoadingViewRunnable, LOADING_DELAY_MS); 899faa9198087c44f2c46af40c8030b26d69617215dJin Cao mLoadingViewPending = true; 900faa9198087c44f2c46af40c8030b26d69617215dJin Cao } 901faa9198087c44f2c46af40c8030b26d69617215dJin Cao } 902faa9198087c44f2c46af40c8030b26d69617215dJin Cao 903dc8963bb37926496e14df8e53e3e5a87a777d9b4Mindy Pereira mFolder = folder; 90406642fab1bf4ab95b5dd97a65b262845cf60c865Mindy Pereira setSwipeAction(); 9054bb435caccc8ac148ca925813da601d9368d5066Mindy Pereira 9064bb435caccc8ac148ca925813da601d9368d5066Mindy Pereira // Update enabled state of swipe to refresh. 9074bb435caccc8ac148ca925813da601d9368d5066Mindy Pereira mSwipeRefreshWidget.setEnabled(!ConversationListContext.isSearchResult(mViewContext)); 9084bb435caccc8ac148ca925813da601d9368d5066Mindy Pereira 90982cea4896518e35bf8509b221bb46ae5ac610699Mindy Pereira if (mFolder == null) { 91082cea4896518e35bf8509b221bb46ae5ac610699Mindy Pereira return; 91182cea4896518e35bf8509b221bb46ae5ac610699Mindy Pereira } 9124584a0d83e160444f931cb565185a2eea39b1683Mindy Pereira mListAdapter.setFolder(mFolder); 91370a70c92bca006af84177d025c32deee3aa6da3fMindy Pereira mFooterView.setFolder(mFolder); 91441b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal if (!mFolder.wasSyncSuccessful()) { 91570a70c92bca006af84177d025c32deee3aa6da3fMindy Pereira mErrorListener.onError(mFolder, false); 91670a70c92bca006af84177d025c32deee3aa6da3fMindy Pereira } 917c01f5d9455f3482b2d72a179733d5854c8acccd0Paul Westbrook 918b95d5611e508abae1f3bacaf46a6649e21007a96Jin Cao // Update the sync status bar with sync results if needed 919b95d5611e508abae1f3bacaf46a6649e21007a96Jin Cao checkSyncStatus(); 920c01f5d9455f3482b2d72a179733d5854c8acccd0Paul Westbrook 92112fe37aa24c313fd8192dede0770dadf0ec23359Mindy Pereira // Blow away conversation items cache. 92212fe37aa24c313fd8192dede0770dadf0ec23359Mindy Pereira ConversationItemViewModel.onFolderUpdated(mFolder); 92370a70c92bca006af84177d025c32deee3aa6da3fMindy Pereira } 92470a70c92bca006af84177d025c32deee3aa6da3fMindy Pereira 92581a4f08b1657bb297cd977e3df92660fab05a1abVikram Aggarwal /** 92681a4f08b1657bb297cd977e3df92660fab05a1abVikram Aggarwal * Updates the footer visibility and updates the conversation cursor 92781a4f08b1657bb297cd977e3df92660fab05a1abVikram Aggarwal */ 92870a70c92bca006af84177d025c32deee3aa6da3fMindy Pereira public void onConversationListStatusUpdated() { 929c01f5d9455f3482b2d72a179733d5854c8acccd0Paul Westbrook // Also change the cursor here. 930c01f5d9455f3482b2d72a179733d5854c8acccd0Paul Westbrook onCursorUpdated(); 931faa9198087c44f2c46af40c8030b26d69617215dJin Cao 93252da8ba2b62cd3479be1ed9d447cb3b64d388395Jin Cao if (isCursorReadyToShow() && mCanTakeDownLoadingView) { 933faa9198087c44f2c46af40c8030b26d69617215dJin Cao hideLoadingViewAndShowContents(); 934faa9198087c44f2c46af40c8030b26d69617215dJin Cao } 935c01f5d9455f3482b2d72a179733d5854c8acccd0Paul Westbrook } 936c01f5d9455f3482b2d72a179733d5854c8acccd0Paul Westbrook 9374cdc4857b1b42659158b585648a58c3260a9536fMartin Hibdon private void hideLoadingViewAndShowContents() { 9384cdc4857b1b42659158b585648a58c3260a9536fMartin Hibdon final ConversationCursor cursor = getConversationListCursor(); 9394cdc4857b1b42659158b585648a58c3260a9536fMartin Hibdon final boolean showFooter = mFooterView.updateStatus(cursor); 940b95d5611e508abae1f3bacaf46a6649e21007a96Jin Cao // Update the sync status bar with sync results if needed 941b95d5611e508abae1f3bacaf46a6649e21007a96Jin Cao checkSyncStatus(); 9424cdc4857b1b42659158b585648a58c3260a9536fMartin Hibdon mListAdapter.setFooterVisibility(showFooter); 9434cdc4857b1b42659158b585648a58c3260a9536fMartin Hibdon mLoadingViewPending = false; 9444cdc4857b1b42659158b585648a58c3260a9536fMartin Hibdon mHandler.removeCallbacks(mLoadingViewRunnable); 945ee6766a2c6938dbb4c975d93732d7033eecd90a5Jin Cao 946ee6766a2c6938dbb4c975d93732d7033eecd90a5Jin Cao // Even though cursor might be empty, the list adapter might have teasers/footers. 947ee6766a2c6938dbb4c975d93732d7033eecd90a5Jin Cao // So we check the list adapter count if the cursor is fully/partially loaded. 948ee6766a2c6938dbb4c975d93732d7033eecd90a5Jin Cao if (cursor != null && ConversationCursor.isCursorReadyToShow(cursor) && 949ee6766a2c6938dbb4c975d93732d7033eecd90a5Jin Cao mListAdapter.getCount() == 0) { 950ee6766a2c6938dbb4c975d93732d7033eecd90a5Jin Cao showEmptyView(); 951ee6766a2c6938dbb4c975d93732d7033eecd90a5Jin Cao } else { 952ee6766a2c6938dbb4c975d93732d7033eecd90a5Jin Cao showListView(); 953ee6766a2c6938dbb4c975d93732d7033eecd90a5Jin Cao } 9544cdc4857b1b42659158b585648a58c3260a9536fMartin Hibdon } 9554cdc4857b1b42659158b585648a58c3260a9536fMartin Hibdon 95606642fab1bf4ab95b5dd97a65b262845cf60c865Mindy Pereira private void setSwipeAction() { 9577c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal int swipeSetting = Settings.getSwipeSetting(mAccount.settings); 958e58222b9421756e11eb24799c1785a090d0793adMindy Pereira if (swipeSetting == Swipe.DISABLED 959b807337282fb19d1a38cdbe092a2f472b0e30321Mindy Pereira || !mAccount.supportsCapability(AccountCapabilities.UNDO) 960b807337282fb19d1a38cdbe092a2f472b0e30321Mindy Pereira || (mFolder != null && mFolder.isTrash())) { 961e58222b9421756e11eb24799c1785a090d0793adMindy Pereira mListView.enableSwipe(false); 962e58222b9421756e11eb24799c1785a090d0793adMindy Pereira } else { 9633b965d78774a42358ce6bbdcc43b4c8df130a60eScott Kennedy final int action; 9647f98d0da720da81d497046c038ffeca6d56f2a08Rohan Shah mListView.enableSwipe(true); 965d4e02e4072a34c0207841298ddcb4f081a94973aJin Cao if (mFolder == null) { 96601f30509c4c8ad0d13b7635447b7f023c7a0cab7Mindy Pereira action = R.id.remove_folder; 9674e812f49c5bbba50a60d062d624403952f8d5487Mindy Pereira } else { 9684e812f49c5bbba50a60d062d624403952f8d5487Mindy Pereira switch (swipeSetting) { 969d4e02e4072a34c0207841298ddcb4f081a94973aJin Cao // Try to respect user's setting as best as we can and default to doing nothing 970d4e02e4072a34c0207841298ddcb4f081a94973aJin Cao case Swipe.DELETE: 971512821c11d89d49908f3cfdee0b582601f500f3dJin Cao // Delete in Outbox means discard failed message and put it in draft 972512821c11d89d49908f3cfdee0b582601f500f3dJin Cao if (mFolder.isType(UIProvider.FolderType.OUTBOX)) { 973512821c11d89d49908f3cfdee0b582601f500f3dJin Cao action = R.id.discard_outbox; 974512821c11d89d49908f3cfdee0b582601f500f3dJin Cao } else { 975512821c11d89d49908f3cfdee0b582601f500f3dJin Cao action = R.id.delete; 976512821c11d89d49908f3cfdee0b582601f500f3dJin Cao } 977d4e02e4072a34c0207841298ddcb4f081a94973aJin Cao break; 9784e812f49c5bbba50a60d062d624403952f8d5487Mindy Pereira case Swipe.ARCHIVE: 979d4e02e4072a34c0207841298ddcb4f081a94973aJin Cao // Special case spam since it shouldn't remove spam folder label on swipe 980d4e02e4072a34c0207841298ddcb4f081a94973aJin Cao if (mAccount.supportsCapability(AccountCapabilities.ARCHIVE) 981d4e02e4072a34c0207841298ddcb4f081a94973aJin Cao && !mFolder.isSpam()) { 9824e812f49c5bbba50a60d062d624403952f8d5487Mindy Pereira if (mFolder.supportsCapability(FolderCapabilities.ARCHIVE)) { 9834e812f49c5bbba50a60d062d624403952f8d5487Mindy Pereira action = R.id.archive; 9844e812f49c5bbba50a60d062d624403952f8d5487Mindy Pereira break; 9854e812f49c5bbba50a60d062d624403952f8d5487Mindy Pereira } else if (mFolder.supportsCapability 9864e812f49c5bbba50a60d062d624403952f8d5487Mindy Pereira (FolderCapabilities.CAN_ACCEPT_MOVED_MESSAGES)) { 98701f30509c4c8ad0d13b7635447b7f023c7a0cab7Mindy Pereira action = R.id.remove_folder; 9884e812f49c5bbba50a60d062d624403952f8d5487Mindy Pereira break; 9894e812f49c5bbba50a60d062d624403952f8d5487Mindy Pereira } 9904e812f49c5bbba50a60d062d624403952f8d5487Mindy Pereira } 9913b965d78774a42358ce6bbdcc43b4c8df130a60eScott Kennedy 9923b965d78774a42358ce6bbdcc43b4c8df130a60eScott Kennedy /* 9933b965d78774a42358ce6bbdcc43b4c8df130a60eScott Kennedy * If we get here, we don't support archive, on either the account or the 9943432b636fbcf7d6cc551b9705ed4dd88ae2b4f49Jin Cao * folder, so we want to fall through to swipe doing nothing 9953b965d78774a42358ce6bbdcc43b4c8df130a60eScott Kennedy */ 9963b965d78774a42358ce6bbdcc43b4c8df130a60eScott Kennedy //$FALL-THROUGH$ 9974e812f49c5bbba50a60d062d624403952f8d5487Mindy Pereira default: 9983432b636fbcf7d6cc551b9705ed4dd88ae2b4f49Jin Cao mListView.enableSwipe(false); 9993432b636fbcf7d6cc551b9705ed4dd88ae2b4f49Jin Cao action = 0; // Use default value so setSwipeAction essentially has no effect 10004e812f49c5bbba50a60d062d624403952f8d5487Mindy Pereira break; 10014e812f49c5bbba50a60d062d624403952f8d5487Mindy Pereira } 1002e58222b9421756e11eb24799c1785a090d0793adMindy Pereira } 1003e58222b9421756e11eb24799c1785a090d0793adMindy Pereira mListView.setSwipeAction(action); 1004e58222b9421756e11eb24799c1785a090d0793adMindy Pereira } 10056126d72ae2769bd39451872f45781cadb5b90515Mark Wei mListView.setCurrentAccount(mAccount); 100606642fab1bf4ab95b5dd97a65b262845cf60c865Mindy Pereira mListView.setCurrentFolder(mFolder); 100706642fab1bf4ab95b5dd97a65b262845cf60c865Mindy Pereira } 100806642fab1bf4ab95b5dd97a65b262845cf60c865Mindy Pereira 100917f373ecf6961112f77a7516ab1bbcb4fcdf2884Vikram Aggarwal /** 101017f373ecf6961112f77a7516ab1bbcb4fcdf2884Vikram Aggarwal * Changes the conversation cursor in the list and sets selected position if none is set. 101117f373ecf6961112f77a7516ab1bbcb4fcdf2884Vikram Aggarwal */ 101217f373ecf6961112f77a7516ab1bbcb4fcdf2884Vikram Aggarwal private void onCursorUpdated() { 101317f373ecf6961112f77a7516ab1bbcb4fcdf2884Vikram Aggarwal if (mCallbacks == null || mListAdapter == null) { 101417f373ecf6961112f77a7516ab1bbcb4fcdf2884Vikram Aggarwal return; 101517f373ecf6961112f77a7516ab1bbcb4fcdf2884Vikram Aggarwal } 101681a4f08b1657bb297cd977e3df92660fab05a1abVikram Aggarwal // Check against the previous cursor here and see if they are the same. If they are, then 101781a4f08b1657bb297cd977e3df92660fab05a1abVikram Aggarwal // do a notifyDataSetChanged. 101881a4f08b1657bb297cd977e3df92660fab05a1abVikram Aggarwal final ConversationCursor newCursor = mCallbacks.getConversationListCursor(); 1019f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy 1020f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy if (newCursor == null && mListAdapter.getCursor() != null) { 1021f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy // We're losing our cursor, so save our scroll position 1022f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy saveLastScrolledPosition(); 1023f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy } 1024f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy 102581a4f08b1657bb297cd977e3df92660fab05a1abVikram Aggarwal mListAdapter.swapCursor(newCursor); 102681a4f08b1657bb297cd977e3df92660fab05a1abVikram Aggarwal // When the conversation cursor is *updated*, we get back the same instance. In that 102781a4f08b1657bb297cd977e3df92660fab05a1abVikram Aggarwal // situation, CursorAdapter.swapCursor() silently returns, without forcing a 102881a4f08b1657bb297cd977e3df92660fab05a1abVikram Aggarwal // notifyDataSetChanged(). So let's force a call to notifyDataSetChanged, since an updated 102981a4f08b1657bb297cd977e3df92660fab05a1abVikram Aggarwal // cursor means that the dataset has changed. 103081a4f08b1657bb297cd977e3df92660fab05a1abVikram Aggarwal final int newCursorHash = (newCursor == null) ? 0 : newCursor.hashCode(); 103181a4f08b1657bb297cd977e3df92660fab05a1abVikram Aggarwal if (mConversationCursorHash == newCursorHash && mConversationCursorHash != 0) { 103281a4f08b1657bb297cd977e3df92660fab05a1abVikram Aggarwal mListAdapter.notifyDataSetChanged(); 103381a4f08b1657bb297cd977e3df92660fab05a1abVikram Aggarwal } 103481a4f08b1657bb297cd977e3df92660fab05a1abVikram Aggarwal mConversationCursorHash = newCursorHash; 1035b1e21487e18503cc110e1cf8726b835e048ab1c1Scott Kennedy 10368bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao updateAnalyticsData(newCursor); 1037780fbf7b56959bbdd2b3346b2f92cabedd84b50ePaul Westbrook if (newCursor != null) { 1038780fbf7b56959bbdd2b3346b2f92cabedd84b50ePaul Westbrook final int newCursorCount = newCursor.getCount(); 1039780fbf7b56959bbdd2b3346b2f92cabedd84b50ePaul Westbrook updateSearchResultHeader(newCursorCount); 1040780fbf7b56959bbdd2b3346b2f92cabedd84b50ePaul Westbrook if (newCursorCount > 0) { 1041780fbf7b56959bbdd2b3346b2f92cabedd84b50ePaul Westbrook newCursor.markContentsSeen(); 1042780fbf7b56959bbdd2b3346b2f92cabedd84b50ePaul Westbrook restoreLastScrolledPosition(); 1043780fbf7b56959bbdd2b3346b2f92cabedd84b50ePaul Westbrook } 1044b1e21487e18503cc110e1cf8726b835e048ab1c1Scott Kennedy } 1045b1e21487e18503cc110e1cf8726b835e048ab1c1Scott Kennedy 104617f373ecf6961112f77a7516ab1bbcb4fcdf2884Vikram Aggarwal // If a current conversation is available, and none is selected in the list, then ask 104717f373ecf6961112f77a7516ab1bbcb4fcdf2884Vikram Aggarwal // the list to select the current conversation. 104817f373ecf6961112f77a7516ab1bbcb4fcdf2884Vikram Aggarwal final Conversation conv = mCallbacks.getCurrentConversation(); 1049f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy if (conv != null) { 1050f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy if (mListView.getChoiceMode() != ListView.CHOICE_MODE_NONE 1051f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy && mListView.getCheckedItemPosition() == -1) { 1052f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy setSelected(conv.position, true); 1053f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy } 1054967ede63e059810d9d4d425c50111d606014c893Mindy Pereira } 105521ab490d4919985d611bc36ae2d8aea86a911499Mindy Pereira } 10561e2573bbba613fb700e8fa01c58c9382c0027b5eMindy Pereira 1057c6adce3cf6887c4c6dd5005724565702751843d0mindyp public void commitDestructiveActions(boolean animate) { 10588937bf1552a86853efc798a4d8df34c01115cdfdMindy Pereira if (mListView != null) { 1059c6adce3cf6887c4c6dd5005724565702751843d0mindyp mListView.commitDestructiveActions(animate); 10607d81600cbce3cfd366cbff9ecd1b7317ff957221Vikram Aggarwal 10617d81600cbce3cfd366cbff9ecd1b7317ff957221Vikram Aggarwal } 10627d81600cbce3cfd366cbff9ecd1b7317ff957221Vikram Aggarwal } 10637d81600cbce3cfd366cbff9ecd1b7317ff957221Vikram Aggarwal 10649365a826b46b0e274df88e92534f7d871eef2aa2mindyp @Override 10659365a826b46b0e274df88e92534f7d871eef2aa2mindyp public void onListItemSwiped(Collection<Conversation> conversations) { 10669365a826b46b0e274df88e92534f7d871eef2aa2mindyp mUpdater.showNextConversation(conversations); 10679365a826b46b0e274df88e92534f7d871eef2aa2mindyp } 10680d74a662f6e96b5a21e37978eb6e3b46545e3cbfAlice Yang 1069486e63e5bc7a88455e6fcb83d9cb251df203f727Alice Yang private void checkSyncStatus() { 107076d20657374ba16e2d4fdc8319057068cfcefcf6Alice Yang if (mFolder != null && mFolder.isSyncInProgress()) { 107103752f3a42e2cfb54c3873c12739982cb16b58d5Alice Yang LogUtils.d(LOG_TAG, "CLF.checkSyncStatus still syncing"); 1072486e63e5bc7a88455e6fcb83d9cb251df203f727Alice Yang // Still syncing, ignore 10730d74a662f6e96b5a21e37978eb6e3b46545e3cbfAlice Yang } else { 1074486e63e5bc7a88455e6fcb83d9cb251df203f727Alice Yang // Finished syncing: 107503752f3a42e2cfb54c3873c12739982cb16b58d5Alice Yang LogUtils.d(LOG_TAG, "CLF.checkSyncStatus done syncing"); 10764bb435caccc8ac148ca925813da601d9368d5066Mindy Pereira mSwipeRefreshWidget.setRefreshing(false); 10770d74a662f6e96b5a21e37978eb6e3b46545e3cbfAlice Yang } 10780d74a662f6e96b5a21e37978eb6e3b46545e3cbfAlice Yang } 10790d74a662f6e96b5a21e37978eb6e3b46545e3cbfAlice Yang 1080486e63e5bc7a88455e6fcb83d9cb251df203f727Alice Yang /** 1081486e63e5bc7a88455e6fcb83d9cb251df203f727Alice Yang * Displays the indefinite progress bar indicating a sync is in progress. This 1082486e63e5bc7a88455e6fcb83d9cb251df203f727Alice Yang * should only be called if user manually requested a sync, and not for background syncs. 1083486e63e5bc7a88455e6fcb83d9cb251df203f727Alice Yang */ 1084486e63e5bc7a88455e6fcb83d9cb251df203f727Alice Yang protected void showSyncStatusBar() { 10854bb435caccc8ac148ca925813da601d9368d5066Mindy Pereira mSwipeRefreshWidget.setRefreshing(true); 10860d74a662f6e96b5a21e37978eb6e3b46545e3cbfAlice Yang } 1087d4f2287a529ae743955700f318f60bb5b6c02bc0Rohan Shah 1088d4f2287a529ae743955700f318f60bb5b6c02bc0Rohan Shah /** 1089d4f2287a529ae743955700f318f60bb5b6c02bc0Rohan Shah * Clears all items in the list. 1090d4f2287a529ae743955700f318f60bb5b6c02bc0Rohan Shah */ 1091d4f2287a529ae743955700f318f60bb5b6c02bc0Rohan Shah public void clear() { 1092d4f2287a529ae743955700f318f60bb5b6c02bc0Rohan Shah mListView.setAdapter(null); 1093d4f2287a529ae743955700f318f60bb5b6c02bc0Rohan Shah } 10941fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy 10951fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy private final ConversationSetObserver mConversationSetObserver = new ConversationSetObserver() { 10961fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy @Override 10971fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy public void onSetPopulated(final ConversationSelectionSet set) { 10984bb435caccc8ac148ca925813da601d9368d5066Mindy Pereira // Disable the swipe to refresh widget. 10994bb435caccc8ac148ca925813da601d9368d5066Mindy Pereira mSwipeRefreshWidget.setEnabled(false); 11001fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy } 11011fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy 11021fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy @Override 11031fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy public void onSetEmpty() { 11044bb435caccc8ac148ca925813da601d9368d5066Mindy Pereira mSwipeRefreshWidget.setEnabled(true); 11051fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy } 11061fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy 11071fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy @Override 11081fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy public void onSetChanged(final ConversationSelectionSet set) { 11098afccad8b03701577bb4b734a0f4fe76f6cab487Scott Kennedy // Do nothing 11101fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy } 11111fea6a3cffcc8c4afc3d877e5dc57d29f9665942Scott Kennedy }; 1112f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy 1113f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy private void saveLastScrolledPosition() { 1114f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy if (mListAdapter.getCursor() == null) { 1115f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy // If you save your scroll position in an empty list, you're gonna have a bad time 1116f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy return; 1117f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy } 1118f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy 1119f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy final Parcelable savedState = mListView.onSaveInstanceState(); 1120f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy 1121f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy mActivity.getListHandler().setConversationListScrollPosition( 1122f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy mFolder.conversationListUri.toString(), savedState); 1123f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy } 1124f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy 1125f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy private void restoreLastScrolledPosition() { 1126f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy // Scroll to our previous position, if necessary 1127599c3cfaeff6ff519b08467a3fcd96fb33c0e1e1Scott Kennedy if (!mScrollPositionRestored && mFolder != null) { 1128599c3cfaeff6ff519b08467a3fcd96fb33c0e1e1Scott Kennedy final String key = mFolder.conversationListUri.toString(); 1129f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy final Parcelable savedState = mActivity.getListHandler() 1130599c3cfaeff6ff519b08467a3fcd96fb33c0e1e1Scott Kennedy .getConversationListScrollPosition(key); 1131f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy if (savedState != null) { 1132f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy mListView.onRestoreInstanceState(savedState); 1133f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy } 1134f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy mScrollPositionRestored = true; 1135f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy } 1136f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy } 11374bb435caccc8ac148ca925813da601d9368d5066Mindy Pereira 11384bb435caccc8ac148ca925813da601d9368d5066Mindy Pereira /* (non-Javadoc) 11394bb435caccc8ac148ca925813da601d9368d5066Mindy Pereira * @see android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener#onRefresh() 11404bb435caccc8ac148ca925813da601d9368d5066Mindy Pereira */ 11414bb435caccc8ac148ca925813da601d9368d5066Mindy Pereira @Override 11424bb435caccc8ac148ca925813da601d9368d5066Mindy Pereira public void onRefresh() { 11434bb435caccc8ac148ca925813da601d9368d5066Mindy Pereira Analytics.getInstance().sendEvent(Analytics.EVENT_CATEGORY_MENU_ITEM, "swipe_refresh", null, 11444bb435caccc8ac148ca925813da601d9368d5066Mindy Pereira 0); 11454bb435caccc8ac148ca925813da601d9368d5066Mindy Pereira 11464bb435caccc8ac148ca925813da601d9368d5066Mindy Pereira // This will call back to showSyncStatusBar(): 11474bb435caccc8ac148ca925813da601d9368d5066Mindy Pereira mActivity.getFolderController().requestFolderRefresh(); 1148f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang 1149f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang // Clear list adapter state out of an abundance of caution. 1150f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang // There is a class of bugs where an animation that should have finished doesn't (maybe 1151f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang // it didn't start, or it didn't finish), and the list gets stuck pretty much forever. 1152f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang // Clearing the state here is in line with user expectation for 'refresh'. 1153f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang getAnimatedAdapter().clearAnimationState(); 1154f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang // possibly act on the now-cleared state 1155f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang mActivity.onAnimationEnd(mListAdapter); 11564bb435caccc8ac148ca925813da601d9368d5066Mindy Pereira } 1157343cf45d9913daed981da891538934995d0dd83fJin Cao 1158343cf45d9913daed981da891538934995d0dd83fJin Cao /** 11598bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao * Extracted function that handles Analytics state and logging updates for each new cursor 11608bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao * @param newCursor the new cursor pointer 1161343cf45d9913daed981da891538934995d0dd83fJin Cao */ 1162343cf45d9913daed981da891538934995d0dd83fJin Cao private void updateAnalyticsData(ConversationCursor newCursor) { 11638bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao if (newCursor != null) { 11648bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao // Check if the initial data returned yet 11658bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao if (mInitialCursorLoading) { 11668bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao // This marks the very first time the cursor with the data the user sees returned. 11678bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao // We either have a cursor in LOADING state with cursor's count > 0, OR the cursor 11688bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao // completed loading. 11698bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao // Use this point to log the appropriate timing information that depends on when 11708bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao // the conversation list view finishes loading 117152da8ba2b62cd3479be1ed9d447cb3b64d388395Jin Cao if (isCursorReadyToShow()) { 11728bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao if (newCursor.getCount() == 0) { 11738bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao Analytics.getInstance().sendEvent("empty_state", "post_label_change", 11748bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao mFolder.getTypeDescription(), 0); 11758bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao } 11768bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao AnalyticsTimer.getInstance().logDuration(AnalyticsTimer.COLD_START_LAUNCHER, 11778bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao true /* isDestructive */, "cold_start_to_list", "from_launcher", null); 11788bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao // Don't need null checks because the activity, controller, and folder cannot 11798bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao // be null in this case 11808bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao if (mActivity.getFolderController().getFolder().isSearch()) { 11818bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao AnalyticsTimer.getInstance().logDuration(AnalyticsTimer.SEARCH_TO_LIST, 11828bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao true /* isDestructive */, "search_to_list", null, null); 11838bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao } 11848bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao 11858bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao mInitialCursorLoading = false; 11868bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao } 11878bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao } else { 11888bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao // Log the appropriate events that happen after the initial cursor is loaded 11898bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao if (newCursor.getCount() == 0 && mConversationCursorLastCount > 0) { 11908bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao Analytics.getInstance().sendEvent("empty_state", "post_delete", 11918bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao mFolder.getTypeDescription(), 0); 11928bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao } 1193343cf45d9913daed981da891538934995d0dd83fJin Cao } 1194faa9198087c44f2c46af40c8030b26d69617215dJin Cao 11958bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao // We save the count here because for folders that are empty, multiple successful 11968bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao // cursor loads will occur with size of 0. Thus we don't want to emit any false 11978bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao // positive post_delete events. 11988bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao mConversationCursorLastCount = newCursor.getCount(); 11998bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao } else { 12008bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao mConversationCursorLastCount = 0; 12018bf9c21a6e89e812621fe2d46c799651f9677784Jin Cao } 1202343cf45d9913daed981da891538934995d0dd83fJin Cao } 1203fa934276f5d1d5432ac96abd7308c3dd7f3e6d4bJin Cao 1204fa934276f5d1d5432ac96abd7308c3dd7f3e6d4bJin Cao /** 120552da8ba2b62cd3479be1ed9d447cb3b64d388395Jin Cao * Helper function to determine if the current cursor is ready to populate the UI 1206bc29ed3a5fe96ac144d5157150551febfb4d1355Jin Cao * Since we extracted the functionality into a static function in ConversationCursor, 1207bc29ed3a5fe96ac144d5157150551febfb4d1355Jin Cao * this function remains for the sole purpose of readability. 1208fa934276f5d1d5432ac96abd7308c3dd7f3e6d4bJin Cao * @return 1209fa934276f5d1d5432ac96abd7308c3dd7f3e6d4bJin Cao */ 121052da8ba2b62cd3479be1ed9d447cb3b64d388395Jin Cao private boolean isCursorReadyToShow() { 1211bc29ed3a5fe96ac144d5157150551febfb4d1355Jin Cao return ConversationCursor.isCursorReadyToShow(getConversationListCursor()); 1212fa934276f5d1d5432ac96abd7308c3dd7f3e6d4bJin Cao } 12135b317924c7c764954870501d99bfc57a243f6319Jin Cao 12145b317924c7c764954870501d99bfc57a243f6319Jin Cao public ListView getListView() { 12155b317924c7c764954870501d99bfc57a243f6319Jin Cao return mListView; 12165b317924c7c764954870501d99bfc57a243f6319Jin Cao } 12175b317924c7c764954870501d99bfc57a243f6319Jin Cao 12185b317924c7c764954870501d99bfc57a243f6319Jin Cao public void setNextFocusLeftId(@IdRes int id) { 12195b317924c7c764954870501d99bfc57a243f6319Jin Cao mNextFocusLeftId = id; 12205b317924c7c764954870501d99bfc57a243f6319Jin Cao if (mListView != null) { 12215b317924c7c764954870501d99bfc57a243f6319Jin Cao mListView.setNextFocusLeftId(mNextFocusLeftId); 12225b317924c7c764954870501d99bfc57a243f6319Jin Cao } 12235b317924c7c764954870501d99bfc57a243f6319Jin Cao } 1224b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal} 1225