14a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal/*******************************************************************************
24a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal *      Copyright (C) 2012 Google Inc.
34a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal *      Licensed to The Android Open Source Project.
44a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal *
54a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal *      Licensed under the Apache License, Version 2.0 (the "License");
64a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal *      you may not use this file except in compliance with the License.
74a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal *      You may obtain a copy of the License at
84a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal *
94a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal *           http://www.apache.org/licenses/LICENSE-2.0
104a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal *
114a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal *      Unless required by applicable law or agreed to in writing, software
124a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal *      distributed under the License is distributed on an "AS IS" BASIS,
134a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal *      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
144a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal *      See the License for the specific language governing permissions and
154a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal *      limitations under the License.
164a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal *******************************************************************************/
174a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal
184a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwalpackage com.android.mail.ui;
194a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal
2012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huangimport android.animation.ValueAnimator;
21a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwalimport android.app.Activity;
2254452ae7a9cdb607ed1db5e74077b5ebd95c46a2Vikram Aggarwalimport android.app.AlertDialog;
23a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwalimport android.app.Dialog;
2400179f19e413461498599ab7e6eb6914cc5c3671Andrew Sappersteinimport android.app.DialogFragment;
256902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwalimport android.app.Fragment;
262d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrookimport android.app.FragmentManager;
27f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huangimport android.app.LoaderManager;
28e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwalimport android.app.SearchManager;
29839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huangimport android.content.ContentProviderOperation;
3080aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwalimport android.content.ContentResolver;
316c2663dc9cade8aff34c462d244e3d72c475c664Mindy Pereiraimport android.content.ContentValues;
32a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwalimport android.content.Context;
3354452ae7a9cdb607ed1db5e74077b5ebd95c46a2Vikram Aggarwalimport android.content.DialogInterface;
346cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwalimport android.content.DialogInterface.OnClickListener;
35a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwalimport android.content.Intent;
367dedb952c8bb9c7b50f25444a42047d903e5121bVikram Aggarwalimport android.content.Loader;
3757246a4bf94e8b440c0597fa4a8e4f4e8a46532aPaul Westbrookimport android.content.res.Configuration;
38bcb16b98140a83a4db3c51568d21c839595f73dfVikram Aggarwalimport android.content.res.Resources;
39ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yangimport android.database.Cursor;
40632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport android.database.DataSetObservable;
41632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huangimport android.database.DataSetObserver;
4261f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huangimport android.database.Observable;
4323b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrookimport android.net.Uri;
4427d89ada3e8d1b17357a7064e1f47f3c15686412Vikram Aggarwalimport android.os.AsyncTask;
45a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwalimport android.os.Bundle;
4621ab490d4919985d611bc36ae2d8aea86a911499Mindy Pereiraimport android.os.Handler;
47f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedyimport android.os.Parcelable;
481a864cce6321f46031d6cd43e205278d0f219f92Jin Caoimport android.os.SystemClock;
49e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwalimport android.provider.SearchRecentSuggestions;
5012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huangimport android.support.v4.app.ActionBarDrawerToggle;
5112b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huangimport android.support.v4.widget.DrawerLayout;
5252882ff08fe9420fe2dbb3f681545a330222e9c8Andrew Sappersteinimport android.support.v7.app.ActionBar;
53acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereiraimport android.view.DragEvent;
5412b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huangimport android.view.Gravity;
55a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwalimport android.view.KeyEvent;
56a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwalimport android.view.Menu;
5728d5f72e0ad0db75b6243ee8125bfe3aadbdcb85Mindy Pereiraimport android.view.MenuInflater;
58a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwalimport android.view.MenuItem;
59a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwalimport android.view.MotionEvent;
60d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereiraimport android.view.View;
6112b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huangimport android.widget.ListView;
62fd0c2974af56ad244006faab2651e054974a4b7bMindy Pereiraimport android.widget.Toast;
63a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal
64b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwalimport com.android.mail.ConversationListContext;
6559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwalimport com.android.mail.MailLogService;
66f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huangimport com.android.mail.R;
67761522c1ac7a8fa05acde714f0e26fde47f3415fAndy Huangimport com.android.mail.analytics.Analytics;
68779dd6012a6c5b4d71255085059e57d94eab1a3fJin Caoimport com.android.mail.analytics.AnalyticsTimer;
69761522c1ac7a8fa05acde714f0e26fde47f3415fAndy Huangimport com.android.mail.analytics.AnalyticsUtils;
706cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwalimport com.android.mail.browse.ConfirmDialogFragment;
71967ede63e059810d9d4d425c50111d606014c893Mindy Pereiraimport com.android.mail.browse.ConversationCursor;
727c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Huimport com.android.mail.browse.ConversationCursor.ConversationOperation;
73ca87de41285bde382a894b6cb2d13c112c5d7a2fmindypimport com.android.mail.browse.ConversationItemViewModel;
748812d3c50e35c4f2a02d29c35c76082c4ebec0cdAndrew Sappersteinimport com.android.mail.browse.ConversationMessage;
75bf232c3735f65b1a4746943e4a134e59e36f0bdePaul Westbrookimport com.android.mail.browse.ConversationPagerController;
767c9f6acdf2b992fecaf0b3ad0afe9ec30ec86f33Marc Blankimport com.android.mail.browse.SelectedConversationsActionMenu;
77991f453c8905ec683458f8b4ea0e2737832425d9Andy Huangimport com.android.mail.browse.SyncErrorDialogFragment;
7830c881a7d31203fc7b233abf4a0c1413de4cd517Jin Caoimport com.android.mail.browse.UndoCallback;
799b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereiraimport com.android.mail.compose.ComposeActivity;
80177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwalimport com.android.mail.content.CursorCreator;
81177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwalimport com.android.mail.content.ObjectCursor;
82177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwalimport com.android.mail.content.ObjectCursorLoader;
831ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwalimport com.android.mail.providers.Account;
849b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereiraimport com.android.mail.providers.Conversation;
85839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huangimport com.android.mail.providers.ConversationInfo;
864a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwalimport com.android.mail.providers.Folder;
877a5d95a08464fb82b0310c82f3c39bfb8b63539fVikram Aggarwalimport com.android.mail.providers.FolderWatcher;
88c2074c4442eacf5604dd3cecd06b363e8fc574dePaul Westbrookimport com.android.mail.providers.MailAppProvider;
89dac00547ce8d44a964e4510315168ee413e55a75Mindy Pereiraimport com.android.mail.providers.Settings;
90e620a7aca0ecdbc85989af80b7f5ae0fef3831ceVikram Aggarwalimport com.android.mail.providers.SuggestionsProvider;
9180aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwalimport com.android.mail.providers.UIProvider;
923cb28b5e1598f74dedbdb87b187d9bd7e132ff0fMindy Pereiraimport com.android.mail.providers.UIProvider.AccountCapabilities;
932388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrookimport com.android.mail.providers.UIProvider.AccountCursorExtraKeys;
940d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedyimport com.android.mail.providers.UIProvider.AutoAdvance;
95c9d59184da271d5a6974edb709e3b39a5a970fa7Mindy Pereiraimport com.android.mail.providers.UIProvider.ConversationColumns;
965109c516b5cac279b4f99271953a27dd9a4828d8Paul Westbrookimport com.android.mail.providers.UIProvider.ConversationOperations;
9754452ae7a9cdb607ed1db5e74077b5ebd95c46a2Vikram Aggarwalimport com.android.mail.providers.UIProvider.FolderCapabilities;
98a158ac8a41239300a2f50b38ed17167aeef4ff4bScott Kennedyimport com.android.mail.providers.UIProvider.FolderType;
99c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sappersteinimport com.android.mail.ui.ActionableToastBar.ActionClickedListener;
100839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huangimport com.android.mail.utils.ContentProviderTask;
101144bfe739b93afdee0a1700a34806b0b287e5887Andy Huangimport com.android.mail.utils.DrawIdler;
102b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrookimport com.android.mail.utils.LogTag;
1035cb0c3e684150d6d8ed4d9df4fb73a7606086aaaMindy Pereiraimport com.android.mail.utils.LogUtils;
10461f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huangimport com.android.mail.utils.MailObservable;
105cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedyimport com.android.mail.utils.NotificationActionUtils;
106fa131a2ff399fd1d544f759b063268fb4e8a3e70Vikram Aggarwalimport com.android.mail.utils.Utils;
10769a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwalimport com.android.mail.utils.VeiledAddressMatcher;
108ca08fc170ca3f04572af8999645d5fd9e0bdb725Paul Westbrookimport com.google.common.base.Objects;
10977eee625cd8c317c605acfd06cd3a7e22120a0fdPaul Westbrookimport com.google.common.collect.ImmutableList;
110acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereiraimport com.google.common.collect.Lists;
111167faa8584750e37781f22ad7e6d61003029f954Marc Blankimport com.google.common.collect.Sets;
1124a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal
113167faa8584750e37781f22ad7e6d61003029f954Marc Blankimport java.util.ArrayList;
1149e4ca7993213d296043d20fe9cf4e166b5d595e8Andy Huangimport java.util.Arrays;
115fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereiraimport java.util.Collection;
116839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huangimport java.util.Collections;
1178db7e407109532557718c6b8064792f2df7a073dMindy Pereiraimport java.util.HashMap;
118cc754b102ed65df9d637b4e0a03e645214ea3cccVikram Aggarwalimport java.util.List;
11923b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrookimport java.util.Set;
120bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blankimport java.util.TimerTask;
12123b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook
12223b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook
1234a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal/**
124161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira * This is an abstract implementation of the Activity Controller. This class
125161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira * knows how to respond to menu items, state changes, layout changes, etc. It
126161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira * weaves together the views and listeners, dispatching actions to the
127161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira * respective underlying classes.
128161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira * <p>
129161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira * Even though this class is abstract, it should provide default implementations
130161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira * for most, if not all the methods in the ActivityController interface. This
131161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira * makes the task of the subclasses easier: OnePaneActivityController and
132161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira * TwoPaneActivityController can be concise when the common functionality is in
133161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira * AbstractActivityController.
134161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira * </p>
135a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal * <p>
136161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira * In the Gmail codebase, this was called BaseActivityController
137161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira * </p>
1384a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal */
139ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sappersteinpublic abstract class AbstractActivityController implements ActivityController,
1405d79a913c8289de4ffc1b662b9bea967a7e65b0dJames Lemieux        EmptyFolderDialogFragment.EmptyFolderDialogFragmentListener, View.OnClickListener {
1411ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    // Keys for serialization of various information in Bundles.
142cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal    /** Tag for {@link #mAccount} */
1436c51158ad3269f157424e6c7bd488425c98da08fVikram Aggarwal    private static final String SAVED_ACCOUNT = "saved-account";
144cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal    /** Tag for {@link #mFolder} */
1455e478d2c4087b50764756b0edc95941cb6094f68Mindy Pereira    private static final String SAVED_FOLDER = "saved-folder";
146cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal    /** Tag for {@link #mCurrentConversation} */
14726f23fc8cc0d49bc0bea1eeec4c0c4742928f693Mindy Pereira    private static final String SAVED_CONVERSATION = "saved-conversation";
148cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal    /** Tag for {@link #mSelectedSet} */
149cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal    private static final String SAVED_SELECTED_SET = "saved-selected-set";
150649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal    /** Tag for {@link ActionableToastBar#getOperation()} */
151d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira    private static final String SAVED_TOAST_BAR_OP = "saved-toast-bar-op";
152649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal    /** Tag for {@link #mFolderListFolder} */
153649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal    private static final String SAVED_HIERARCHICAL_FOLDER = "saved-hierarchical-folder";
154f3341405c97ff6d27a0d5fa37f6031e1c395d161Vikram Aggarwal    /** Tag for {@link ConversationListContext#searchQuery} */
155f3341405c97ff6d27a0d5fa37f6031e1c395d161Vikram Aggarwal    private static final String SAVED_QUERY = "saved-query";
1566cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal    /** Tag for {@link #mDialogAction} */
1576cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal    private static final String SAVED_ACTION = "saved-action";
158b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal    /** Tag for {@link #mDialogFromSelectedSet} */
159b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal    private static final String SAVED_ACTION_FROM_SELECTED = "saved-action-from-selected";
160a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal    /** Tag for {@link #mDetachedConvUri} */
161a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal    private static final String SAVED_DETACHED_CONV_URI = "saved-detached-conv-uri";
162ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang    /** Key to store {@link #mInbox}. */
163f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy    private static final String SAVED_INBOX_KEY = "m-inbox";
164f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy    /** Key to store {@link #mConversationListScrollPositions} */
165f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy    private static final String SAVED_CONVERSATION_LIST_SCROLL_POSITIONS =
166f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy            "saved-conversation-list-scroll-positions";
1675cb0c3e684150d6d8ed4d9df4fb73a7606086aaaMindy Pereira
168ede2e52e725abc625ae963f979639ff8ecc40cf5Greg Bullock    /** Tag used when loading a wait fragment */
1696902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal    protected static final String TAG_WAIT = "wait-fragment";
1706902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal    /** Tag used when loading a conversation list fragment. */
171bf232c3735f65b1a4746943e4a134e59e36f0bdePaul Westbrook    public static final String TAG_CONVERSATION_LIST = "tag-conversation-list";
172103319aaed26bce257de55b2fe93d4f78d3c59b9Scott Kennedy    /** Tag used when loading a custom fragment. */
173103319aaed26bce257de55b2fe93d4f78d3c59b9Scott Kennedy    protected static final String TAG_CUSTOM_FRAGMENT = "tag-custom-fragment";
1746902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal
175a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal    /** Key to store an account in a bundle */
176a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal    private final String BUNDLE_ACCOUNT_KEY = "account";
177a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal    /** Key to store a folder in a bundle */
178a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal    private final String BUNDLE_FOLDER_KEY = "folder";
1795bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein    /**
1805bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein     * Key to set a flag for the ConversationCursorLoader to ignore any
1815bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein     * initial load limit that may be set by the Account. Instead,
1825bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein     * perform a full load instead of the full-stage load.
1835bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein     */
1845bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein    private final String BUNDLE_IGNORE_INITIAL_CONVERSATION_LIMIT_KEY =
1855bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein            "ignore-initial-conversation-limit";
186a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal
187d7a12cd5b5c2639b653d8c671b04844ef02ee01dVikram Aggarwal    protected Account mAccount;
18812a4d8040d64501aa5a24c25865c806331560b74Mindy Pereira    protected Folder mFolder;
189ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang    protected Folder mInbox;
19069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal    /** True when {@link #mFolder} is first shown to the user. */
19169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal    private boolean mFolderChanged = false;
1922d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein    protected ActionBarController mActionBarController;
19310fcd645f703799326da326630b663df69f91508James Lemieux    protected final MailActivity mActivity;
194a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    protected final Context mContext;
1956902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal    private final FragmentManager mFragmentManager;
196ec5cbf79b825c6f96d45e85015319c66470b7e57Vikram Aggarwal    protected final RecentFolderList mRecentFolderList;
19780aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwal    protected ConversationListContext mConvListContext;
1989b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira    protected Conversation mCurrentConversation;
199a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal    /**
200a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal     * The hash of {@link #mCurrentConversation} in detached mode. 0 if we are not in detached mode.
201a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal     */
202a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal    private Uri mDetachedConvUri;
203d7a12cd5b5c2639b653d8c671b04844ef02ee01dVikram Aggarwal
204f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy    /** A map of {@link Folder} {@link Uri} to scroll position in the conversation list. */
205f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy    private final Bundle mConversationListScrollPositions = new Bundle();
206f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy
2076ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook    /** A {@link android.content.BroadcastReceiver} that suppresses new e-mail notifications. */
2086ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook    private SuppressNotificationReceiver mNewEmailReceiver = null;
2096ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook
21059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal    /** Handler for all our local runnables. */
211fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira    protected Handler mHandler = new Handler();
212fa995b4bbccd0e650ef48878107ede5e53dd8309Mindy Pereira
213fa131a2ff399fd1d544f759b063268fb4e8a3e70Vikram Aggarwal    /**
214161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * The current mode of the application. All changes in mode are initiated by
215161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * the activity controller. View mode changes are propagated to classes that
216161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * attach themselves as listeners of view mode changes.
217fa131a2ff399fd1d544f759b063268fb4e8a3e70Vikram Aggarwal     */
2181ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    protected final ViewMode mViewMode;
21980aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwal    protected ContentResolver mResolver;
220c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal    protected boolean mHaveAccountList = false;
221b7b33e07325fc6664d8ab803b141b87adaa2d013Mindy Pereira    private AsyncRefreshTask mAsyncRefreshTask;
2225cb0c3e684150d6d8ed4d9df4fb73a7606086aaaMindy Pereira
2234e0158f03fd8f430e88394e8b99e1b65ed01838aAndy Huang    private boolean mDestroyed;
2244e0158f03fd8f430e88394e8b99e1b65ed01838aAndy Huang
225bcb16b98140a83a4db3c51568d21c839595f73dfVikram Aggarwal    /** True if running on tablet */
226bcb16b98140a83a4db3c51568d21c839595f73dfVikram Aggarwal    private final boolean mIsTablet;
227bcb16b98140a83a4db3c51568d21c839595f73dfVikram Aggarwal
2281ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    /**
2291ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang     * Are we in a point in the Activity/Fragment lifecycle where it's safe to execute fragment
2301ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang     * transactions? (including back stack manipulation)
2311ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang     * <p>
2321ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang     * Per docs in {@link FragmentManager#beginTransaction()}, this flag starts out true, switches
2331ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang     * to false after {@link Activity#onSaveInstanceState}, and becomes true again in both onStart
2341ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang     * and onResume.
2351ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang     */
2361ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    private boolean mSafeToModifyFragments = true;
2371ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang
23823b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook    private final Set<Uri> mCurrentAccountUris = Sets.newHashSet();
239967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    protected ConversationCursor mConversationListCursor;
24061f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang    private final DataSetObservable mConversationListObservable = new MailObservable("List");
241bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank
24259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal    /** Runnable that checks the logging level to enable/disable the logging service. */
24359f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal    private Runnable mLogServiceChecker = null;
244de60c9d0ad164a5daf358181a05bbd43edd5d5edVikram Aggarwal    /** List of all accounts currently known to the controller. This is never null. */
245de60c9d0ad164a5daf358181a05bbd43edd5d5edVikram Aggarwal    private Account[] mAllAccounts = new Account[0];
24659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal
24777ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal    private FolderWatcher mFolderWatcher;
24877ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal
2495bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein    private boolean mIgnoreInitialConversationLimit;
2505bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein
2517c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu    /**
2527c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu     * Interface for actions that are deferred until after a load completes. This is for handling
2537c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu     * user actions which affect cursors (e.g. marking messages read or unread) that happen before
2547c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu     * that cursor is loaded.
2557c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu     */
2567c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu    private interface LoadFinishedCallback {
2577c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu        void onLoadFinished();
2587c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu    }
2597c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu
2607c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu    /** The deferred actions to execute when mConversationListCursor load completes. */
2617c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu    private final ArrayList<LoadFinishedCallback> mConversationListLoadFinishedCallbacks =
2627c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu            new ArrayList<LoadFinishedCallback>();
2637c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu
264bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    private RefreshTimerTask mConversationListRefreshTask;
265e1d1b07cdb0026097eb80f6c2912a16353aacec1Marc Blank
2667c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal    /** Listeners that are interested in changes to the current account. */
26761f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang    private final DataSetObservable mAccountObservers = new MailObservable("Account");
26858cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal    /** Listeners that are interested in changes to the recent folders. */
26961f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang    private final DataSetObservable mRecentFolderObservers = new MailObservable("RecentFolder");
27007dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal    /** Listeners that are interested in changes to the list of all accounts. */
27161f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang    private final DataSetObservable mAllAccountObservers = new MailObservable("AllAccounts");
27250ff0e50f1a27144e55e4184f48e433439727e7bVikram Aggarwal    /** Listeners that are interested in changes to the current folder. */
27361f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang    private final DataSetObservable mFolderObservable = new MailObservable("CurrentFolder");
27454022ee4c98f02401ca567e643f24ed42aebe203Tony Mantler    /** Listeners that are interested in changes to the Folder or Account selection */
27554022ee4c98f02401ca567e643f24ed42aebe203Tony Mantler    private final DataSetObservable mFolderOrAccountObservers =
27654022ee4c98f02401ca567e643f24ed42aebe203Tony Mantler            new MailObservable("FolderOrAccount");
27758cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal
278967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    /**
279967ede63e059810d9d4d425c50111d606014c893Mindy Pereira     * Selected conversations, if any.
280967ede63e059810d9d4d425c50111d606014c893Mindy Pereira     */
2814556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang    private final ConversationSelectionSet mSelectedSet = new ConversationSelectionSet();
282dac00547ce8d44a964e4510315168ee413e55a75Mindy Pereira
283c7a070f90a47ea658c36185c65ba8dcc7f2b09b9Paul Westbrook    private final int mFolderItemUpdateDelayMs;
284c7a070f90a47ea658c36185c65ba8dcc7f2b09b9Paul Westbrook
285135fd02e74ea8449b36c3dc44cd796e06a7e714dVikram Aggarwal    /** Keeps track of selected and unselected conversations */
286937c94f025ed55c19bd46c7c077fa3575ab6be52Paul Westbrook    final protected ConversationPositionTracker mTracker;
287af1ee0c4b6258530d8da30bcdb2a9cb403f70d5eVikram Aggarwal
288e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal    /**
289e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal     * Action menu associated with the selected set.
290e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal     */
291e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal    SelectedConversationsActionMenu mCabActionMenu;
2921a1e9754cce66296ae730f2c483f55fcf1a24359James Lemieux
2931a1e9754cce66296ae730f2c483f55fcf1a24359James Lemieux    /** The compose button floating over the conversation/search lists */
2941a1e9754cce66296ae730f2c483f55fcf1a24359James Lemieux    protected View mFloatingComposeButton;
295c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein    protected ActionableToastBar mToastBar;
296632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    protected ConversationPagerController mPagerController;
297ab4863650794e81f35a0fd9abc16a5564d7b102bMindy Pereira
298d70fe49cabf896e87574bcb2e57e8a32608537beVikram Aggarwal    // This is split out from the general loader dispatcher because its loader doesn't return a
299b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang    // basic Cursor
300d70fe49cabf896e87574bcb2e57e8a32608537beVikram Aggarwal    /** Handles loader callbacks to create a convesation cursor. */
301b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang    private final ConversationListLoaderCallbacks mListCursorCallbacks =
302b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang            new ConversationListLoaderCallbacks();
303b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang
304177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    /** Object that listens to all LoaderCallbacks that result in {@link Folder} creation. */
305177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    private final FolderLoads mFolderCallbacks = new FolderLoads();
306177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    /** Object that listens to all LoaderCallbacks that result in {@link Account} creation. */
307177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    private final AccountLoads mAccountCallbacks = new AccountLoads();
308177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal
30969a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    /**
31069a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     * Matched addresses that must be shielded from users because they are temporary. Even though
31169a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     * this is instantiated from settings, this matcher is valid for all accounts, and is expected
31269a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     * to live past the life of an account.
31369a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     */
31469a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    private final VeiledAddressMatcher mVeiledMatcher;
31569a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal
316b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrook    protected static final String LOG_TAG = LogTag.getLogTag();
317d70fe49cabf896e87574bcb2e57e8a32608537beVikram Aggarwal
318aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal    // Loader constants: Accounts
319aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal    /**
320aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * The list of accounts. This loader is started early in the application life-cycle since
321aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * the list of accounts is central to all other data the application needs: unread counts for
322aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * folders, critical UI settings like show/hide checkboxes, ...
323aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * The loader is started when the application is created: both in
324aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * {@link #onCreate(Bundle)} and in {@link #onActivityResult(int, int, Intent)}. It is never
325aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * destroyed since the cursor is needed through the life of the application. When the list of
326aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * accounts changes, we notify {@link #mAllAccountObservers}.
327aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     */
328fa4b47e4a1962010ec3c8ea7476ac1e701f461b4Vikram Aggarwal    private static final int LOADER_ACCOUNT_CURSOR = 0;
329aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal
330aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal    /**
331aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * The current account. This loader is started when we have an account. The mail application
332aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * <b>needs</b> a valid account to function. As soon as we set {@link #mAccount},
333aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * we start a loader to observe for changes on the current account.
334aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * The loader is always restarted when an account is set in {@link #setAccount(Account)}.
335aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * When the current account object changes, we notify {@link #mAccountObservers}.
336aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * A possible performance improvement would be to listen purely on
337aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * {@link #LOADER_ACCOUNT_CURSOR}. The current account is guaranteed to be in the list,
338aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * and would avoid two updates when a single setting on the current account changes.
339aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     */
340004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps    private static final int LOADER_ACCOUNT_UPDATE_CURSOR = 1;
341004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps
342004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps    // Loader constants: Conversations
343004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps
344004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps    /** The conversation cursor over the current conversation list. This loader provides
345004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps     * a cursor over conversation entries from a folder to display a conversation
346004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps     * list.
347004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps     * This loader is started when the user switches folders (in {@link #updateFolder(Folder)},
348004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps     * or when the controller is told that a folder/account change is imminent
349004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps     * (in {@link #preloadConvList(Account, Folder)}. The loader is maintained for the life of
350004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps     * the current folder. When the user switches folders, the old loader is destroyed and a new
351004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps     * one is created.
352004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps     *
353004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps     * When the conversation list changes, we notify {@link #mConversationListObservable}.
354004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps     */
355004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps    private static final int LOADER_CONVERSATION_LIST = 10;
356004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps
357004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps    // Loader constants: misc
358004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps    /**
359004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps     * The loader that determines whether the Warm welcome tour should be displayed for the user.
360004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps     */
361004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps    public static final int LOADER_WELCOME_TOUR = 20;
362d70fe49cabf896e87574bcb2e57e8a32608537beVikram Aggarwal
363944ef3b09d15d28f13cf63e3410850e246b1d54fGreg Bullock    /**
364944ef3b09d15d28f13cf63e3410850e246b1d54fGreg Bullock     * The load which loads accounts for the welcome tour.
365944ef3b09d15d28f13cf63e3410850e246b1d54fGreg Bullock     */
366944ef3b09d15d28f13cf63e3410850e246b1d54fGreg Bullock    public static final int LOADER_WELCOME_TOUR_ACCOUNTS = 21;
367944ef3b09d15d28f13cf63e3410850e246b1d54fGreg Bullock
368aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal    // Loader constants: Folders
369004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps
370aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal    /** The current folder. This loader watches for updates to the current folder in a manner
371aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * analogous to the {@link #LOADER_ACCOUNT_UPDATE_CURSOR}. Updates to the current folder
372aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * might be due to server-side changes (unread count), or local changes (sync window or sync
373aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * status change).
374aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * The change of current folder calls {@link #updateFolder(Folder)}.
375aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * This is responsible for restarting a loader using the URI of the provided folder. When the
376aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * loader returns, the current folder is updated and consumers, if any, are notified.
377aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * When the current folder changes, we notify {@link #mFolderObservable}
378aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     */
379004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps    private static final int LOADER_FOLDER_CURSOR = 30;
380004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps
381aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal    /**
382aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * The list of recent folders. Recent folders are shown in the DrawerFragment. The recent
383aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * folders are tied to the current account being viewed. When the account is changed,
384aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * we restart this loader to retrieve the recent accounts. Recents are pre-populated for
385aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * phones historically, when they were displayed in the spinner. On the tablet,
386aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * they showed in the {@link FolderListFragment} and were not-populated.  The code to
387aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * pre-populate the recents is somewhat convoluted: when the loader returns a short list of
388aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * recent folders, it issues an update on the Recent Folder URI. The underlying provider then
389aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * does the appropriate thing to populate recent folders, and notify of a change on the cursor.
390aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * Recent folders are needed for the life of the current account.
391aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * When the recent folders change, we notify {@link #mRecentFolderObservers}.
392aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     */
393004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps    private static final int LOADER_RECENT_FOLDERS = 31;
394aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal    /**
395aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * The primary inbox for the current account. The mechanism to load the default inbox for the
396aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * current account is (sadly) different from loading other folders. The method
397aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * {@link #loadAccountInbox()} is called, and it restarts this loader. When the loader returns
398aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * a valid cursor, we create a folder, call {@link #onFolderChanged{Folder)} eventually
399aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * calling {@link #updateFolder(Folder)} which starts a loader {@link #LOADER_FOLDER_CURSOR}
400aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * over the current folder.
401aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * When we have a valid cursor, we destroy this loader, This convoluted flow is historical.
402aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     */
403004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps    private static final int LOADER_ACCOUNT_INBOX = 32;
404004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps
405aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal    /**
406aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * The fake folder of search results for a term. When we search for a term,
407aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * a new activity is created with {@link Intent#ACTION_SEARCH}. For this new activity,
408aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * we start a loader which returns conversations that match the user-provided query.
409aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * We destroy the loader when we obtain a valid cursor since subsequent searches will create
410aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * a new activity.
411aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     */
412004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps    private static final int LOADER_SEARCH = 33;
413aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal    /**
414aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * The initial folder at app start. When the application is launched from an intent that
415aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * specifies the initial folder (notifications/widgets/shortcuts),
416aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * then we extract the folder URI from the intent, but we cannot trust the folder object. Since
417aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * shortcuts and widgets persist past application update, they might have incorrect
418aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * information encoded in them. So, to obtain a {@link Folder} object from a {@link Uri},
419aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * we need to start another loader. Upon obtaining a valid cursor, the loader is destroyed.
420aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * An additional complication arises if we have to view a specific conversation within this
421aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * folder. This is the case when launching the app from a single conversation notification
422aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * or tapping on a specific conversation in the widget. In these cases, the conversation is
423aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * saved in {@link #mConversationToShow} and is retrieved when the loader returns.
424aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     */
425004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps    public static final int LOADER_FIRST_FOLDER = 34;
426de21e898901a86acdcf62ea46d358184bfd063b3Régis Décamps
427de21e898901a86acdcf62ea46d358184bfd063b3Régis Décamps    /**
4287a5d95a08464fb82b0310c82f3c39bfb8b63539fVikram Aggarwal     * Guaranteed to be the last loader ID used by the activity. Loaders are owned by Activity or
4297a5d95a08464fb82b0310c82f3c39bfb8b63539fVikram Aggarwal     * fragments, and within an activity, loader IDs need to be unique. A hack to ensure that the
4307a5d95a08464fb82b0310c82f3c39bfb8b63539fVikram Aggarwal     * {@link FolderWatcher} can create its folder loaders without clashing with the IDs of those
4317a5d95a08464fb82b0310c82f3c39bfb8b63539fVikram Aggarwal     * of the {@link AbstractActivityController}. Currently, the {@link FolderWatcher} is the only
4327a5d95a08464fb82b0310c82f3c39bfb8b63539fVikram Aggarwal     * other class that uses this activity's LoaderManager. If another class needs activity-level
4337a5d95a08464fb82b0310c82f3c39bfb8b63539fVikram Aggarwal     * loaders, consider consolidating the loaders in a central location: a UI-less fragment
4347a5d95a08464fb82b0310c82f3c39bfb8b63539fVikram Aggarwal     * perhaps.
4357a5d95a08464fb82b0310c82f3c39bfb8b63539fVikram Aggarwal     */
436004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps    public static final int LAST_LOADER_ID = 35;
437004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps
4387c8325de829ca029ce4547e4f0fa266124301367Scott Kennedy    /**
4397c8325de829ca029ce4547e4f0fa266124301367Scott Kennedy     * Guaranteed to be the last loader ID used by the Fragment. Loaders are owned by Activity or
4407c8325de829ca029ce4547e4f0fa266124301367Scott Kennedy     * fragments, and within an activity, loader IDs need to be unique. Currently,
4412d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein     * SectionedInboxTeaserView is the only class that uses the
4427c8325de829ca029ce4547e4f0fa266124301367Scott Kennedy     * {@link ConversationListFragment}'s LoaderManager.
4437c8325de829ca029ce4547e4f0fa266124301367Scott Kennedy     */
4447c8325de829ca029ce4547e4f0fa266124301367Scott Kennedy    public static final int LAST_FRAGMENT_LOADER_ID = 1000;
4454a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal
4466aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal    /** Code returned after an account has been added. */
4472388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook    private static final int ADD_ACCOUNT_REQUEST_CODE = 1;
4486aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal    /** Code returned when the user has to enter the new password on an existing account. */
449122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook    private static final int REAUTHENTICATE_REQUEST_CODE = 2;
450371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon    /** Code returned when the previous activity needs to navigate to a different folder
451371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon     *  or account */
452371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon    private static final int CHANGE_NAVIGATION_REQUEST_CODE = 3;
453371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon
454371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon    public static final String EXTRA_FOLDER = "extra-folder";
455371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon    public static final String EXTRA_ACCOUNT = "extra-account";
4562388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook
457e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal    /** The pending destructive action to be carried out before swapping the conversation cursor.*/
458e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal    private DestructiveAction mPendingDestruction;
459c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein    protected AsyncRefreshTask mFolderSyncTask;
460c975e8416b64a1ebdec6adee7e65dd1d0928e8d4Mindy Pereira    private Folder mFolderListFolder;
4615390fcabb7baf683fc14bd68d8612913dac70a81mindyp    private boolean mIsDragHappening;
462e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon    private final int mShowUndoBarDelay;
4636f54e1b0e8beae2e624852f9f2f2b1b38745e3bemindyp    private boolean mRecentsDataUpdated;
464a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal    /** A wait fragment we added, if any. */
465a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal    private WaitFragment mWaitFragment;
466f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal    /** True if we have results from a search query */
467f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal    private boolean mHaveSearchResults = false;
4686cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal    /** If a confirmation dialog is being show, the listener for the positive action. */
4696cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal    private OnClickListener mDialogListener;
4706cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal    /**
4716cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal     * If a confirmation dialog is being show, the resource of the action: R.id.delete, etc.  This
4726cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal     * is used to create a new {@link #mDialogListener} on orientation changes.
4736cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal     */
4746cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal    private int mDialogAction = -1;
475b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal    /**
476b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal     * If a confirmation dialog is being shown, this is true if the dialog acts on the selected set
477b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal     * and false if it acts on the currently selected conversation
478b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal     */
479b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal    private boolean mDialogFromSelectedSet;
4806cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal
481177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    /** Which conversation to show, if started from widget/notification. */
482177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    private Conversation mConversationToShow = null;
483177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal
484c94d07f4460d00db898237603de311d226707a96Andy Huang    /**
485c94d07f4460d00db898237603de311d226707a96Andy Huang     * A temporary reference to the pending destructive action that was deferred due to an
486c94d07f4460d00db898237603de311d226707a96Andy Huang     * auto-advance transition in progress.
487c94d07f4460d00db898237603de311d226707a96Andy Huang     * <p>
488c94d07f4460d00db898237603de311d226707a96Andy Huang     * In detail: when auto-advance triggers a mode change, we must wait until the transition
489c94d07f4460d00db898237603de311d226707a96Andy Huang     * completes before executing the destructive action to ensure a smooth mode change transition.
490c94d07f4460d00db898237603de311d226707a96Andy Huang     * This member variable houses the pending destructive action work to be run upon completion.
491c94d07f4460d00db898237603de311d226707a96Andy Huang     */
492c94d07f4460d00db898237603de311d226707a96Andy Huang    private Runnable mAutoAdvanceOp = null;
493c94d07f4460d00db898237603de311d226707a96Andy Huang
49412b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang    protected DrawerLayout mDrawerContainer;
49512b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang    protected View mDrawerPullout;
49612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang    protected ActionBarDrawerToggle mDrawerToggle;
497f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang
49812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang    protected ListView mListViewForAnimating;
49912b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang    protected boolean mHasNewAccountOrFolder;
500a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein    private boolean mConversationListLoadFinishedIgnored;
50161f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang    private final MailDrawerListener mDrawerListener = new MailDrawerListener();
5025747e152fa606c20261b18431f4a9f0ff3be7b5aAndrew Sapperstein    private boolean mHideMenuItems;
50312b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang
504144bfe739b93afdee0a1700a34806b0b287e5887Andy Huang    private final DrawIdler mDrawIdler = new DrawIdler();
505144bfe739b93afdee0a1700a34806b0b287e5887Andy Huang
50600179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein    public static final String SYNC_ERROR_DIALOG_FRAGMENT_TAG = "SyncErrorDialogFragment";
507e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal
508cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedy    private final DataSetObserver mUndoNotificationObserver = new DataSetObserver() {
509cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedy        @Override
510cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedy        public void onChanged() {
511cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedy            super.onChanged();
512cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedy
513cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedy            if (mConversationListCursor != null) {
514cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedy                mConversationListCursor.handleNotificationActions();
515cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedy            }
516cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedy        }
517cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedy    };
518cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedy
51953de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein    private final HomeButtonListener mHomeButtonListener = new HomeButtonListener();
52053de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein
521a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    public AbstractActivityController(MailActivity activity, ViewMode viewMode) {
522a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal        mActivity = activity;
5236902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal        mFragmentManager = mActivity.getFragmentManager();
524a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal        mViewMode = viewMode;
525a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal        mContext = activity.getApplicationContext();
526025eba8bfd4d0b5e248b6de0bda6f1129170fb41Vikram Aggarwal        mRecentFolderList = new RecentFolderList(mContext);
527937c94f025ed55c19bd46c7c077fa3575ab6be52Paul Westbrook        mTracker = new ConversationPositionTracker(this);
528967ede63e059810d9d4d425c50111d606014c893Mindy Pereira        // Allow the fragment to observe changes to its own selection set. No other object is
529967ede63e059810d9d4d425c50111d606014c893Mindy Pereira        // aware of the selected set.
530967ede63e059810d9d4d425c50111d606014c893Mindy Pereira        mSelectedSet.addObserver(this);
531c7a070f90a47ea658c36185c65ba8dcc7f2b09b9Paul Westbrook
532bcb16b98140a83a4db3c51568d21c839595f73dfVikram Aggarwal        final Resources r = mContext.getResources();
533bcb16b98140a83a4db3c51568d21c839595f73dfVikram Aggarwal        mFolderItemUpdateDelayMs = r.getInteger(R.integer.folder_item_refresh_delay_ms);
534bcb16b98140a83a4db3c51568d21c839595f73dfVikram Aggarwal        mShowUndoBarDelay = r.getInteger(R.integer.show_undo_bar_delay_ms);
53569a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal        mVeiledMatcher = VeiledAddressMatcher.newInstance(activity.getResources());
536bcb16b98140a83a4db3c51568d21c839595f73dfVikram Aggarwal        mIsTablet = Utils.useTabletUI(r);
537a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein        mConversationListLoadFinishedIgnored = false;
5384a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal    }
5394a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal
5404a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal    @Override
541e9a81035bcef5a372fa13a992f8553eb60e6aaf0Vikram Aggarwal    public Account getCurrentAccount() {
542e9a81035bcef5a372fa13a992f8553eb60e6aaf0Vikram Aggarwal        return mAccount;
5434a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal    }
5444a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal
5454a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal    @Override
5461ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    public ConversationListContext getCurrentListContext() {
547e9a81035bcef5a372fa13a992f8553eb60e6aaf0Vikram Aggarwal        return mConvListContext;
5484a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal    }
5494a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal
550a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    @Override
551ef7c992d8cb462be6da34d42965cf185f3bb1b22Vikram Aggarwal    public final ConversationCursor getConversationListCursor() {
552967ede63e059810d9d4d425c50111d606014c893Mindy Pereira        return mConversationListCursor;
553967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    }
554967ede63e059810d9d4d425c50111d606014c893Mindy Pereira
5556902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal    /**
55634a650154646f550435b7976d2ac6fc74c21234fVikram Aggarwal     * Check if the fragment is attached to an activity and has a root view.
557715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @param in fragment to be checked
55834a650154646f550435b7976d2ac6fc74c21234fVikram Aggarwal     * @return true if the fragment is valid, false otherwise
55934a650154646f550435b7976d2ac6fc74c21234fVikram Aggarwal     */
560715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal    private static boolean isValidFragment(Fragment in) {
561715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal        return !(in == null || in.getActivity() == null || in.getView() == null);
56234a650154646f550435b7976d2ac6fc74c21234fVikram Aggarwal    }
56334a650154646f550435b7976d2ac6fc74c21234fVikram Aggarwal
56434a650154646f550435b7976d2ac6fc74c21234fVikram Aggarwal    /**
56549e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * Get the conversation list fragment for this activity. If the conversation list fragment is
56649e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * not attached, this method returns null.
567839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang     *
56849e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * Caution! This method returns the {@link ConversationListFragment} after the fragment has been
56949e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * added, <b>and</b> after the {@link FragmentManager} has run through its queue to add the
57049e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * fragment. There is a non-trivial amount of time after the fragment is instantiated and before
57149e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * this call returns a non-null value, depending on the {@link FragmentManager}. If you
57249e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * need the fragment immediately after adding it, consider making the fragment an observer of
57349e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * the controller and perform the task immediately on {@link Fragment#onActivityCreated(Bundle)}
5746902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal     */
5756902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal    protected ConversationListFragment getConversationListFragment() {
5766902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal        final Fragment fragment = mFragmentManager.findFragmentByTag(TAG_CONVERSATION_LIST);
57734a650154646f550435b7976d2ac6fc74c21234fVikram Aggarwal        if (isValidFragment(fragment)) {
57834a650154646f550435b7976d2ac6fc74c21234fVikram Aggarwal            return (ConversationListFragment) fragment;
5799585d7810519b7e5475d9c7458e7a8a9c12fd024Andy Huang        }
58034a650154646f550435b7976d2ac6fc74c21234fVikram Aggarwal        return null;
5816902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal    }
5826902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal
5836902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal    /**
5846902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal     * Returns the folder list fragment attached with this activity. If no such fragment is attached
5856902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal     * this method returns null.
586839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang     *
58749e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * Caution! This method returns the {@link FolderListFragment} after the fragment has been
58849e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * added, <b>and</b> after the {@link FragmentManager} has run through its queue to add the
58949e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * fragment. There is a non-trivial amount of time after the fragment is instantiated and before
59049e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * this call returns a non-null value, depending on the {@link FragmentManager}. If you
59149e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * need the fragment immediately after adding it, consider making the fragment an observer of
59249e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * the controller and perform the task immediately on {@link Fragment#onActivityCreated(Bundle)}
5936902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal     */
5946902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal    protected FolderListFragment getFolderListFragment() {
59510fcd645f703799326da326630b663df69f91508James Lemieux        final String drawerPulloutTag = mActivity.getString(R.string.drawer_pullout_tag);
59610fcd645f703799326da326630b663df69f91508James Lemieux        final Fragment fragment = mFragmentManager.findFragmentByTag(drawerPulloutTag);
59734a650154646f550435b7976d2ac6fc74c21234fVikram Aggarwal        if (isValidFragment(fragment)) {
59834a650154646f550435b7976d2ac6fc74c21234fVikram Aggarwal            return (FolderListFragment) fragment;
5999585d7810519b7e5475d9c7458e7a8a9c12fd024Andy Huang        }
60034a650154646f550435b7976d2ac6fc74c21234fVikram Aggarwal        return null;
6016902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal    }
6026902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal
6031ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    /**
604161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * Initialize the action bar. This is not visible to OnePaneController and
605161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * TwoPaneController so they cannot override this behavior.
6061ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal     */
607abd24d82e41fd4747945d3f124b4e3a57e5dc6e6Vikram Aggarwal    private void initializeActionBar() {
60852882ff08fe9420fe2dbb3f681545a330222e9c8Andrew Sapperstein        final ActionBar actionBar = mActivity.getSupportActionBar();
6095895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang        if (actionBar == null) {
6105895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang            return;
611abd24d82e41fd4747945d3f124b4e3a57e5dc6e6Vikram Aggarwal        }
6125895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang
61382faec7ee6cfc4e68fdfe7658d38dfeb5c9c1843Mindy Pereira        final boolean isSearch = mActivity.getIntent() != null
61482faec7ee6cfc4e68fdfe7658d38dfeb5c9c1843Mindy Pereira                && Intent.ACTION_SEARCH.equals(mActivity.getIntent().getAction());
6152d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein        mActionBarController = isSearch ?
6162d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein                new SearchActionBarController(mContext) :
6172d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein                new ActionBarController(mContext);
6182d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein        mActionBarController.initialize(mActivity, this, actionBar);
6191dd054f4e5581b69a52936c9d257fa91cb6daee7Rohan Shah
62012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        // init the action bar to allow the 'up' affordance.
62112b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        // any configurations that disallow 'up' should do that later.
6222d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein        mActionBarController.setBackButton();
623abd24d82e41fd4747945d3f124b4e3a57e5dc6e6Vikram Aggarwal    }
624abd24d82e41fd4747945d3f124b4e3a57e5dc6e6Vikram Aggarwal
625abd24d82e41fd4747945d3f124b4e3a57e5dc6e6Vikram Aggarwal    /**
626abd24d82e41fd4747945d3f124b4e3a57e5dc6e6Vikram Aggarwal     * Attach the action bar to the activity.
627abd24d82e41fd4747945d3f124b4e3a57e5dc6e6Vikram Aggarwal     */
628abd24d82e41fd4747945d3f124b4e3a57e5dc6e6Vikram Aggarwal    private void attachActionBar() {
62952882ff08fe9420fe2dbb3f681545a330222e9c8Andrew Sapperstein        final ActionBar actionBar = mActivity.getSupportActionBar();
630bd09150070b1aa25ebdd9af0a29e8bf9fbbf7af8Tony Mantler        if (actionBar != null) {
6312d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein            // Show a title
6322d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein            final int mask = ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_HOME;
633bc462cadc2dcd11d27e7810e437aeef1505f4cb2Vikram Aggarwal            actionBar.setDisplayOptions(mask, mask);
6342d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein            mActionBarController.setViewModeController(mViewMode);
6351ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal        }
636a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    }
637a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal
638a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    /**
639161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * Returns whether the conversation list fragment is visible or not.
640161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * Different layouts will have their own notion on the visibility of
641161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * fragments, so this method needs to be overriden.
642161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     *
643a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal     */
6441ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    protected abstract boolean isConversationListVisible();
6451ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal
646eb7d329e448eb42195f7c35b978035766b6f46e1Vikram Aggarwal    /**
64734f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal     * If required, starts wait mode for the current account.
64834f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal     */
64934f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal    final void perhapsEnterWaitMode() {
65034f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal        // If the account is not initialized, then show the wait fragment, since nothing can be
65134f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal        // shown.
65234f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal        if (mAccount.isAccountInitializationRequired()) {
65334f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal            showWaitForInitialization();
65434f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal            return;
65534f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal        }
65634f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal
65734f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal        final boolean inWaitingMode = inWaitMode();
65834f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal        final boolean isSyncRequired = mAccount.isAccountSyncRequired();
65934f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal        if (isSyncRequired) {
66034f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal            if (inWaitingMode) {
66134f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal                // Update the WaitFragment's account object
66234f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal                updateWaitMode();
66334f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal            } else {
66434f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal                // Transition to waiting mode
66534f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal                showWaitForInitialization();
66634f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal            }
66734f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal        } else if (inWaitingMode) {
66834f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal            // Dismiss waiting mode
66934f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal            hideWaitForInitialization();
67034f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal        }
67134f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal    }
67234f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal
673eb7d329e448eb42195f7c35b978035766b6f46e1Vikram Aggarwal    @Override
6744cbb0da3cd701ee0c06237ded965648d68c663c8Andrew Sapperstein    public void switchToDefaultInboxOrChangeAccount(Account account) {
6754cbb0da3cd701ee0c06237ded965648d68c663c8Andrew Sapperstein        LogUtils.d(LOG_TAG, "AAC.switchToDefaultAccount(%s)", account);
676e764cfd130c4f92ae823fd1f361ab70439e8ec95Andy Huang        if (mViewMode.isSearchMode()) {
677371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon            // We are in an activity on top of the main navigation activity.
678371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon            // We need to return to it with a result code that indicates it should navigate to
679371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon            // a different folder.
680371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon            final Intent intent = new Intent();
681371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon            intent.putExtra(AbstractActivityController.EXTRA_ACCOUNT, account);
682371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon            mActivity.setResult(Activity.RESULT_OK, intent);
683371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon            mActivity.finish();
684371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon            return;
685371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon        }
68666bc6ed16956963cb18660d15ea7e6adc6c62411Vikram Aggarwal        final boolean firstLoad = mAccount == null;
687252684a1ddaf00119d8f2440e5d4cd93c77e5298Andrew Sapperstein        final boolean switchToDefaultInbox = !firstLoad && account.uri.equals(mAccount.uri);
688d70fe49cabf896e87574bcb2e57e8a32608537beVikram Aggarwal        // If the active account has been clicked in the drawer, go to default inbox
689252684a1ddaf00119d8f2440e5d4cd93c77e5298Andrew Sapperstein        if (switchToDefaultInbox) {
690252684a1ddaf00119d8f2440e5d4cd93c77e5298Andrew Sapperstein            loadAccountInbox();
691252684a1ddaf00119d8f2440e5d4cd93c77e5298Andrew Sapperstein            return;
692252684a1ddaf00119d8f2440e5d4cd93c77e5298Andrew Sapperstein        }
6934cbb0da3cd701ee0c06237ded965648d68c663c8Andrew Sapperstein        changeAccount(account);
6944cbb0da3cd701ee0c06237ded965648d68c663c8Andrew Sapperstein    }
6954cbb0da3cd701ee0c06237ded965648d68c663c8Andrew Sapperstein
6964cbb0da3cd701ee0c06237ded965648d68c663c8Andrew Sapperstein    public void changeAccount(Account account) {
6974cbb0da3cd701ee0c06237ded965648d68c663c8Andrew Sapperstein        LogUtils.d(LOG_TAG, "AAC.changeAccount(%s)", account);
6984cbb0da3cd701ee0c06237ded965648d68c663c8Andrew Sapperstein        // Is the account or account settings different from the existing account?
6994cbb0da3cd701ee0c06237ded965648d68c663c8Andrew Sapperstein        final boolean firstLoad = mAccount == null;
7004cbb0da3cd701ee0c06237ded965648d68c663c8Andrew Sapperstein        final boolean accountChanged = firstLoad || !account.uri.equals(mAccount.uri);
7014cbb0da3cd701ee0c06237ded965648d68c663c8Andrew Sapperstein
702472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        // If nothing has changed, return early without wasting any more time.
703472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        if (!accountChanged && !account.settingsDiffer(mAccount)) {
704472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal            return;
705472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        }
706472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        // We also don't want to do anything if the new account is null
707472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        if (account == null) {
7085fd8afd5c1b40580c069a5a97039ca0601208216Vikram Aggarwal            LogUtils.e(LOG_TAG, "AAC.changeAccount(null) called.");
709472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal            return;
710472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        }
71179b1156803585952a4851221dc9fa9974e606433Tony Mantler        final String emailAddress = account.getEmailAddress();
712472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        mHandler.post(new Runnable() {
713472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal            @Override
714472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal            public void run() {
71579b1156803585952a4851221dc9fa9974e606433Tony Mantler                MailActivity.setNfcMessage(emailAddress);
716c6adce3cf6887c4c6dd5005724565702751843d0mindyp            }
717472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        });
718472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        if (accountChanged) {
719472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal            commitDestructiveActions(false);
720472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        }
721761522c1ac7a8fa05acde714f0e26fde47f3415fAndy Huang        Analytics.getInstance().setCustomDimension(Analytics.CD_INDEX_ACCOUNT_TYPE,
72279b1156803585952a4851221dc9fa9974e606433Tony Mantler                AnalyticsUtils.getAccountTypeForAccount(emailAddress));
723472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        // Change the account here
724472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        setAccount(account);
725472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        // And carry out associated actions.
726472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        cancelRefreshTask();
727472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        if (accountChanged) {
728472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal            loadAccountInbox();
729472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        }
730472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        // Check if we need to force setting up an account before proceeding.
731472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        if (mAccount != null && !Uri.EMPTY.equals(mAccount.settings.setupIntentUri)) {
732472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal            // Launch the intent!
733472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal            final Intent intent = new Intent(Intent.ACTION_EDIT);
734ec6e2516037e7172202e208b2015d7684a78eddbTony Mantler
735ec6e2516037e7172202e208b2015d7684a78eddbTony Mantler            intent.setPackage(mContext.getPackageName());
736472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal            intent.setData(mAccount.settings.setupIntentUri);
737ec6e2516037e7172202e208b2015d7684a78eddbTony Mantler
738472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal            mActivity.startActivity(intent);
73928d5f72e0ad0db75b6243ee8125bfe3aadbdcb85Mindy Pereira        }
740a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    }
741a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal
742e6340bc7f2bb9868e5014a101ea69b777d7b6750Vikram Aggarwal    /**
7437c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal     * Adds a listener interested in change in the current account. If a class is storing a
7447c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal     * reference to the current account, it should listen on changes, so it can receive updates to
7457c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal     * settings. Must happen in the UI thread.
7467d81600cbce3cfd366cbff9ecd1b7317ff957221Vikram Aggarwal     */
7477c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal    @Override
7487c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal    public void registerAccountObserver(DataSetObserver obs) {
7497c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal        mAccountObservers.registerObserver(obs);
7507d81600cbce3cfd366cbff9ecd1b7317ff957221Vikram Aggarwal    }
7517d81600cbce3cfd366cbff9ecd1b7317ff957221Vikram Aggarwal
7527d81600cbce3cfd366cbff9ecd1b7317ff957221Vikram Aggarwal    /**
7537c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal     * Removes a listener from receiving current account changes.
7547d81600cbce3cfd366cbff9ecd1b7317ff957221Vikram Aggarwal     * Must happen in the UI thread.
7557d81600cbce3cfd366cbff9ecd1b7317ff957221Vikram Aggarwal     */
7567c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal    @Override
7577c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal    public void unregisterAccountObserver(DataSetObserver obs) {
7587c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal        mAccountObservers.unregisterObserver(obs);
7597d81600cbce3cfd366cbff9ecd1b7317ff957221Vikram Aggarwal    }
7607d81600cbce3cfd366cbff9ecd1b7317ff957221Vikram Aggarwal
7617c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal    @Override
76207dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal    public void registerAllAccountObserver(DataSetObserver observer) {
76307dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal        mAllAccountObservers.registerObserver(observer);
76407dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal    }
76507dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal
76607dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal    @Override
76707dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal    public void unregisterAllAccountObserver(DataSetObserver observer) {
76807dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal        mAllAccountObservers.unregisterObserver(observer);
76907dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal    }
77007dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal
77107dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal    @Override
77207dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal    public Account[] getAllAccounts() {
77307dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal        return mAllAccounts;
77407dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal    }
77507dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal
77607dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal    @Override
7777c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal    public Account getAccount() {
7787c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal        return mAccount;
7797d81600cbce3cfd366cbff9ecd1b7317ff957221Vikram Aggarwal    }
7807d81600cbce3cfd366cbff9ecd1b7317ff957221Vikram Aggarwal
7810f73d905c5e935a5280f8eb647cc924c2854b7deRohan Shah    @Override
78254022ee4c98f02401ca567e643f24ed42aebe203Tony Mantler    public void registerFolderOrAccountChangedObserver(final DataSetObserver observer) {
78354022ee4c98f02401ca567e643f24ed42aebe203Tony Mantler        mFolderOrAccountObservers.registerObserver(observer);
7840f73d905c5e935a5280f8eb647cc924c2854b7deRohan Shah    }
7850f73d905c5e935a5280f8eb647cc924c2854b7deRohan Shah
7860f73d905c5e935a5280f8eb647cc924c2854b7deRohan Shah    @Override
78754022ee4c98f02401ca567e643f24ed42aebe203Tony Mantler    public void unregisterFolderOrAccountChangedObserver(final DataSetObserver observer) {
78854022ee4c98f02401ca567e643f24ed42aebe203Tony Mantler        mFolderOrAccountObservers.unregisterObserver(observer);
7890f73d905c5e935a5280f8eb647cc924c2854b7deRohan Shah    }
7900f73d905c5e935a5280f8eb647cc924c2854b7deRohan Shah
7910f73d905c5e935a5280f8eb647cc924c2854b7deRohan Shah    /**
79212b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang     * If the drawer is open, the function locks the drawer to the closed, thereby sliding in
79312b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang     * the drawer to the left edge, disabling events, and refreshing it once it's either closed
79412b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang     * or put in an idle state.
7950f73d905c5e935a5280f8eb647cc924c2854b7deRohan Shah     */
7960f73d905c5e935a5280f8eb647cc924c2854b7deRohan Shah    @Override
797a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal    public void closeDrawer(final boolean hasNewFolderOrAccount, Account nextAccount,
798a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal            Folder nextFolder) {
79912b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        if (!isDrawerEnabled()) {
80054022ee4c98f02401ca567e643f24ed42aebe203Tony Mantler            if (hasNewFolderOrAccount) {
80154022ee4c98f02401ca567e643f24ed42aebe203Tony Mantler                mFolderOrAccountObservers.notifyChanged();
80254022ee4c98f02401ca567e643f24ed42aebe203Tony Mantler            }
80312b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            return;
80412b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
80512b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        // If there are no new folders or accounts to switch to, just close the drawer
80612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        if (!hasNewFolderOrAccount) {
80712b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            mDrawerContainer.closeDrawers();
80812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            return;
80912b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
8102f9d394867be45302bb4323da7cacf193da38f39Vikram Aggarwal        // Otherwise, start preloading the conversation list for the new folder.
8112f9d394867be45302bb4323da7cacf193da38f39Vikram Aggarwal        if (nextFolder != null) {
8122f9d394867be45302bb4323da7cacf193da38f39Vikram Aggarwal            preloadConvList(nextAccount, nextFolder);
8132f9d394867be45302bb4323da7cacf193da38f39Vikram Aggarwal        }
8142f9d394867be45302bb4323da7cacf193da38f39Vikram Aggarwal        // Remember if the conversation list view is animating
81512b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        final ConversationListFragment conversationList = getConversationListFragment();
81612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        if (conversationList != null) {
81712b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            mListViewForAnimating = conversationList.getListView();
81812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        } else {
81912b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            // There is no conversation list to animate, so just set it to null
82012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            mListViewForAnimating = null;
82112b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
82212b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang
82312b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        if (mDrawerContainer.isDrawerOpen(mDrawerPullout)) {
82412b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            // Lets the drawer listener update the drawer contents and notify the FolderListFragment
82512b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            mHasNewAccountOrFolder = true;
82612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            mDrawerContainer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
82712b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        } else {
82812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            // Drawer is already closed, notify observers that is the case.
82954022ee4c98f02401ca567e643f24ed42aebe203Tony Mantler            if (hasNewFolderOrAccount) {
83054022ee4c98f02401ca567e643f24ed42aebe203Tony Mantler                mFolderOrAccountObservers.notifyChanged();
83154022ee4c98f02401ca567e643f24ed42aebe203Tony Mantler            }
83212b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
8330f73d905c5e935a5280f8eb647cc924c2854b7deRohan Shah    }
8340f73d905c5e935a5280f8eb647cc924c2854b7deRohan Shah
835a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal    /**
836aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * Load the conversation list early for the given folder. This happens when some UI element
837aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * (usually the drawer) instructs the controller that an account change or folder change is
838aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * imminent. While the UI element is animating, the controller can preload the conversation
839aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * list for the default inbox of the account provided here or to the folder provided here.
840aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     *
841aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * @param nextAccount The account which the app will switch to shortly, possibly null.
842aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * @param nextFolder The folder which the app will switch to shortly, possibly null.
843a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal     */
844a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal    protected void preloadConvList(Account nextAccount, Folder nextFolder) {
845a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal        // Fire off the conversation list loader for this account already with a fake
846a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal        // listener.
847e2be97a58fbae98b95e51735c6468d121e465355Paul Westbrook        final Bundle args = new Bundle(2);
848a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal        if (nextAccount != null) {
849a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal            args.putParcelable(BUNDLE_ACCOUNT_KEY, nextAccount);
850a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal        } else {
851a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal            args.putParcelable(BUNDLE_ACCOUNT_KEY, mAccount);
852a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal        }
853a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal        if (nextFolder != null) {
854a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal            args.putParcelable(BUNDLE_FOLDER_KEY, nextFolder);
8552dc850446320d6b56b89dda7fefc5113c6b7d8d1Vikram Aggarwal        } else {
8562dc850446320d6b56b89dda7fefc5113c6b7d8d1Vikram Aggarwal            LogUtils.e(LOG_TAG, new Error(), "AAC.preloadConvList(): Got an empty folder");
857a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal        }
858a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal        mFolder = null;
859a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal        final LoaderManager lm = mActivity.getLoaderManager();
860a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal        lm.destroyLoader(LOADER_CONVERSATION_LIST);
861a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal        lm.initLoader(LOADER_CONVERSATION_LIST, args, mListCursorCallbacks);
862a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal    }
863a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal
864aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal    /**
865aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * Initiates the async request to create a fake search folder, which returns conversations that
866aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * match the query term provided by the user. Returns immediately.
867aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * @param intent Intent that the app was started with. This intent contains the search query.
868aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     */
869e0828393e175c9293c86a7490225f324cbec5eefMindy Pereira    private void fetchSearchFolder(Intent intent) {
870e2be97a58fbae98b95e51735c6468d121e465355Paul Westbrook        final Bundle args = new Bundle(1);
871ab4863650794e81f35a0fd9abc16a5564d7b102bMindy Pereira        args.putString(ConversationListContext.EXTRA_SEARCH_QUERY, intent
872e0828393e175c9293c86a7490225f324cbec5eefMindy Pereira                .getStringExtra(ConversationListContext.EXTRA_SEARCH_QUERY));
873177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        mActivity.getLoaderManager().restartLoader(LOADER_SEARCH, args, mFolderCallbacks);
874e0828393e175c9293c86a7490225f324cbec5eefMindy Pereira    }
875e0828393e175c9293c86a7490225f324cbec5eefMindy Pereira
876a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    @Override
877ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang    public void onFolderChanged(Folder folder, final boolean force) {
878f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang        if (isDrawerEnabled()) {
879f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang            /** If the folder doesn't exist, or its parent URI is empty,
880f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang             * this is not a child folder */
881f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang            final boolean isTopLevel = Folder.isRoot(folder);
882f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang            final int mode = mViewMode.getMode();
883f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang            mDrawerToggle.setDrawerIndicatorEnabled(
884f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang                    getShouldShowDrawerIndicator(mode, isTopLevel));
885f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang            mDrawerContainer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
886dfbc6985e44c05e37fd8addce65a063ff57a3dc1Vikram Aggarwal
887f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang            mDrawerContainer.closeDrawers();
888f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang        }
8897dd0e1cec391fc2562795d6e6d9fc91ab36ca647Alice Yang
8907dd0e1cec391fc2562795d6e6d9fc91ab36ca647Alice Yang        if (mFolder == null || !mFolder.equals(folder)) {
8917dd0e1cec391fc2562795d6e6d9fc91ab36ca647Alice Yang            // We are actually changing the folder, so exit cab mode
8927dd0e1cec391fc2562795d6e6d9fc91ab36ca647Alice Yang            exitCabMode();
8937dd0e1cec391fc2562795d6e6d9fc91ab36ca647Alice Yang        }
8947dd0e1cec391fc2562795d6e6d9fc91ab36ca647Alice Yang
895a158ac8a41239300a2f50b38ed17167aeef4ff4bScott Kennedy        final String query;
896a158ac8a41239300a2f50b38ed17167aeef4ff4bScott Kennedy        if (folder != null && folder.isType(FolderType.SEARCH)) {
897a158ac8a41239300a2f50b38ed17167aeef4ff4bScott Kennedy            query = mConvListContext.searchQuery;
898a158ac8a41239300a2f50b38ed17167aeef4ff4bScott Kennedy        } else {
899a158ac8a41239300a2f50b38ed17167aeef4ff4bScott Kennedy            query = null;
900a158ac8a41239300a2f50b38ed17167aeef4ff4bScott Kennedy        }
901a158ac8a41239300a2f50b38ed17167aeef4ff4bScott Kennedy
902a158ac8a41239300a2f50b38ed17167aeef4ff4bScott Kennedy        changeFolder(folder, query, force);
903f3341405c97ff6d27a0d5fa37f6031e1c395d161Vikram Aggarwal    }
904f3341405c97ff6d27a0d5fa37f6031e1c395d161Vikram Aggarwal
905f3341405c97ff6d27a0d5fa37f6031e1c395d161Vikram Aggarwal    /**
906203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal     * Sets the folder state without changing view mode and without creating a list fragment, if
907203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal     * possible.
908715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @param folder the folder whose list of conversations are to be shown
909715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @param query the query string for a list of conversations matching a search
910203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal     */
911203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal    private void setListContext(Folder folder, String query) {
912203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal        updateFolder(folder);
913203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal        if (query != null) {
914203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal            mConvListContext = ConversationListContext.forSearchQuery(mAccount, mFolder, query);
915203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal        } else {
916203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal            mConvListContext = ConversationListContext.forFolder(mAccount, mFolder);
917203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal        }
918203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal        cancelRefreshTask();
919203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal    }
920203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal
921203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal    /**
922f3341405c97ff6d27a0d5fa37f6031e1c395d161Vikram Aggarwal     * Changes the folder to the value provided here. This causes the view mode to change.
923f3341405c97ff6d27a0d5fa37f6031e1c395d161Vikram Aggarwal     * @param folder the folder to change to
924f3341405c97ff6d27a0d5fa37f6031e1c395d161Vikram Aggarwal     * @param query if non-null, this represents the search string that the folder represents.
925ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang     * @param force <code>true</code> to force a folder change, <code>false</code> to disallow
926ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang     *          changing to the current folder
927f3341405c97ff6d27a0d5fa37f6031e1c395d161Vikram Aggarwal     */
928ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang    private void changeFolder(Folder folder, String query, final boolean force) {
929203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal        if (!Objects.equal(mFolder, folder)) {
930203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal            commitDestructiveActions(false);
931203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal        }
932ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang        if (folder != null && (!folder.equals(mFolder) || force)
933a1f99981919755e50f31fe300517d2fed752997eMindy Pereira                || (mViewMode.getMode() != ViewMode.CONVERSATION_LIST)) {
934203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal            setListContext(folder, query);
93528e0c34ad35925fc74633bec66344911f2de6a22Mindy Pereira            showConversationList(mConvListContext);
93658ccd6952c0a5ed979e9cee1a3f6992b4040c732Vikram Aggarwal            // Touch the current folder: it is different, and it has been accessed.
93758ccd6952c0a5ed979e9cee1a3f6992b4040c732Vikram Aggarwal            mRecentFolderList.touchFolder(mFolder, mAccount);
93828e0c34ad35925fc74633bec66344911f2de6a22Mindy Pereira        }
93993dc202420c3bf8adf4d9e8ae18c8e30e3cb1959Vikram Aggarwal        resetActionBarIcon();
940a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    }
941a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal
94213c12a628bd4f1e52b9a4fc44a18b85bfdaeb88bMindy Pereira    @Override
943505df5f59c92adbe8eaa734321c1d3f91be84a4aMindy Pereira    public void onFolderSelected(Folder folder) {
944ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang        onFolderChanged(folder, false /* force */);
94513c12a628bd4f1e52b9a4fc44a18b85bfdaeb88bMindy Pereira    }
94613c12a628bd4f1e52b9a4fc44a18b85bfdaeb88bMindy Pereira
947792ccbac8b579fc1077712d82144a31fd92241d0Vikram Aggarwal    /**
94858cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal     * Adds a listener interested in change in the recent folders. If a class is storing a
94958cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal     * reference to the recent folders, it should listen on changes, so it can receive updates.
95058cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal     * Must happen in the UI thread.
95158cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal     */
95258cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal    @Override
95358cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal    public void registerRecentFolderObserver(DataSetObserver obs) {
95458cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal        mRecentFolderObservers.registerObserver(obs);
95558cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal    }
95658cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal
95758cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal    /**
95858cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal     * Removes a listener from receiving recent folder changes.
95958cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal     * Must happen in the UI thread.
96058cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal     */
96158cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal    @Override
96258cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal    public void unregisterRecentFolderObserver(DataSetObserver obs) {
96358cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal        mRecentFolderObservers.unregisterObserver(obs);
96458cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal    }
96558cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal
96658cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal    @Override
96758cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal    public RecentFolderList getRecentFolders() {
96858cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal        return mRecentFolderList;
96958cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal    }
97058cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal
971eb7d329e448eb42195f7c35b978035766b6f46e1Vikram Aggarwal    @Override
972eb7d329e448eb42195f7c35b978035766b6f46e1Vikram Aggarwal    public void loadAccountInbox() {
97377ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal        boolean handled = false;
97477ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal        if (mFolderWatcher != null) {
97577ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal            final Folder inbox = mFolderWatcher.getDefaultInbox(mAccount);
97677ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal            if (inbox != null) {
977ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                onFolderChanged(inbox, false /* force */);
97877ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal                handled = true;
97977ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal            }
98077ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal        }
98177ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal        if (!handled) {
9821bf20e01767d6864ef8ed196195cf7771dc2c45cPaul Westbrook            LogUtils.d(LOG_TAG, "Starting a LOADER_ACCOUNT_INBOX for %s", mAccount);
98377ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal            restartOptionalLoader(LOADER_ACCOUNT_INBOX, mFolderCallbacks, Bundle.EMPTY);
98477ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal        }
9858cbf281d1676c21ced08c42da2aa9828cdadd87dVikram Aggarwal        final int mode = mViewMode.getMode();
9868cbf281d1676c21ced08c42da2aa9828cdadd87dVikram Aggarwal        if (mode == ViewMode.UNKNOWN || mode == ViewMode.WAITING_FOR_ACCOUNT_INITIALIZATION) {
987e6459424b3e46d768865c908af1caa932f783095Andy Huang            mViewMode.enterConversationListMode();
988e6459424b3e46d768865c908af1caa932f783095Andy Huang        }
989f6acdade010b2d0f49fd425b33e7f7b376286490Mindy Pereira    }
990f6acdade010b2d0f49fd425b33e7f7b376286490Mindy Pereira
99177ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal    @Override
99277ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal    public void setFolderWatcher(FolderWatcher watcher) {
99377ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal        mFolderWatcher = watcher;
99477ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal    }
99577ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal
99669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal    /**
997d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal     * Marks the {@link #mFolderChanged} value if the newFolder is different from the existing
998d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal     * {@link #mFolder}. This should be called immediately <b>before</b> assigning newFolder to
999d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal     * mFolder.
1000715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @param newFolder the new folder we are switching to.
1001d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal     */
1002715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal    private void setHasFolderChanged(final Folder newFolder) {
1003d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal        // We should never try to assign a null folder. But in the rare event that we do, we should
1004d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal        // only set the bit when we have a valid folder, and null is not valid.
1005d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal        if (newFolder == null) {
1006d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal            return;
1007d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal        }
1008d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal        // If the previous folder was null, or if the two folders represent different data, then we
1009d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal        // consider that the folder has changed.
1010259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy        if (mFolder == null || !newFolder.equals(mFolder)) {
1011d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal            mFolderChanged = true;
1012d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal        }
1013d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal    }
1014d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal
1015d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal    /**
101669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * Sets the current folder if it is different from the object provided here. This method does
101769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * NOT notify the folder observers that a change has happened. Observers are notified when we
101869b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * get an updated folder from the loaders, which will happen as a consequence of this method
101969b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * (since this method starts/restarts the loaders).
102069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * @param folder The folder to assign
102169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     */
102211e3596e8c5978d07195ae2d7c8a96bb51aa75b3Mindy Pereira    private void updateFolder(Folder folder) {
10231672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        if (folder == null || !folder.isInitialized()) {
10241672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal            LogUtils.e(LOG_TAG, new Error(), "AAC.setFolder(%s): Bad input", folder);
10251672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal            return;
10261672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        }
10271672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        if (folder.equals(mFolder)) {
10281672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal            LogUtils.d(LOG_TAG, "AAC.setFolder(%s): Input matches mFolder", folder);
10291672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal            return;
10301672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        }
10311672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        final boolean wasNull = mFolder == null;
10321672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        LogUtils.d(LOG_TAG, "AbstractActivityController.setFolder(%s)", folder.name);
10331672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        final LoaderManager lm = mActivity.getLoaderManager();
10341672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        // updateFolder is called from AAC.onLoadFinished() on folder changes.  We need to
10351672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        // ensure that the folder is different from the previous folder before marking the
10361672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        // folder changed.
10371672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        setHasFolderChanged(folder);
10381672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        mFolder = folder;
10391672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal
10401672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        // We do not need to notify folder observers yet. Instead we start the loaders and
10411672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        // when the load finishes, we will get an updated folder. Then, we notify the
10421672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        // folderObservers in onLoadFinished.
10432d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein        mActionBarController.setFolder(mFolder);
10441672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal
10451672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        // Only when we switch from one folder to another do we want to restart the
10461672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        // folder and conversation list loaders (to trigger onCreateLoader).
10471672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        // The first time this runs when the activity is [re-]initialized, we want to re-use the
10481672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        // previous loader's instance and data upon configuration change (e.g. rotation).
10491672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        // If there was not already an instance of the loader, init it.
10501672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        if (lm.getLoader(LOADER_FOLDER_CURSOR) == null) {
1051177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            lm.initLoader(LOADER_FOLDER_CURSOR, Bundle.EMPTY, mFolderCallbacks);
10521672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        } else {
1053177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            lm.restartLoader(LOADER_FOLDER_CURSOR, Bundle.EMPTY, mFolderCallbacks);
10541672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        }
1055a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal        if (!wasNull && lm.getLoader(LOADER_CONVERSATION_LIST) != null) {
1056a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal            // If there was an existing folder AND we have changed
10571672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal            // folders, we want to restart the loader to get the information
10581672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal            // for the newly selected folder
10591672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal            lm.destroyLoader(LOADER_CONVERSATION_LIST);
10605cb0c3e684150d6d8ed4d9df4fb73a7606086aaaMindy Pereira        }
1061e2be97a58fbae98b95e51735c6468d121e465355Paul Westbrook        final Bundle args = new Bundle(2);
10622dc850446320d6b56b89dda7fefc5113c6b7d8d1Vikram Aggarwal        args.putParcelable(BUNDLE_ACCOUNT_KEY, mAccount);
1063a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal        args.putParcelable(BUNDLE_FOLDER_KEY, mFolder);
10645bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein        args.putBoolean(BUNDLE_IGNORE_INITIAL_CONVERSATION_LIMIT_KEY,
10655bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein                mIgnoreInitialConversationLimit);
10665bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein        mIgnoreInitialConversationLimit = false;
1067a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal        lm.initLoader(LOADER_CONVERSATION_LIST, args, mListCursorCallbacks);
10685cb0c3e684150d6d8ed4d9df4fb73a7606086aaaMindy Pereira    }
10695cb0c3e684150d6d8ed4d9df4fb73a7606086aaaMindy Pereira
1070a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    @Override
1071090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    public Folder getFolder() {
1072090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang        return mFolder;
1073090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    }
1074090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang
1075090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    @Override
1076c975e8416b64a1ebdec6adee7e65dd1d0928e8d4Mindy Pereira    public Folder getHierarchyFolder() {
1077c975e8416b64a1ebdec6adee7e65dd1d0928e8d4Mindy Pereira        return mFolderListFolder;
1078c975e8416b64a1ebdec6adee7e65dd1d0928e8d4Mindy Pereira    }
1079c975e8416b64a1ebdec6adee7e65dd1d0928e8d4Mindy Pereira
1080c975e8416b64a1ebdec6adee7e65dd1d0928e8d4Mindy Pereira    @Override
1081c975e8416b64a1ebdec6adee7e65dd1d0928e8d4Mindy Pereira    public void setHierarchyFolder(Folder folder) {
1082c975e8416b64a1ebdec6adee7e65dd1d0928e8d4Mindy Pereira        mFolderListFolder = folder;
108323aadfd1c83e08b6723d4cb9357b2908cc464a86Mindy Pereira    }
108423aadfd1c83e08b6723d4cb9357b2908cc464a86Mindy Pereira
10856aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal    /**
10866aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     * The mail activity calls other activities for two specific reasons:
10876aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     * <ul>
10886aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     <li>To add an account. And receives the result {@link #ADD_ACCOUNT_REQUEST_CODE}</li>
10896aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     <li>To update the password on a current account. The result {@link
10906aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     #REAUTHENTICATE_REQUEST_CODE} is received.</li>
10916aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     * </ul>
10926aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     * @param requestCode
10936aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     * @param resultCode
10946aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     * @param data
10956aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     */
109623aadfd1c83e08b6723d4cb9357b2908cc464a86Mindy Pereira    @Override
10971ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    public void onActivityResult(int requestCode, int resultCode, Intent data) {
1098122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook        switch (requestCode) {
1099122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook            case ADD_ACCOUNT_REQUEST_CODE:
1100122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                // We were waiting for the user to create an account
1101122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                if (resultCode == Activity.RESULT_OK) {
1102122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                    // restart the loader to get the updated list of accounts
1103177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    mActivity.getLoaderManager().initLoader(LOADER_ACCOUNT_CURSOR, Bundle.EMPTY,
1104177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            mAccountCallbacks);
1105122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                } else {
1106122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                    // The user failed to create an account, just exit the app
1107122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                    mActivity.finish();
1108122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                }
1109122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                break;
1110122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook            case REAUTHENTICATE_REQUEST_CODE:
1111122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                if (resultCode == Activity.RESULT_OK) {
1112122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                    // The user successfully authenticated, attempt to refresh the list
1113122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                    final Uri refreshUri = mFolder != null ? mFolder.refreshUri : null;
1114122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                    if (refreshUri != null) {
1115122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                        startAsyncRefreshTask(refreshUri);
1116122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                    }
1117122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                }
1118122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                break;
1119371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon            case CHANGE_NAVIGATION_REQUEST_CODE:
1120371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                if (resultCode == Activity.RESULT_OK && data != null) {
1121371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                    // We have have received a result that indicates we need to navigate to a
1122371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                    // different folder or account. This happens if someone navigates using the
1123371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                    // drawer on the search results activity.
1124371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                    final Folder folder = data.getParcelableExtra(EXTRA_FOLDER);
1125371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                    final Account account = data.getParcelableExtra(EXTRA_ACCOUNT);
1126371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                    if (folder != null) {
1127371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                        onFolderSelected(folder);
1128371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                        mViewMode.enterConversationListMode();
1129371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                    } else if (account != null) {
1130371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                        switchToDefaultInboxOrChangeAccount(account);
1131371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                        mViewMode.enterConversationListMode();
1132371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                    }
1133371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                }
1134371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                break;
11352388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook        }
1136a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    }
1137a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal
113869b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal    /**
113969b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * Inform the conversation cursor that there has been a visibility change.
1140715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @param visible true if the conversation list is visible, false otherwise.
114169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     */
114269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal    protected synchronized void informCursorVisiblity(boolean visible) {
11439f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        if (mConversationListCursor != null) {
114469b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            Utils.setConversationCursorVisibility(mConversationListCursor, visible, mFolderChanged);
114569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            // We have informed the cursor. Subsequent visibility changes should not tell it that
114669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            // the folder has changed.
114769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            mFolderChanged = false;
11489f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        }
1149a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    }
1150a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal
115169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal    @Override
115269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal    public void onConversationListVisibilityChanged(boolean visible) {
115369b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        informCursorVisiblity(visible);
1154c94d07f4460d00db898237603de311d226707a96Andy Huang        commitAutoAdvanceOperation();
115532ddb84f2d4f53bfc4b53656461068c0c69d6325Scott Kennedy
115632ddb84f2d4f53bfc4b53656461068c0c69d6325Scott Kennedy        // Notify special views
115732ddb84f2d4f53bfc4b53656461068c0c69d6325Scott Kennedy        final ConversationListFragment convListFragment = getConversationListFragment();
115832ddb84f2d4f53bfc4b53656461068c0c69d6325Scott Kennedy        if (convListFragment != null && convListFragment.getAnimatedAdapter() != null) {
115932ddb84f2d4f53bfc4b53656461068c0c69d6325Scott Kennedy            convListFragment.getAnimatedAdapter().onConversationListVisibilityChanged(visible);
116032ddb84f2d4f53bfc4b53656461068c0c69d6325Scott Kennedy        }
116169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal    }
116269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal
11631ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    /**
11647dd054e39986de84a213c56a3c11ac94731402e6Vikram Aggarwal     * Called when a conversation is visible. Child classes must call the super class implementation
11657dd054e39986de84a213c56a3c11ac94731402e6Vikram Aggarwal     * before performing local computation.
11661ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal     */
11671ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    @Override
11681ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    public void onConversationVisibilityChanged(boolean visible) {
1169c94d07f4460d00db898237603de311d226707a96Andy Huang        commitAutoAdvanceOperation();
1170c94d07f4460d00db898237603de311d226707a96Andy Huang    }
1171c94d07f4460d00db898237603de311d226707a96Andy Huang
1172c94d07f4460d00db898237603de311d226707a96Andy Huang    /**
1173c94d07f4460d00db898237603de311d226707a96Andy Huang     * Commits any pending destructive action that was earlier deferred by an auto-advance
1174c94d07f4460d00db898237603de311d226707a96Andy Huang     * mode-change transition.
1175c94d07f4460d00db898237603de311d226707a96Andy Huang     */
1176c94d07f4460d00db898237603de311d226707a96Andy Huang    private void commitAutoAdvanceOperation() {
1177c94d07f4460d00db898237603de311d226707a96Andy Huang        if (mAutoAdvanceOp != null) {
1178c94d07f4460d00db898237603de311d226707a96Andy Huang            mAutoAdvanceOp.run();
1179c94d07f4460d00db898237603de311d226707a96Andy Huang            mAutoAdvanceOp = null;
1180c94d07f4460d00db898237603de311d226707a96Andy Huang        }
11811ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    }
11821ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal
118359f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal    /**
118459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal     * Initialize development time logging. This can potentially log a lot of PII, and we don't want
118559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal     * to turn it on for shipped versions.
118659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal     */
118759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal    private void initializeDevLoggingService() {
118859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal        if (!MailLogService.DEBUG_ENABLED) {
118959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal            return;
119059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal        }
119159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal        // Check every 5 minutes.
119259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal        final int WAIT_TIME = 5 * 60 * 1000;
119359f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal        // Start a runnable that periodically checks the log level and starts/stops the service.
119459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal        mLogServiceChecker = new Runnable() {
119559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal            /** True if currently logging. */
119659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal            private boolean mCurrentlyLogging = false;
119759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal
119859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal            /**
119959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal             * If the logging level has been changed since the previous run, start or stop the
120059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal             * service.
120159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal             */
120259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal            private void startOrStopService() {
120359f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                // If the log level is already high, start the service.
120459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                final Intent i = new Intent(mContext, MailLogService.class);
120559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                final boolean loggingEnabled = MailLogService.isLoggingLevelHighEnough();
120659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                if (mCurrentlyLogging == loggingEnabled) {
120759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                    // No change since previous run, just return;
120859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                    return;
120959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                }
121059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                if (loggingEnabled) {
121159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                    LogUtils.e(LOG_TAG, "Starting MailLogService");
121259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                    mContext.startService(i);
121359f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                } else {
121459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                    LogUtils.e(LOG_TAG, "Stopping MailLogService");
121559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                    mContext.stopService(i);
121659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                }
121759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                mCurrentlyLogging = loggingEnabled;
121859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal            }
121959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal
122059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal            @Override
122159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal            public void run() {
122259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                startOrStopService();
122359f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                mHandler.postDelayed(this, WAIT_TIME);
122459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal            }
122559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal        };
122659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal        // Start the runnable right away.
122759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal        mHandler.post(mLogServiceChecker);
122859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal    }
122959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal
12306aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal    /**
12316aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     * The application can be started from the following entry points:
12326aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     * <ul>
12336aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     <li>Launcher: you tap on the Gmail icon in the launcher. This is what most users think of
12346aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *         as “Starting the app”.</li>
12356aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     <li>Shortcut: Users can make a shortcut to take them directly to a label.</li>
12366aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     <li>Widget: Shows the contents of a synced label, and allows:
12376aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     <ul>
12386aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *         <li>Viewing the list (tapping on the title)</li>
12396aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *         <li>Composing a new message (tapping on the new message icon in the title. This
12406aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *         launches the {@link ComposeActivity}.
12416aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *         </li>
12426aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *         <li>Viewing a single message (tapping on a list element)</li>
12436aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     </ul>
12446aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *
12456aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     </li>
12466aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     <li>Tapping on a notification:
12476aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     <ul>
12486aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *         <li>Shows message list if more than one message</li>
12496aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *         <li>Shows the conversation if the notification is for a single message</li>
12506aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     </ul>
12516aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     </li>
12526aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     <li>...and most importantly, the activity life cycle can tear down the application and
12536aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     restart it:
12546aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     <ul>
12556aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *         <li>Rotate the application: it is destroyed and recreated.</li>
12566aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *         <li>Navigate away, and return from recent applications.</li>
12576aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     </ul>
12586aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     </li>
12596aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     <li>Add a new account: fires off an intent to add an account,
12606aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     and returns in {@link #onActivityResult(int, int, android.content.Intent)} .</li>
12616aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     <li>Re-authenticate your account: again returns in onActivityResult().</li>
12626aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     <li>Composing can happen from many entry points: third party applications fire off an
12636aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     intent to compose email, and launch directly into the {@link ComposeActivity}
12646aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     .</li>
12656aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     * </ul>
12666aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     * {@inheritDoc}
12676aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     */
12681ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    @Override
1269d7a12cd5b5c2639b653d8c671b04844ef02ee01dVikram Aggarwal    public boolean onCreate(Bundle savedState) {
1270abd24d82e41fd4747945d3f124b4e3a57e5dc6e6Vikram Aggarwal        initializeActionBar();
127159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal        initializeDevLoggingService();
12721ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal        // Allow shortcut keys to function for the ActionBar and menus.
12731ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal        mActivity.setDefaultKeyMode(Activity.DEFAULT_KEYS_SHORTCUT);
127480aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwal        mResolver = mActivity.getContentResolver();
12756ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook        mNewEmailReceiver = new SuppressNotificationReceiver();
12767c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal        mRecentFolderList.initialize(mActivity);
127769a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal        mVeiledMatcher.initialize(this);
12786ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook
12791a1e9754cce66296ae730f2c483f55fcf1a24359James Lemieux        mFloatingComposeButton = mActivity.findViewById(R.id.compose_button);
12801a1e9754cce66296ae730f2c483f55fcf1a24359James Lemieux        mFloatingComposeButton.setOnClickListener(this);
12815d79a913c8289de4ffc1b662b9bea967a7e65b0dJames Lemieux
1282f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang        if (isDrawerEnabled()) {
1283f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang            mDrawerToggle = new ActionBarDrawerToggle(mActivity, mDrawerContainer, false,
1284f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang                    R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close);
1285f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang            mDrawerContainer.setDrawerListener(mDrawerListener);
1286f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang            mDrawerContainer.setDrawerShadow(
1287f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang                    mContext.getResources().getDrawable(R.drawable.drawer_shadow), Gravity.START);
128812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang
1289f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang            mDrawerToggle.setDrawerIndicatorEnabled(isDrawerEnabled());
1290f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang        } else {
129152882ff08fe9420fe2dbb3f681545a330222e9c8Andrew Sapperstein            final ActionBar ab = mActivity.getSupportActionBar();
1292f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang            ab.setHomeAsUpIndicator(R.drawable.ic_drawer);
1293f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang            ab.setHomeActionContentDescription(R.string.drawer_open);
1294f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang            ab.setDisplayHomeAsUpEnabled(true);
1295f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang        }
129612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang
1297161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira        // All the individual UI components listen for ViewMode changes. This
1298b849dfbda3c917f078a5c09f03906886c27ecbecMindy Pereira        // simplifies the amount of logic in the AbstractActivityController, but increases the
1299b849dfbda3c917f078a5c09f03906886c27ecbecMindy Pereira        // possibility of timing-related bugs.
13001ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal        mViewMode.addListener(this);
1301632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        mPagerController = new ConversationPagerController(mActivity, this);
13029a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux        mToastBar = findActionableToastBar(mActivity);
1303ada517849ada8a3c3a62d81c5163f392bc674eb7Vikram Aggarwal        attachActionBar();
13041ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal
1305144bfe739b93afdee0a1700a34806b0b287e5887Andy Huang        mDrawIdler.setRootView(mActivity.getWindow().getDecorView());
1306144bfe739b93afdee0a1700a34806b0b287e5887Andy Huang
1307632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        final Intent intent = mActivity.getIntent();
13084fe0af81874976a1995191321e35c844b2229811Andy Huang
1309abd24d82e41fd4747945d3f124b4e3a57e5dc6e6Vikram Aggarwal        // Immediately handle a clean launch with intent, and any state restoration
1310632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        // that does not rely on restored fragments or loader data
1311632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        // any state restoration that relies on those can be done later in
1312632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        // onRestoreInstanceState, once fragments are up and loader data is re-delivered
1313632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        if (savedState != null) {
1314632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            if (savedState.containsKey(SAVED_ACCOUNT)) {
1315632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                setAccount((Account) savedState.getParcelable(SAVED_ACCOUNT));
1316632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            }
1317632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            if (savedState.containsKey(SAVED_FOLDER)) {
13182bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang                final Folder folder = savedState.getParcelable(SAVED_FOLDER);
1319203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal                final String query = savedState.getString(SAVED_QUERY, null);
1320649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal                setListContext(folder, query);
1321632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            }
13226cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal            if (savedState.containsKey(SAVED_ACTION)) {
13236cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal                mDialogAction = savedState.getInt(SAVED_ACTION);
13246cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal            }
1325a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal            mDialogFromSelectedSet = savedState.getBoolean(SAVED_ACTION_FROM_SELECTED, false);
1326649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal            mViewMode.handleRestore(savedState);
1327632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        } else if (intent != null) {
1328632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            handleIntent(intent);
1329632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
1330632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        // Create the accounts loader; this loads the account switch spinner.
1331177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        mActivity.getLoaderManager().initLoader(LOADER_ACCOUNT_CURSOR, Bundle.EMPTY,
1332177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                mAccountCallbacks);
1333632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        return true;
1334b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang    }
1335b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang
13369a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux    /**
13379a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux     * @param activity the activity that has been inflated
13389a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux     * @return the Actionable Toast Bar defined within the activity
13399a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux     */
13409a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux    protected ActionableToastBar findActionableToastBar(MailActivity activity) {
13419a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux        return (ActionableToastBar) activity.findViewById(R.id.toast_bar);
13429a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux    }
13439a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux
1344b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang    @Override
134557246a4bf94e8b440c0597fa4a8e4f4e8a46532aPaul Westbrook    public void onPostCreate(Bundle savedState) {
1346f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang        if (!isDrawerEnabled()) {
1347f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang            return;
1348f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang        }
134912b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        // Sync the toggle state after onRestoreInstanceState has occurred.
135012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        mDrawerToggle.syncState();
13515747e152fa606c20261b18431f4a9f0ff3be7b5aAndrew Sapperstein
1352d70fe49cabf896e87574bcb2e57e8a32608537beVikram Aggarwal        mHideMenuItems = isDrawerEnabled() && mDrawerContainer.isDrawerOpen(mDrawerPullout);
135357246a4bf94e8b440c0597fa4a8e4f4e8a46532aPaul Westbrook    }
135457246a4bf94e8b440c0597fa4a8e4f4e8a46532aPaul Westbrook
135557246a4bf94e8b440c0597fa4a8e4f4e8a46532aPaul Westbrook    @Override
135657246a4bf94e8b440c0597fa4a8e4f4e8a46532aPaul Westbrook    public void onConfigurationChanged(Configuration newConfig) {
1357f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang        if (isDrawerEnabled()) {
1358f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang            mDrawerToggle.onConfigurationChanged(newConfig);
1359f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang        }
136012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang    }
136112b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang
136212b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang    /**
13635d79a913c8289de4ffc1b662b9bea967a7e65b0dJames Lemieux     * This controller listens for clicks on items in the floating action bar.
13645d79a913c8289de4ffc1b662b9bea967a7e65b0dJames Lemieux     *
13655d79a913c8289de4ffc1b662b9bea967a7e65b0dJames Lemieux     * @param view the item that was clicked in the floating action bar
13665d79a913c8289de4ffc1b662b9bea967a7e65b0dJames Lemieux     */
13675d79a913c8289de4ffc1b662b9bea967a7e65b0dJames Lemieux    @Override
13685d79a913c8289de4ffc1b662b9bea967a7e65b0dJames Lemieux    public void onClick(View view) {
136953de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein        final int viewId = view.getId();
137053de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein        if (viewId == R.id.compose_button) {
137153de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein            ComposeActivity.compose(mActivity.getActivityContext(), getAccount());
137253de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein        } else if (viewId == android.R.id.home) {
137353de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein            // TODO: b/16627877
137453de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein            onUpPressed();
137553de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein        }
13765d79a913c8289de4ffc1b662b9bea967a7e65b0dJames Lemieux    }
13775d79a913c8289de4ffc1b662b9bea967a7e65b0dJames Lemieux
13785d79a913c8289de4ffc1b662b9bea967a7e65b0dJames Lemieux    /**
137912b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang     * If drawer is open/visible (even partially), close it.
138012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang     */
138112b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang    protected void closeDrawerIfOpen() {
138212b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        if (!isDrawerEnabled()) {
138312b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            return;
138412b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
138512b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        if(mDrawerContainer.isDrawerOpen(mDrawerPullout)) {
138612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            mDrawerContainer.closeDrawers();
138712b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
138857246a4bf94e8b440c0597fa4a8e4f4e8a46532aPaul Westbrook    }
138957246a4bf94e8b440c0597fa4a8e4f4e8a46532aPaul Westbrook
139057246a4bf94e8b440c0597fa4a8e4f4e8a46532aPaul Westbrook    @Override
13911ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    public void onStart() {
13921ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        mSafeToModifyFragments = true;
1393cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedy
1394cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedy        NotificationActionUtils.registerUndoNotificationObserver(mUndoNotificationObserver);
1395761522c1ac7a8fa05acde714f0e26fde47f3415fAndy Huang
1396761522c1ac7a8fa05acde714f0e26fde47f3415fAndy Huang        if (mViewMode.getMode() != ViewMode.UNKNOWN) {
1397761522c1ac7a8fa05acde714f0e26fde47f3415fAndy Huang            Analytics.getInstance().sendView("MainActivity" + mViewMode.toString());
1398761522c1ac7a8fa05acde714f0e26fde47f3415fAndy Huang        }
13991ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    }
14001ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang
14011ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    @Override
140200179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein    public void onRestart() {
1403177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        final DialogFragment fragment = (DialogFragment)
140400179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                mFragmentManager.findFragmentByTag(SYNC_ERROR_DIALOG_FRAGMENT_TAG);
140500179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein        if (fragment != null) {
140600179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein            fragment.dismiss();
140700179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein        }
1408ea04f938bef8e2ec7c6f64067511f006681318fbmindyp        // When the user places the app in the background by pressing "home",
1409ea04f938bef8e2ec7c6f64067511f006681318fbmindyp        // dismiss the toast bar. However, since there is no way to determine if
1410ea04f938bef8e2ec7c6f64067511f006681318fbmindyp        // home was pressed, just dismiss any existing toast bar when restarting
1411ea04f938bef8e2ec7c6f64067511f006681318fbmindyp        // the app.
1412ea04f938bef8e2ec7c6f64067511f006681318fbmindyp        if (mToastBar != null) {
1413f8ccdcfc26f362d9c20771e2295a0c0e8d1195f3Andrew Sapperstein            mToastBar.hide(false, false /* actionClicked */);
1414ea04f938bef8e2ec7c6f64067511f006681318fbmindyp        }
141500179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein    }
141600179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein
141700179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein    @Override
14181ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    public Dialog onCreateDialog(int id, Bundle bundle) {
14191ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal        return null;
14201ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    }
14211ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal
1422a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    @Override
14234eb5271e0f0a48ea624e048eb69fbe86d6c62dc3Vikram Aggarwal    public final boolean onCreateOptionsMenu(Menu menu) {
14240fa9efd6df21a92866908ab679a78b7a128bc28fAndrew Sapperstein        if (mViewMode.isAdMode()) {
14250fa9efd6df21a92866908ab679a78b7a128bc28fAndrew Sapperstein            return false;
14260fa9efd6df21a92866908ab679a78b7a128bc28fAndrew Sapperstein        }
1427e5e917c10cbc4afd31358c0425954e2c27e0fb8eVikram Aggarwal        final MenuInflater inflater = mActivity.getMenuInflater();
14282d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein        inflater.inflate(mActionBarController.getOptionsMenuId(), menu);
14292d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein        mActionBarController.onCreateOptionsMenu(menu);
143028d5f72e0ad0db75b6243ee8125bfe3aadbdcb85Mindy Pereira        return true;
1431a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    }
1432a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal
1433a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    @Override
14344eb5271e0f0a48ea624e048eb69fbe86d6c62dc3Vikram Aggarwal    public final boolean onKeyDown(int keyCode, KeyEvent event) {
1435b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal        return false;
14361ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    }
14371ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal
143817a8e78d61bff414610e23f24889bab29955f54emindyp    public abstract boolean doesActionChangeConversationListVisibility(int action);
143917a8e78d61bff414610e23f24889bab29955f54emindyp
144030c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao    /**
144130c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao     * Helper function that determines if we should associate an undo callback with
144230c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao     * the current menu action item
144330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao     * @param actionId the id of the action
144430c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao     * @return the appropriate callback handler, or null if not applicable
144530c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao     */
144630c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao    private UndoCallback getUndoCallbackForDestructiveActionsWithAutoAdvance(
144730c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            int actionId, final Conversation conv) {
144830c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        // We associated the undoCallback if the user is going to perform an action on the current
144930c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        // conversation, causing the current conversation to be removed from view and replacing it
145030c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        // with another (via Auto Advance). The undoCallback will bring the removed conversation
145130c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        // back into the view if the action is undone.
145230c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        final Collection<Conversation> convCol = Conversation.listOf(conv);
145330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        final boolean isApplicableForReshow = mAccount != null &&
145430c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                mAccount.settings != null &&
145530c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                mTracker != null &&
145630c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                // ensure that we will show another conversation due to Auto Advance
145730c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                mTracker.getNextConversation(
145830c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                        mAccount.settings.getAutoAdvanceSetting(), convCol) != null &&
145930c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                // ensure that we are performing the action from conversation view
146030c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                isCurrentConversationInView(convCol) &&
146130c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                // check for the appropriate destructive actions
146230c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                doesActionRemoveCurrentConversationFromView(actionId);
146330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        return (isApplicableForReshow) ?
146430c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            new UndoCallback() {
146530c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                @Override
146630c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                public void performUndoCallback() {
146730c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                    showConversation(conv);
146830c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                }
146930c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            } : null;
147030c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao    }
147130c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao
147230c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao    /**
147330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao     * Check if the provided action will remove the active conversation from view
147430c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao     * @param actionId the applied action
147530c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao     * @return true if it will remove the conversation from view, false otherwise
147630c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao     */
147730c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao    private boolean doesActionRemoveCurrentConversationFromView(int actionId) {
147830c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        return actionId == R.id.archive ||
147930c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                actionId == R.id.delete ||
1480512821c11d89d49908f3cfdee0b582601f500f3dJin Cao                actionId == R.id.discard_outbox ||
148130c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                actionId == R.id.remove_folder ||
148230c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                actionId == R.id.report_spam ||
148330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                actionId == R.id.report_phishing ||
148430c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                actionId == R.id.move_to;
148530c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao    }
148630c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao
14871ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    @Override
148857246a4bf94e8b440c0597fa4a8e4f4e8a46532aPaul Westbrook    public boolean onOptionsItemSelected(MenuItem item) {
1489761522c1ac7a8fa05acde714f0e26fde47f3415fAndy Huang
149012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        /*
149112b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         * The action bar home/up action should open or close the drawer.
149212b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         * mDrawerToggle will take care of this.
149312b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         */
1494f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang        if (isDrawerEnabled() && mDrawerToggle.onOptionsItemSelected(item)) {
1495042a530b2296487fa5899a3e871214ac4a47e3d8Andy Huang            Analytics.getInstance().sendEvent(Analytics.EVENT_CATEGORY_MENU_ITEM, "drawer_toggle",
1496042a530b2296487fa5899a3e871214ac4a47e3d8Andy Huang                    null, 0);
149712b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            return true;
149812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
149912b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang
15002b55549d4fbe25e91f673a0727fc89ff755d9327Andy Huang        Analytics.getInstance().sendMenuItemEvent(Analytics.EVENT_CATEGORY_MENU_ITEM,
1501f8c59b0198bd3230ef669c5e258b86443e7ed682Andy Huang                item.getItemId(), "action_bar/" + mViewMode.getModeString(), 0);
1502042a530b2296487fa5899a3e871214ac4a47e3d8Andy Huang
150354452ae7a9cdb607ed1db5e74077b5ebd95c46a2Vikram Aggarwal        final int id = item.getItemId();
15047dd054e39986de84a213c56a3c11ac94731402e6Vikram Aggarwal        LogUtils.d(LOG_TAG, "AbstractController.onOptionsItemSelected(%d) called.", id);
15059b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira        boolean handled = true;
150684fe99431df3b989dff9b363562f09fd2757b63cVikram Aggarwal        /** This is NOT a batch action. */
150784fe99431df3b989dff9b363562f09fd2757b63cVikram Aggarwal        final boolean isBatch = false;
1508ba68fdaf4f251eccba0832878a74bdaf6aa7469eMindy Pereira        final Collection<Conversation> target = Conversation.listOf(mCurrentConversation);
1509112cd16335e1ca56c96d005aa3b5bb11508396dcVikram Aggarwal        final Settings settings = (mAccount == null) ? null : mAccount.settings;
151084f7d32bdc79263004ed5241480988e02f8e618cmindyp        // The user is choosing a new action; commit whatever they had been
151117a8e78d61bff414610e23f24889bab29955f54emindyp        // doing before. Don't animate if we are launching a new screen.
151217a8e78d61bff414610e23f24889bab29955f54emindyp        commitDestructiveActions(!doesActionChangeConversationListVisibility(id));
151330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        final UndoCallback undoCallback = getUndoCallbackForDestructiveActionsWithAutoAdvance(
151430c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                id, mCurrentConversation);
151530c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao
15162b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy        if (id == R.id.archive) {
15172b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            final boolean showDialog = (settings != null && settings.confirmArchive);
151830c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            confirmAndDelete(id, target, showDialog, R.plurals.confirm_archive_conversation, undoCallback);
15192b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy        } else if (id == R.id.remove_folder) {
15202b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            delete(R.id.remove_folder, target,
152130c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                    getDeferredRemoveFolder(target, mFolder, true, isBatch, true, undoCallback),
152230c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                    isBatch);
15232b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy        } else if (id == R.id.delete) {
15242b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            final boolean showDialog = (settings != null && settings.confirmDelete);
152530c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            confirmAndDelete(id, target, showDialog, R.plurals.confirm_delete_conversation, undoCallback);
15262b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy        } else if (id == R.id.discard_drafts) {
1527121c8b877090e780b4a16d5a14507c3649e61d7fAndy Huang            // drafts are lost forever, so always confirm
1528121c8b877090e780b4a16d5a14507c3649e61d7fAndy Huang            confirmAndDelete(id, target, true /* showDialog */,
152930c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                    R.plurals.confirm_discard_drafts_conversation, undoCallback);
1530512821c11d89d49908f3cfdee0b582601f500f3dJin Cao        } else if (id == R.id.discard_outbox) {
1531512821c11d89d49908f3cfdee0b582601f500f3dJin Cao            // discard in outbox means we discard the failed message and save them in drafts
1532512821c11d89d49908f3cfdee0b582601f500f3dJin Cao            delete(id, target, getDeferredAction(id, target, isBatch, undoCallback), isBatch);
15332b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy        } else if (id == R.id.mark_important) {
15342b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            updateConversation(Conversation.listOf(mCurrentConversation),
15352b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                    ConversationColumns.PRIORITY, UIProvider.ConversationPriority.HIGH);
15362b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy        } else if (id == R.id.mark_not_important) {
15372b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            if (mFolder != null && mFolder.isImportantOnly()) {
15382b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                delete(R.id.mark_not_important, target,
153930c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                        getDeferredAction(R.id.mark_not_important, target, isBatch, undoCallback),
154030c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                        isBatch);
15412b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            } else {
15422b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                updateConversation(Conversation.listOf(mCurrentConversation),
15432b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                        ConversationColumns.PRIORITY, UIProvider.ConversationPriority.LOW);
1544ba68fdaf4f251eccba0832878a74bdaf6aa7469eMindy Pereira            }
15452b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy        } else if (id == R.id.mute) {
154630c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            delete(R.id.mute, target, getDeferredAction(R.id.mute, target, isBatch, undoCallback),
154730c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                    isBatch);
15482b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy        } else if (id == R.id.report_spam) {
15492b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            delete(R.id.report_spam, target,
155030c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                    getDeferredAction(R.id.report_spam, target, isBatch, undoCallback), isBatch);
15512b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy        } else if (id == R.id.mark_not_spam) {
15522b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            // Currently, since spam messages are only shown in list with
15532b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            // other spam messages,
15542b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            // marking a message not as spam is a destructive action
15552b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            delete(R.id.mark_not_spam, target,
155630c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                    getDeferredAction(R.id.mark_not_spam, target, isBatch, undoCallback), isBatch);
15572b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy        } else if (id == R.id.report_phishing) {
15582b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            delete(R.id.report_phishing, target,
155930c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                    getDeferredAction(R.id.report_phishing, target, isBatch, undoCallback), isBatch);
15602b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy        } else if (id == android.R.id.home) {
15612b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            onUpPressed();
15622b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy        } else if (id == R.id.compose) {
15632b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            ComposeActivity.compose(mActivity.getActivityContext(), mAccount);
15642b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy        } else if (id == R.id.refresh) {
15652b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            requestFolderRefresh();
15662b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy        } else if (id == R.id.settings) {
15672b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            Utils.showSettings(mActivity.getActivityContext(), mAccount);
15682b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy        } else if (id == R.id.help_info_menu_item) {
1569a57da3c151ae174bc8621af79f3f09392ed90953Ray Chen            mActivity.showHelp(mAccount, mViewMode.getMode());
15706c570db71b8adc43d7dc2f31e6fa63cf50c1426eAndrew Sapperstein        } else if (id == R.id.move_to || id == R.id.change_folders) {
15712a4be2459962700e1a38ba897de5d2f5913412f5Tony Mantler            final FolderSelectionDialog dialog = FolderSelectionDialog.getInstance(mAccount,
15722b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                    Conversation.listOf(mCurrentConversation), isBatch, mFolder,
15732b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                    id == R.id.move_to);
15742b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            if (dialog != null) {
15752a4be2459962700e1a38ba897de5d2f5913412f5Tony Mantler                dialog.show(mActivity.getFragmentManager(), null);
1576a8e4318bb9921e2ec6045c5f7187a4b78c55fe10Vikram Aggarwal            }
15772b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy        } else if (id == R.id.move_to_inbox) {
15782b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            new AsyncTask<Void, Void, Folder>() {
15792b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                @Override
15802b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                protected Folder doInBackground(final Void... params) {
15812b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                    // Get the "move to" inbox
15822b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                    return Utils.getFolder(mContext, mAccount.settings.moveToInbox,
15832b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                            true /* allowHidden */);
1584a7e154530add87e05d1fcee980e1a1fc34a456e7mindyp                }
1585dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy
15862b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                @Override
15872b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                protected void onPostExecute(final Folder moveToInbox) {
15882b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                    final List<FolderOperation> ops = Lists.newArrayListWithCapacity(1);
15892b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                    // Add inbox
15902b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                    ops.add(new FolderOperation(moveToInbox, true));
15912b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                    assignFolder(ops, Conversation.listOf(mCurrentConversation), true,
15922b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                            true /* showUndo */, false /* isMoveTo */);
15932b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                }
15942b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            }.execute((Void[]) null);
15952b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy        } else if (id == R.id.empty_trash) {
15962b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            showEmptyDialog();
15972b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy        } else if (id == R.id.empty_spam) {
15982b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            showEmptyDialog();
15992b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy        } else {
16002b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            handled = false;
160128d5f72e0ad0db75b6243ee8125bfe3aadbdcb85Mindy Pereira        }
16029b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira        return handled;
1603a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    }
1604a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal
1605ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein    /**
1606ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein     * Opens an {@link EmptyFolderDialogFragment} for the current folder.
1607ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein     */
1608ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein    private void showEmptyDialog() {
1609ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein        if (mFolder != null) {
1610ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein            final EmptyFolderDialogFragment fragment =
1611ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein                    EmptyFolderDialogFragment.newInstance(mFolder.totalCount, mFolder.type);
1612ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein            fragment.setListener(this);
1613ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein            fragment.show(mActivity.getFragmentManager(), EmptyFolderDialogFragment.FRAGMENT_TAG);
1614ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein        }
1615ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein    }
1616ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein
1617ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein    @Override
1618ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein    public void onFolderEmptied() {
1619ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein        emptyFolder();
1620ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein    }
1621ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein
1622ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein    /**
1623ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein     * Performs the work of emptying the currently visible folder.
1624ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein     */
16257ee089ea2ef96e31504e88637b5f3b0969b3c7c1Scott Kennedy    private void emptyFolder() {
16261d0f1f839023e405b24ffc6a670b5c26b36f24e0Yorke Lee        if (mConversationListCursor != null) {
16271d0f1f839023e405b24ffc6a670b5c26b36f24e0Yorke Lee            mConversationListCursor.emptyFolder();
16281d0f1f839023e405b24ffc6a670b5c26b36f24e0Yorke Lee        }
16297ee089ea2ef96e31504e88637b5f3b0969b3c7c1Scott Kennedy    }
16307ee089ea2ef96e31504e88637b5f3b0969b3c7c1Scott Kennedy
1631ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein    private void attachEmptyFolderDialogFragmentListener() {
1632ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein        final EmptyFolderDialogFragment fragment =
1633ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein                (EmptyFolderDialogFragment) mActivity.getFragmentManager()
1634ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein                        .findFragmentByTag(EmptyFolderDialogFragment.FRAGMENT_TAG);
1635ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein
1636ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein        if (fragment != null) {
1637ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein            fragment.setListener(this);
1638ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein        }
1639ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein    }
1640ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein
16418e65c6d2527c7539b689e0e8548c84f690badca5Rohan Shah    /**
164212b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang     * Toggles the drawer pullout. If it was open (Fully extended), the
164312b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang     * drawer will be closed. Otherwise, the drawer will be opened. This should
164412b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang     * only be called when used with a toggle item. Other cases should be handled
164512b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang     * explicitly with just closeDrawers() or openDrawer(View drawerView);
16468e65c6d2527c7539b689e0e8548c84f690badca5Rohan Shah     */
1647d9eaca8676ad4c6c58122fd41be799872bfd9cdaTony Mantler    protected void toggleDrawerState() {
164812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        if (!isDrawerEnabled()) {
164912b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            return;
165012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
165112b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        if(mDrawerContainer.isDrawerOpen(mDrawerPullout)) {
165212b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            mDrawerContainer.closeDrawers();
165312b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        } else {
165412b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            mDrawerContainer.openDrawer(mDrawerPullout);
165512b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
16568e65c6d2527c7539b689e0e8548c84f690badca5Rohan Shah    }
16578e65c6d2527c7539b689e0e8548c84f690badca5Rohan Shah
1658531488ed848766708252a5fdc01c4ecf0fd167d1Vikram Aggarwal    @Override
1659c1fb9a9c2730178105977fca629e80951bfc3cdcAndy Huang    public final boolean onUpPressed() {
1660c1fb9a9c2730178105977fca629e80951bfc3cdcAndy Huang        return handleUpPress();
1661c1fb9a9c2730178105977fca629e80951bfc3cdcAndy Huang    }
1662c1fb9a9c2730178105977fca629e80951bfc3cdcAndy Huang
1663c1fb9a9c2730178105977fca629e80951bfc3cdcAndy Huang    @Override
1664c1fb9a9c2730178105977fca629e80951bfc3cdcAndy Huang    public final boolean onBackPressed() {
166512b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        if (isDrawerEnabled() && mDrawerContainer.isDrawerVisible(mDrawerPullout)) {
166612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            mDrawerContainer.closeDrawers();
166712b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            return true;
166812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
166912b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang
1670c1fb9a9c2730178105977fca629e80951bfc3cdcAndy Huang        return handleBackPress();
1671c1fb9a9c2730178105977fca629e80951bfc3cdcAndy Huang    }
1672c1fb9a9c2730178105977fca629e80951bfc3cdcAndy Huang
1673c1fb9a9c2730178105977fca629e80951bfc3cdcAndy Huang    protected abstract boolean handleBackPress();
1674ede2e52e725abc625ae963f979639ff8ecc40cf5Greg Bullock
1675c1fb9a9c2730178105977fca629e80951bfc3cdcAndy Huang    protected abstract boolean handleUpPress();
1676c1fb9a9c2730178105977fca629e80951bfc3cdcAndy Huang
1677c1fb9a9c2730178105977fca629e80951bfc3cdcAndy Huang    @Override
16786c2663dc9cade8aff34c462d244e3d72c475c664Mindy Pereira    public void updateConversation(Collection<Conversation> target, ContentValues values) {
16799e2d407fdafeb874e640eb84017feaf784309075Scott Kennedy        mConversationListCursor.updateValues(target, values);
16806c2663dc9cade8aff34c462d244e3d72c475c664Mindy Pereira        refreshConversationList();
16816c2663dc9cade8aff34c462d244e3d72c475c664Mindy Pereira    }
16826c2663dc9cade8aff34c462d244e3d72c475c664Mindy Pereira
16836c2663dc9cade8aff34c462d244e3d72c475c664Mindy Pereira    @Override
1684531488ed848766708252a5fdc01c4ecf0fd167d1Vikram Aggarwal    public void updateConversation(Collection <Conversation> target, String columnName,
1685531488ed848766708252a5fdc01c4ecf0fd167d1Vikram Aggarwal            boolean value) {
16869e2d407fdafeb874e640eb84017feaf784309075Scott Kennedy        mConversationListCursor.updateBoolean(target, columnName, value);
168775daee52f08c9bf66652b9bddf42cfbd1ca71987Vikram Aggarwal        refreshConversationList();
168854452ae7a9cdb607ed1db5e74077b5ebd95c46a2Vikram Aggarwal    }
168954452ae7a9cdb607ed1db5e74077b5ebd95c46a2Vikram Aggarwal
1690531488ed848766708252a5fdc01c4ecf0fd167d1Vikram Aggarwal    @Override
16916c2663dc9cade8aff34c462d244e3d72c475c664Mindy Pereira    public void updateConversation(Collection <Conversation> target, String columnName,
16926c2663dc9cade8aff34c462d244e3d72c475c664Mindy Pereira            int value) {
16939e2d407fdafeb874e640eb84017feaf784309075Scott Kennedy        mConversationListCursor.updateInt(target, columnName, value);
169475daee52f08c9bf66652b9bddf42cfbd1ca71987Vikram Aggarwal        refreshConversationList();
1695c9d59184da271d5a6974edb709e3b39a5a970fa7Mindy Pereira    }
1696c9d59184da271d5a6974edb709e3b39a5a970fa7Mindy Pereira
1697531488ed848766708252a5fdc01c4ecf0fd167d1Vikram Aggarwal    @Override
1698531488ed848766708252a5fdc01c4ecf0fd167d1Vikram Aggarwal    public void updateConversation(Collection <Conversation> target, String columnName,
1699531488ed848766708252a5fdc01c4ecf0fd167d1Vikram Aggarwal            String value) {
17009e2d407fdafeb874e640eb84017feaf784309075Scott Kennedy        mConversationListCursor.updateString(target, columnName, value);
170175daee52f08c9bf66652b9bddf42cfbd1ca71987Vikram Aggarwal        refreshConversationList();
170254452ae7a9cdb607ed1db5e74077b5ebd95c46a2Vikram Aggarwal    }
170354452ae7a9cdb607ed1db5e74077b5ebd95c46a2Vikram Aggarwal
1704839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang    @Override
17057c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu    public void markConversationMessagesUnread(final Conversation conv,
17067c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu            final Set<Uri> unreadMessageUris, final byte[] originalConversationInfo) {
17078f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang        // The only caller of this method is the conversation view, from where marking unread should
17088f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang        // *always* take you back to list mode.
17098f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang        showConversation(null);
17108f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang
1711839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        // locally mark conversation unread (the provider is supposed to propagate message unread
1712839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        // to conversation unread)
1713839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        conv.read = false;
17141faf93d79a00acb0fe607495c39f87d53a05889bPaul Westbrook        if (mConversationListCursor == null) {
171581230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal            LogUtils.d(LOG_TAG, "markConversationMessagesUnread(id=%d), deferring", conv.id);
17167c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu
17177c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu            mConversationListLoadFinishedCallbacks.add(new LoadFinishedCallback() {
17187c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu                @Override
17197c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu                public void onLoadFinished() {
17207c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu                    doMarkConversationMessagesUnread(conv, unreadMessageUris,
17217c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu                            originalConversationInfo);
17227c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu                }
17237c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu            });
17247c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu        } else {
172581230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal            LogUtils.d(LOG_TAG, "markConversationMessagesUnread(id=%d), performing", conv.id);
17267c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu            doMarkConversationMessagesUnread(conv, unreadMessageUris, originalConversationInfo);
17271faf93d79a00acb0fe607495c39f87d53a05889bPaul Westbrook        }
17287c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu    }
17291faf93d79a00acb0fe607495c39f87d53a05889bPaul Westbrook
17307c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu    private void doMarkConversationMessagesUnread(Conversation conv, Set<Uri> unreadMessageUris,
17317c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu            byte[] originalConversationInfo) {
173281230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal        // Only do a granular 'mark unread' if a subset of messages are unread
173328e31e2417d775b343919cf6018f85871e40a294Andy Huang        final int unreadCount = (unreadMessageUris == null) ? 0 : unreadMessageUris.size();
17340972e0793cc321670391d063348aecb5031b2677Mindy Pereira        final int numMessages = conv.getNumMessages();
17350972e0793cc321670391d063348aecb5031b2677Mindy Pereira        final boolean subsetIsUnread = (numMessages > 1 && unreadCount > 0
17360972e0793cc321670391d063348aecb5031b2677Mindy Pereira                && unreadCount < numMessages);
173728e31e2417d775b343919cf6018f85871e40a294Andy Huang
17389e4ca7993213d296043d20fe9cf4e166b5d595e8Andy Huang        LogUtils.d(LOG_TAG, "markConversationMessagesUnread(conv=%s)"
173981230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal                + ", numMessages=%d, unreadCount=%d, subsetIsUnread=%b",
17409e4ca7993213d296043d20fe9cf4e166b5d595e8Andy Huang                conv, numMessages, unreadCount, subsetIsUnread);
174128e31e2417d775b343919cf6018f85871e40a294Andy Huang        if (!subsetIsUnread) {
174266bc2aaf76bb0dfa4fc38f8fcbaaf9e413e321ddVikram Aggarwal            // Conversations are neither marked read, nor viewed, and we don't want to show
174366bc2aaf76bb0dfa4fc38f8fcbaaf9e413e321ddVikram Aggarwal            // the next conversation.
174481230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal            LogUtils.d(LOG_TAG, ". . doing full mark unread");
1745caaeed3837316175111bb16e6b60aeb0d523debfScott Kennedy            markConversationsRead(Collections.singletonList(conv), false, false, false);
1746839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        } else {
174781230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal            if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)) {
174881230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal                final ConversationInfo info = ConversationInfo.fromBlob(originalConversationInfo);
174981230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal                LogUtils.d(LOG_TAG, ". . doing subset mark unread, originalConversationInfo = %s",
175081230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal                        info);
175181230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal            }
1752daa06aba887e8e3748453f45ae52c1e571fe9f2eAndy Huang            mConversationListCursor.setConversationColumn(conv.uri, ConversationColumns.READ, 0);
1753839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang
175481230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal            // Locally update conversation's conversationInfo to revert to original version
175528e31e2417d775b343919cf6018f85871e40a294Andy Huang            if (originalConversationInfo != null) {
175628e31e2417d775b343919cf6018f85871e40a294Andy Huang                mConversationListCursor.setConversationColumn(conv.uri,
175728e31e2417d775b343919cf6018f85871e40a294Andy Huang                        ConversationColumns.CONVERSATION_INFO, originalConversationInfo);
175828e31e2417d775b343919cf6018f85871e40a294Andy Huang            }
1759839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang
1760839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang            // applyBatch with each CPO as an UPDATE op on each affected message uri
1761839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang            final ArrayList<ContentProviderOperation> ops = Lists.newArrayList();
1762839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang            String authority = null;
1763839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang            for (Uri messageUri : unreadMessageUris) {
1764839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang                if (authority == null) {
1765839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang                    authority = messageUri.getAuthority();
1766839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang                }
1767839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang                ops.add(ContentProviderOperation.newUpdate(messageUri)
1768839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang                        .withValue(UIProvider.MessageColumns.READ, 0)
1769839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang                        .build());
177081230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal                LogUtils.d(LOG_TAG, ". . Adding op: read=0, uri=%s", messageUri);
1771839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang            }
177281230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal            LogUtils.d(LOG_TAG, ". . operations = %s", ops);
1773839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang            new ContentProviderTask() {
1774839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang                @Override
1775839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang                protected void onPostExecute(Result result) {
1776d9895b65e074a2cd68ad2aaa87d7b49f05bacdbeVikram Aggarwal                    if (result.exception != null) {
1777d9895b65e074a2cd68ad2aaa87d7b49f05bacdbeVikram Aggarwal                        LogUtils.e(LOG_TAG, result.exception, "ContentProviderTask() ERROR.");
1778d9895b65e074a2cd68ad2aaa87d7b49f05bacdbeVikram Aggarwal                    } else {
17799e4ca7993213d296043d20fe9cf4e166b5d595e8Andy Huang                        LogUtils.d(LOG_TAG, "ContentProviderTask(): success %s",
17809e4ca7993213d296043d20fe9cf4e166b5d595e8Andy Huang                                Arrays.toString(result.results));
1781d9895b65e074a2cd68ad2aaa87d7b49f05bacdbeVikram Aggarwal                    }
1782839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang                }
1783839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang            }.run(mResolver, authority, ops);
1784839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        }
1785839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang    }
1786839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang
1787839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang    @Override
17887c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu    public void markConversationsRead(final Collection<Conversation> targets, final boolean read,
17897c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu            final boolean viewed) {
1790919d01a1144dc14f114860949ff4af79a9165ec7Scott Kennedy        LogUtils.d(LOG_TAG, "markConversationsRead(targets=%s)", targets.toArray());
1791919d01a1144dc14f114860949ff4af79a9165ec7Scott Kennedy
17927c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu        if (mConversationListCursor == null) {
179381230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal            if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)) {
179481230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal                LogUtils.d(LOG_TAG, "markConversationsRead(targets=%s), deferring",
179581230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal                        targets.toArray());
179681230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal            }
17977c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu            mConversationListLoadFinishedCallbacks.add(new LoadFinishedCallback() {
17987c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu                @Override
17997c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu                public void onLoadFinished() {
1800caaeed3837316175111bb16e6b60aeb0d523debfScott Kennedy                    markConversationsRead(targets, read, viewed, true);
18017c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu                }
18027c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu            });
18037c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu        } else {
18047c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu            // We want to show the next conversation if we are marking unread.
1805caaeed3837316175111bb16e6b60aeb0d523debfScott Kennedy            markConversationsRead(targets, read, viewed, true);
18067c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu        }
18078f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang    }
18088f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang
18090d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy    private void markConversationsRead(final Collection<Conversation> targets, final boolean read,
1810caaeed3837316175111bb16e6b60aeb0d523debfScott Kennedy            final boolean markViewed, final boolean showNext) {
18117c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu        LogUtils.d(LOG_TAG, "performing markConversationsRead");
1812cc754b102ed65df9d637b4e0a03e645214ea3cccVikram Aggarwal        // Auto-advance if requested and the current conversation is being marked unread
18138f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang        if (showNext && !read) {
18140d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy            final Runnable operation = new Runnable() {
18150d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy                @Override
18160d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy                public void run() {
1817caaeed3837316175111bb16e6b60aeb0d523debfScott Kennedy                    markConversationsRead(targets, read, markViewed, showNext);
18180d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy                }
18190d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy            };
18200d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy
1821caaeed3837316175111bb16e6b60aeb0d523debfScott Kennedy            if (!showNextConversation(targets, operation)) {
18220d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy                // This method will be called again if the user selects an autoadvance option
18230d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy                return;
18240d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy            }
18258f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang        }
18260d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy
1827cc754b102ed65df9d637b4e0a03e645214ea3cccVikram Aggarwal        final int size = targets.size();
1828cc754b102ed65df9d637b4e0a03e645214ea3cccVikram Aggarwal        final List<ConversationOperation> opList = new ArrayList<ConversationOperation>(size);
1829cc754b102ed65df9d637b4e0a03e645214ea3cccVikram Aggarwal        for (final Conversation target : targets) {
1830e2be97a58fbae98b95e51735c6468d121e465355Paul Westbrook            final ContentValues value = new ContentValues(4);
1831cc754b102ed65df9d637b4e0a03e645214ea3cccVikram Aggarwal            value.put(ConversationColumns.READ, read);
18325109c516b5cac279b4f99271953a27dd9a4828d8Paul Westbrook
1833d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy            // We never want to mark unseen here, but we do want to mark it seen
1834d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy            if (read || markViewed) {
1835d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy                value.put(ConversationColumns.SEEN, Boolean.TRUE);
1836d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy            }
1837d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy
18385109c516b5cac279b4f99271953a27dd9a4828d8Paul Westbrook            // The mark read/unread/viewed operations do not show an undo bar
18395109c516b5cac279b4f99271953a27dd9a4828d8Paul Westbrook            value.put(ConversationOperations.Parameters.SUPPRESS_UNDO, true);
184066bc2aaf76bb0dfa4fc38f8fcbaaf9e413e321ddVikram Aggarwal            if (markViewed) {
1841cc754b102ed65df9d637b4e0a03e645214ea3cccVikram Aggarwal                value.put(ConversationColumns.VIEWED, true);
184266bc2aaf76bb0dfa4fc38f8fcbaaf9e413e321ddVikram Aggarwal            }
1843192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal            final ConversationInfo info = target.conversationInfo;
1844edd6c1a2807d2ade930dfd4622707298dc470d64Tony Mantler            final boolean changed = info.markRead(read);
1845edd6c1a2807d2ade930dfd4622707298dc470d64Tony Mantler            if (changed) {
1846edd6c1a2807d2ade930dfd4622707298dc470d64Tony Mantler                value.put(ConversationColumns.CONVERSATION_INFO, info.toBlob());
1847839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang            }
1848cc754b102ed65df9d637b4e0a03e645214ea3cccVikram Aggarwal            opList.add(mConversationListCursor.getOperationForConversation(
1849cc754b102ed65df9d637b4e0a03e645214ea3cccVikram Aggarwal                    target, ConversationOperation.UPDATE, value));
1850cc754b102ed65df9d637b4e0a03e645214ea3cccVikram Aggarwal            // Update the local conversation objects so they immediately change state.
1851cc754b102ed65df9d637b4e0a03e645214ea3cccVikram Aggarwal            target.read = read;
1852cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang            if (markViewed) {
1853cc754b102ed65df9d637b4e0a03e645214ea3cccVikram Aggarwal                target.markViewed();
1854cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang            }
1855839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        }
18569e2d407fdafeb874e640eb84017feaf784309075Scott Kennedy        mConversationListCursor.updateBulkValues(opList);
1857839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang    }
1858839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang
18598f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang    /**
18608f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang     * Auto-advance to a different conversation if the currently visible conversation in
18618f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang     * conversation mode is affected (deleted, marked unread, etc.).
18628f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang     *
18630d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy     * <p>Does nothing if outside of conversation mode.</p>
18648f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang     *
18658f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang     * @param target the set of conversations being deleted/marked unread
18668f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang     */
18679365a826b46b0e274df88e92534f7d871eef2aa2mindyp    @Override
18680d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy    public void showNextConversation(final Collection<Conversation> target) {
1869caaeed3837316175111bb16e6b60aeb0d523debfScott Kennedy        showNextConversation(target, null);
18700d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy    }
18710d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy
18720d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy    /**
187330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao     * Helper function to determine if the provided set of conversations is in view
187430c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao     * @param target set of conversations that we are interested in
187530c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao     * @return true if they are in view, false otherwise
187630c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao     */
187730c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao    private boolean isCurrentConversationInView(final Collection<Conversation> target) {
187830c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        final int viewMode = mViewMode.getMode();
187930c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        return (viewMode == ViewMode.CONVERSATION
188030c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                || viewMode == ViewMode.SEARCH_RESULTS_CONVERSATION)
188130c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                && Conversation.contains(target, mCurrentConversation);
188230c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao    }
188330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao
188430c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao    /**
18850d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy     * Auto-advance to a different conversation if the currently visible conversation in
18860d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy     * conversation mode is affected (deleted, marked unread, etc.).
18870d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy     *
18880d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy     * <p>Does nothing if outside of conversation mode.</p>
1889c94d07f4460d00db898237603de311d226707a96Andy Huang     * <p>
1890c94d07f4460d00db898237603de311d226707a96Andy Huang     * Clients may pass an operation to execute on the target that this method will run after
1891c94d07f4460d00db898237603de311d226707a96Andy Huang     * auto-advance is complete. The operation, if provided, may run immediately, or it may run
1892c94d07f4460d00db898237603de311d226707a96Andy Huang     * later, or not at all. Reasons it may run later include:
1893c94d07f4460d00db898237603de311d226707a96Andy Huang     * <ul>
1894c94d07f4460d00db898237603de311d226707a96Andy Huang     * <li>the auto-advance setting is uninitialized and we need to wait for the user to set it</li>
1895c94d07f4460d00db898237603de311d226707a96Andy Huang     * <li>auto-advance in this configuration requires a mode change, and we need to wait for the
1896c94d07f4460d00db898237603de311d226707a96Andy Huang     * mode change transition to finish</li>
1897c94d07f4460d00db898237603de311d226707a96Andy Huang     * </ul>
1898c94d07f4460d00db898237603de311d226707a96Andy Huang     * <p>If the current conversation is not in the target collection, this method will do nothing,
1899c94d07f4460d00db898237603de311d226707a96Andy Huang     * and will not execute the operation.
19000d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy     *
19010d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy     * @param target the set of conversations being deleted/marked unread
1902c94d07f4460d00db898237603de311d226707a96Andy Huang     * @param operation (optional) the operation to execute after advancing
1903c94d07f4460d00db898237603de311d226707a96Andy Huang     * @return <code>false</code> if this method handled or will execute the operation,
1904c94d07f4460d00db898237603de311d226707a96Andy Huang     * <code>true</code> otherwise.
19050d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy     */
19060d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy    private boolean showNextConversation(final Collection<Conversation> target,
1907caaeed3837316175111bb16e6b60aeb0d523debfScott Kennedy            final Runnable operation) {
190830c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        if (isCurrentConversationInView(target)) {
19090d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy            final int autoAdvanceSetting = mAccount.settings.getAutoAdvanceSetting();
19100d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy
191193e6457cf4391f1aff025808b536135b5bdbfe39Tony Mantler            // If we don't have one set, but we're here, just take the default
191293e6457cf4391f1aff025808b536135b5bdbfe39Tony Mantler            final int autoAdvance = (autoAdvanceSetting == AutoAdvance.UNSET) ?
191393e6457cf4391f1aff025808b536135b5bdbfe39Tony Mantler                    AutoAdvance.DEFAULT : autoAdvanceSetting;
19140d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy
191593e6457cf4391f1aff025808b536135b5bdbfe39Tony Mantler            final Conversation next = mTracker.getNextConversation(autoAdvance, target);
191693e6457cf4391f1aff025808b536135b5bdbfe39Tony Mantler            LogUtils.d(LOG_TAG, "showNextConversation: showing %s next.", next);
191793e6457cf4391f1aff025808b536135b5bdbfe39Tony Mantler            // Set mAutoAdvanceOp *before* showConversation() to ensure that it runs when the
191893e6457cf4391f1aff025808b536135b5bdbfe39Tony Mantler            // transition doesn't run (i.e. it "completes" immediately).
191993e6457cf4391f1aff025808b536135b5bdbfe39Tony Mantler            mAutoAdvanceOp = operation;
192093e6457cf4391f1aff025808b536135b5bdbfe39Tony Mantler            showConversation(next);
192193e6457cf4391f1aff025808b536135b5bdbfe39Tony Mantler            return (mAutoAdvanceOp == null);
19220d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy        }
19230d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy
192493e6457cf4391f1aff025808b536135b5bdbfe39Tony Mantler        return true;
19258f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang    }
19268f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang
1927839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang    @Override
1928839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang    public void starMessage(ConversationMessage msg, boolean starred) {
1929839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        if (msg.starred == starred) {
1930839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang            return;
1931839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        }
1932839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang
1933839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        msg.starred = starred;
1934839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang
1935839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        // locally propagate the change to the owning conversation
1936839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        // (figure the provider will properly propagate the change when it commits it)
1937839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        //
1938839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        // when unstarring, only propagate the change if this was the only message starred
1939839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        final boolean conversationStarred = starred || msg.isConversationStarred();
1940cd12e82dead8a920b060340c037d51b144966a73Andy Huang        final Conversation conv = msg.getConversation();
1941cd12e82dead8a920b060340c037d51b144966a73Andy Huang        if (conversationStarred != conv.starred) {
1942cd12e82dead8a920b060340c037d51b144966a73Andy Huang            conv.starred = conversationStarred;
1943cd12e82dead8a920b060340c037d51b144966a73Andy Huang            mConversationListCursor.setConversationColumn(conv.uri,
1944839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang                    ConversationColumns.STARRED, conversationStarred);
1945839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        }
1946839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang
1947839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        final ContentValues values = new ContentValues(1);
1948839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        values.put(UIProvider.MessageColumns.STARRED, starred ? 1 : 0);
1949839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang
1950839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        new ContentProviderTask.UpdateTask() {
1951839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang            @Override
1952839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang            protected void onPostExecute(Result result) {
1953839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang                // TODO: handle errors?
1954839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang            }
1955839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        }.run(mResolver, msg.uri, values, null /* selection*/, null /* selectionArgs */);
1956839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang    }
1957839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang
195812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang    @Override
1959486e63e5bc7a88455e6fcb83d9cb251df203f727Alice Yang    public void requestFolderRefresh() {
196037dda440aaa245cef2d21d17babe951e1d449c4aAlice Yang        if (mFolder == null) {
196137dda440aaa245cef2d21d17babe951e1d449c4aAlice Yang            return;
196237dda440aaa245cef2d21d17babe951e1d449c4aAlice Yang        }
196337dda440aaa245cef2d21d17babe951e1d449c4aAlice Yang        final ConversationListFragment convList = getConversationListFragment();
196437dda440aaa245cef2d21d17babe951e1d449c4aAlice Yang        if (convList == null) {
196537dda440aaa245cef2d21d17babe951e1d449c4aAlice Yang            // This could happen if this account is in initial sync (user
196637dda440aaa245cef2d21d17babe951e1d449c4aAlice Yang            // is seeing the "your mail will appear shortly" message)
196737dda440aaa245cef2d21d17babe951e1d449c4aAlice Yang            return;
196837dda440aaa245cef2d21d17babe951e1d449c4aAlice Yang        }
196937dda440aaa245cef2d21d17babe951e1d449c4aAlice Yang        convList.showSyncStatusBar();
19700d74a662f6e96b5a21e37978eb6e3b46545e3cbfAlice Yang
197137dda440aaa245cef2d21d17babe951e1d449c4aAlice Yang        if (mAsyncRefreshTask != null) {
197237dda440aaa245cef2d21d17babe951e1d449c4aAlice Yang            mAsyncRefreshTask.cancel(true);
197328e0c34ad35925fc74633bec66344911f2de6a22Mindy Pereira        }
197437dda440aaa245cef2d21d17babe951e1d449c4aAlice Yang        mAsyncRefreshTask = new AsyncRefreshTask(mContext, mFolder.refreshUri);
197537dda440aaa245cef2d21d17babe951e1d449c4aAlice Yang        mAsyncRefreshTask.execute();
197628e0c34ad35925fc74633bec66344911f2de6a22Mindy Pereira    }
197728e0c34ad35925fc74633bec66344911f2de6a22Mindy Pereira
1978fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira    /**
1979fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira     * Confirm (based on user's settings) and delete a conversation from the conversation list and
1980fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira     * from the database.
1981b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal     * @param actionId the ID of the menu item that caused the delete: R.id.delete, R.id.archive...
19823d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal     * @param target the conversations to act upon
19833d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal     * @param showDialog true if a confirmation dialog is to be shown, false otherwise.
19843d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal     * @param confirmResource the resource ID of the string that is shown in the confirmation dialog
1985fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira     */
1986b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal    private void confirmAndDelete(int actionId, final Collection<Conversation> target,
198730c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            boolean showDialog, int confirmResource, UndoCallback undoCallback) {
198884fe99431df3b989dff9b363562f09fd2757b63cVikram Aggarwal        final boolean isBatch = false;
1989fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira        if (showDialog) {
199030c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            makeDialogListener(actionId, isBatch, undoCallback);
19913d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal            final CharSequence message = Utils.formatPlural(mContext, confirmResource,
19923d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal                    target.size());
1993b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal            final ConfirmDialogFragment c = ConfirmDialogFragment.newInstance(message);
1994b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal            c.displayDialog(mActivity.getFragmentManager());
1995fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira        } else {
199630c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            delete(0, target, getDeferredAction(actionId, target, isBatch, undoCallback), isBatch);
1997fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira        }
1998fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira    }
1999fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira
20004f4782bcc53e13b30f1fd417beb1a6c04a54faedVikram Aggarwal    @Override
20010d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy    public void delete(final int actionId, final Collection<Conversation> target,
2002caaeed3837316175111bb16e6b60aeb0d523debfScott Kennedy                       final DestructiveAction action, final boolean isBatch) {
200384f7d32bdc79263004ed5241480988e02f8e618cmindyp        // Order of events is critical! The Conversation View Fragment must be
200484f7d32bdc79263004ed5241480988e02f8e618cmindyp        // notified of the next conversation with showConversation(next) *before* the
200584f7d32bdc79263004ed5241480988e02f8e618cmindyp        // conversation list
2006192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal        // fragment has a chance to delete the conversation, animating it away.
2007192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal
200884f7d32bdc79263004ed5241480988e02f8e618cmindyp        // Update the conversation fragment if the current conversation is
200984f7d32bdc79263004ed5241480988e02f8e618cmindyp        // deleted.
20100d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy        final Runnable operation = new Runnable() {
20110d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy            @Override
20120d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy            public void run() {
2013caaeed3837316175111bb16e6b60aeb0d523debfScott Kennedy                delete(actionId, target, action, isBatch);
20140d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy            }
20150d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy        };
20160d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy
2017caaeed3837316175111bb16e6b60aeb0d523debfScott Kennedy        if (!showNextConversation(target, operation)) {
20180d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy            // This method will be called again if the user selects an autoadvance option
20190d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy            return;
20200d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy        }
2021ab1b5b6d16800ff675116cdab33cf26ab3514732Vikram Aggarwal        // If the conversation is in the selected set, remove it from the set.
202284fe99431df3b989dff9b363562f09fd2757b63cVikram Aggarwal        // Batch selections are cleared in the end of the action, so not done for batch actions.
202384fe99431df3b989dff9b363562f09fd2757b63cVikram Aggarwal        if (!isBatch) {
202484fe99431df3b989dff9b363562f09fd2757b63cVikram Aggarwal            for (final Conversation conv : target) {
202584fe99431df3b989dff9b363562f09fd2757b63cVikram Aggarwal                if (mSelectedSet.contains(conv)) {
202697ae7847b3a0eac8327b5f6300c854d1c9a19c3eVikram Aggarwal                    mSelectedSet.toggle(conv);
202784fe99431df3b989dff9b363562f09fd2757b63cVikram Aggarwal                }
2028ab1b5b6d16800ff675116cdab33cf26ab3514732Vikram Aggarwal            }
2029ab1b5b6d16800ff675116cdab33cf26ab3514732Vikram Aggarwal        }
2030192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal        // The conversation list deletes and performs the action if it exists.
2031192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal        final ConversationListFragment convListFragment = getConversationListFragment();
2032192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal        if (convListFragment != null) {
2033193e05a8c38b87c400ac594b53b54eb211d35602Alice Yang            LogUtils.i(LOG_TAG, "AAC.requestDelete: ListFragment is handling delete.");
2034669947bf67a3d0eed377b18099bb873604ad5009Vikram Aggarwal            convListFragment.requestDelete(actionId, target, action);
2035192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal            return;
2036192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal        }
203784f7d32bdc79263004ed5241480988e02f8e618cmindyp        // No visible UI element handled it on our behalf. Perform the action
203884f7d32bdc79263004ed5241480988e02f8e618cmindyp        // ourself.
2039193e05a8c38b87c400ac594b53b54eb211d35602Alice Yang        LogUtils.i(LOG_TAG, "ACC.requestDelete: performing remove action ourselves");
2040d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal        action.performAction();
2041d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal    }
2042d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal
2043d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal    /**
2044d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal     * Requests that the action be performed and the UI state is updated to reflect the new change.
2045715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @param action the action to be performed, specified as a menu id: R.id.archive, ...
2046d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal     */
2047715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal    private void requestUpdate(final DestructiveAction action) {
2048d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal        action.performAction();
2049d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal        refreshConversationList();
205075daee52f08c9bf66652b9bddf42cfbd1ca71987Vikram Aggarwal    }
20516fbc87affe46f6d49c584a307ec836e68163c182Vikram Aggarwal
20526fbc87affe46f6d49c584a307ec836e68163c182Vikram Aggarwal    @Override
20531ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    public void onPrepareDialog(int id, Dialog dialog, Bundle bundle) {
20541ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal        // TODO(viki): Auto-generated method stub
20551ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    }
20561ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal
20571ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    @Override
20581ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    public boolean onPrepareOptionsMenu(Menu menu) {
20592d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein        return mActionBarController.onPrepareOptionsMenu(menu);
2060a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    }
2061a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal
206268f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira    @Override
206368f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira    public void onPause() {
2064c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal        mHaveAccountList = false;
20656ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook        enableNotifications();
206694e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    }
206794e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
2068a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    @Override
20691ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    public void onResume() {
20706ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook        // Register the receiver that will prevent the status receiver from
20716ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook        // displaying its notification icon as long as we're running.
20726ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook        // The SupressNotificationReceiver will block the broadcast if we're looking at the folder
20736ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook        // that the notification was received for.
20746ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook        disableNotifications();
20751ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang
20761ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        mSafeToModifyFragments = true;
2077ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein
2078ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein        attachEmptyFolderDialogFragmentListener();
207923e3313977d35d5caf152baf6cb27c9cc2d8ab91Andrew Sapperstein
208023e3313977d35d5caf152baf6cb27c9cc2d8ab91Andrew Sapperstein        // Invalidating the options menu so that when we make changes in settings,
208123e3313977d35d5caf152baf6cb27c9cc2d8ab91Andrew Sapperstein        // the changes will always be updated in the action bar/options menu/
208223e3313977d35d5caf152baf6cb27c9cc2d8ab91Andrew Sapperstein        mActivity.invalidateOptionsMenu();
20831ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    }
20841ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal
20851ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    @Override
2086a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    public void onSaveInstanceState(Bundle outState) {
2087649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal        mViewMode.handleSaveInstanceState(outState);
20886c51158ad3269f157424e6c7bd488425c98da08fVikram Aggarwal        if (mAccount != null) {
20896c51158ad3269f157424e6c7bd488425c98da08fVikram Aggarwal            outState.putParcelable(SAVED_ACCOUNT, mAccount);
20906c51158ad3269f157424e6c7bd488425c98da08fVikram Aggarwal        }
20915e478d2c4087b50764756b0edc95941cb6094f68Mindy Pereira        if (mFolder != null) {
20925e478d2c4087b50764756b0edc95941cb6094f68Mindy Pereira            outState.putParcelable(SAVED_FOLDER, mFolder);
20938b15263097bd9ce1a28db981b6a85261e6cb6886Vikram Aggarwal        }
2094f3341405c97ff6d27a0d5fa37f6031e1c395d161Vikram Aggarwal        // If this is a search activity, let's store the search query term as well.
2095f3341405c97ff6d27a0d5fa37f6031e1c395d161Vikram Aggarwal        if (ConversationListContext.isSearchResult(mConvListContext)) {
2096f3341405c97ff6d27a0d5fa37f6031e1c395d161Vikram Aggarwal            outState.putString(SAVED_QUERY, mConvListContext.searchQuery);
2097f3341405c97ff6d27a0d5fa37f6031e1c395d161Vikram Aggarwal        }
209849e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal        if (mCurrentConversation != null && mViewMode.isConversationMode()) {
209926f23fc8cc0d49bc0bea1eeec4c0c4742928f693Mindy Pereira            outState.putParcelable(SAVED_CONVERSATION, mCurrentConversation);
210026f23fc8cc0d49bc0bea1eeec4c0c4742928f693Mindy Pereira        }
21014556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang        if (!mSelectedSet.isEmpty()) {
2102cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal            outState.putParcelable(SAVED_SELECTED_SET, mSelectedSet);
21034556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang        }
2104d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira        if (mToastBar.getVisibility() == View.VISIBLE) {
2105d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira            outState.putParcelable(SAVED_TOAST_BAR_OP, mToastBar.getOperation());
2106d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira        }
2107649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal        final ConversationListFragment convListFragment = getConversationListFragment();
2108d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira        if (convListFragment != null) {
2109649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal            convListFragment.getAnimatedAdapter().onSaveInstanceState(outState);
2110d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira        }
2111b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal        // If there is a dialog being shown, save the state so we can create a listener for it.
21126cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal        if (mDialogAction != -1) {
21136cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal            outState.putInt(SAVED_ACTION, mDialogAction);
2114b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal            outState.putBoolean(SAVED_ACTION_FROM_SELECTED, mDialogFromSelectedSet);
21156cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal        }
2116a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        if (mDetachedConvUri != null) {
2117a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal            outState.putParcelable(SAVED_DETACHED_CONV_URI, mDetachedConvUri);
2118a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        }
21195747e152fa606c20261b18431f4a9f0ff3be7b5aAndrew Sapperstein
2120b10212ef23a4e2317fc43c62dff9ad177b5bcf2cScott Kennedy        outState.putParcelable(SAVED_HIERARCHICAL_FOLDER, mFolderListFolder);
21215747e152fa606c20261b18431f4a9f0ff3be7b5aAndrew Sapperstein        mSafeToModifyFragments = false;
2122ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang
2123ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang        outState.putParcelable(SAVED_INBOX_KEY, mInbox);
2124f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy
2125f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy        outState.putBundle(SAVED_CONVERSATION_LIST_SCROLL_POSITIONS,
2126f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy                mConversationListScrollPositions);
21271ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    }
21281ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang
21291ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    /**
21301ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang     * @see #mSafeToModifyFragments
21311ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang     */
21321ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    protected boolean safeToModifyFragments() {
21331ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        return mSafeToModifyFragments;
2134a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    }
2135a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal
2136a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    @Override
2137313ac13095a386be366bd25f8106fbb34b4a7098Andy Huang    public void executeSearch(String query) {
2138779dd6012a6c5b4d71255085059e57d94eab1a3fJin Cao        AnalyticsTimer.getInstance().trackStart(AnalyticsTimer.SEARCH_TO_LIST);
213968f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira        Intent intent = new Intent();
214068f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira        intent.setAction(Intent.ACTION_SEARCH);
214168f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira        intent.putExtra(ConversationListContext.EXTRA_SEARCH_QUERY, query);
214268f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira        intent.putExtra(Utils.EXTRA_ACCOUNT, mAccount);
214368f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira        intent.setComponent(mActivity.getComponentName());
21442d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein        mActionBarController.collapseSearch();
2145371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon        // Call startActivityForResult here so we can tell if we have navigated to a different folder
2146371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon        // or account from search results.
2147371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon        mActivity.startActivityForResult(intent, CHANGE_NAVIGATION_REQUEST_CODE);
2148a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    }
2149a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal
2150a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    @Override
21511ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    public void onStop() {
2152cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedy        NotificationActionUtils.unregisterUndoNotificationObserver(mUndoNotificationObserver);
2153a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    }
2154a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal
2155632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
2156632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public void onDestroy() {
2157b2ef9c18d1f68543d9c782e1952b70d3bbcd94d6Andy Huang        // stop listening to the cursor on e.g. configuration changes
2158b2ef9c18d1f68543d9c782e1952b70d3bbcd94d6Andy Huang        if (mConversationListCursor != null) {
2159b2ef9c18d1f68543d9c782e1952b70d3bbcd94d6Andy Huang            mConversationListCursor.removeListener(this);
2160b2ef9c18d1f68543d9c782e1952b70d3bbcd94d6Andy Huang        }
2161144bfe739b93afdee0a1700a34806b0b287e5887Andy Huang        mDrawIdler.setListener(null);
2162144bfe739b93afdee0a1700a34806b0b287e5887Andy Huang        mDrawIdler.setRootView(null);
2163632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        // unregister the ViewPager's observer on the conversation cursor
2164632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        mPagerController.onDestroy();
21652d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein        mActionBarController.onDestroy();
21667c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal        mRecentFolderList.destroy();
21674e0158f03fd8f430e88394e8b99e1b65ed01838aAndy Huang        mDestroyed = true;
216859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal        mHandler.removeCallbacks(mLogServiceChecker);
216959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal        mLogServiceChecker = null;
2170632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
2171632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
2172fa131a2ff399fd1d544f759b063268fb4e8a3e70Vikram Aggarwal    /**
217393dc202420c3bf8adf4d9e8ae18c8e30e3cb1959Vikram Aggarwal     * Set the Action Bar icon according to the mode. The Action Bar icon can contain a back button
217493dc202420c3bf8adf4d9e8ae18c8e30e3cb1959Vikram Aggarwal     * or not. The individual controller is responsible for changing the icon based on the mode.
217593dc202420c3bf8adf4d9e8ae18c8e30e3cb1959Vikram Aggarwal     */
217693dc202420c3bf8adf4d9e8ae18c8e30e3cb1959Vikram Aggarwal    protected abstract void resetActionBarIcon();
217793dc202420c3bf8adf4d9e8ae18c8e30e3cb1959Vikram Aggarwal
217893dc202420c3bf8adf4d9e8ae18c8e30e3cb1959Vikram Aggarwal    /**
2179161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * {@inheritDoc} Subclasses must override this to listen to mode changes
2180161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * from the ViewMode. Subclasses <b>must</b> call the parent's
2181161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * onViewModeChanged since the parent will handle common state changes.
2182fa131a2ff399fd1d544f759b063268fb4e8a3e70Vikram Aggarwal     */
2183a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    @Override
2184fa131a2ff399fd1d544f759b063268fb4e8a3e70Vikram Aggarwal    public void onViewModeChanged(int newMode) {
21851a1e9754cce66296ae730f2c483f55fcf1a24359James Lemieux        // The floating action compose button is only visible in the conversation/search lists
21861a1e9754cce66296ae730f2c483f55fcf1a24359James Lemieux        final int composeVisible = ViewMode.isListMode(newMode) ? View.VISIBLE : View.GONE;
21871a1e9754cce66296ae730f2c483f55fcf1a24359James Lemieux        mFloatingComposeButton.setVisibility(composeVisible);
21881a1e9754cce66296ae730f2c483f55fcf1a24359James Lemieux
218949e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal        // When we step away from the conversation mode, we don't have a current conversation
219049e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal        // anymore. Let's blank it out so clients calling getCurrentConversation are not misled.
219149e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal        if (!ViewMode.isConversationMode(newMode)) {
219249e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal            setCurrentConversation(null);
219349e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal        }
21946c570db71b8adc43d7dc2f31e6fa63cf50c1426eAndrew Sapperstein
219593dc202420c3bf8adf4d9e8ae18c8e30e3cb1959Vikram Aggarwal        // If the viewmode is not set, preserve existing icon.
219693dc202420c3bf8adf4d9e8ae18c8e30e3cb1959Vikram Aggarwal        if (newMode != ViewMode.UNKNOWN) {
219793dc202420c3bf8adf4d9e8ae18c8e30e3cb1959Vikram Aggarwal            resetActionBarIcon();
219893dc202420c3bf8adf4d9e8ae18c8e30e3cb1959Vikram Aggarwal        }
219912b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang
220012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        if (isDrawerEnabled()) {
2201dfbc6985e44c05e37fd8addce65a063ff57a3dc1Vikram Aggarwal            /** If the folder doesn't exist, or its parent URI is empty,
2202dfbc6985e44c05e37fd8addce65a063ff57a3dc1Vikram Aggarwal             * this is not a child folder */
22039695e0046f79bd2d7166a411d6feff4cf0fb2539Jin Cao            final boolean isTopLevel = Folder.isRoot(mFolder);
2204dfbc6985e44c05e37fd8addce65a063ff57a3dc1Vikram Aggarwal            mDrawerToggle.setDrawerIndicatorEnabled(
2205dfbc6985e44c05e37fd8addce65a063ff57a3dc1Vikram Aggarwal                    getShouldShowDrawerIndicator(newMode, isTopLevel));
2206371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon            mDrawerContainer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
220712b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            closeDrawerIfOpen();
220812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
2209a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    }
2210a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal
2211dfbc6985e44c05e37fd8addce65a063ff57a3dc1Vikram Aggarwal    /**
2212dfbc6985e44c05e37fd8addce65a063ff57a3dc1Vikram Aggarwal     * Returns true if the drawer icon is shown
2213dfbc6985e44c05e37fd8addce65a063ff57a3dc1Vikram Aggarwal     * @param viewMode the current view mode
2214dfbc6985e44c05e37fd8addce65a063ff57a3dc1Vikram Aggarwal     * @param isTopLevel true if the current folder is not a child
2215dfbc6985e44c05e37fd8addce65a063ff57a3dc1Vikram Aggarwal     * @return whether the drawer indicator is shown
2216dfbc6985e44c05e37fd8addce65a063ff57a3dc1Vikram Aggarwal     */
221781b5fa063cb17ac7caf5622e83462151d03e0ecdScott Kennedy    private boolean getShouldShowDrawerIndicator(final int viewMode,
2218dfbc6985e44c05e37fd8addce65a063ff57a3dc1Vikram Aggarwal            final boolean isTopLevel) {
2219dfbc6985e44c05e37fd8addce65a063ff57a3dc1Vikram Aggarwal        // If search list/conv mode: disable indicator
2220dfbc6985e44c05e37fd8addce65a063ff57a3dc1Vikram Aggarwal        // Indicator is enabled either in conversation list or folder list mode.
222181b5fa063cb17ac7caf5622e83462151d03e0ecdScott Kennedy        return isDrawerEnabled() && !ViewMode.isSearchMode(viewMode)
2222aded57845c51f128fc5dbd8e992697823775ce5aScott Kennedy            && (viewMode == ViewMode.CONVERSATION_LIST  && isTopLevel);
22238a72b8590d9166ada55f69eeb083d2b8c5c915c9Scott Kennedy    }
22248a72b8590d9166ada55f69eeb083d2b8c5c915c9Scott Kennedy
22253825f3d2284b2b57fadcfe6a4ebd9992f3c5c7bbAndy Huang    public void disablePagerUpdates() {
22263825f3d2284b2b57fadcfe6a4ebd9992f3c5c7bbAndy Huang        mPagerController.stopListening();
22273825f3d2284b2b57fadcfe6a4ebd9992f3c5c7bbAndy Huang    }
22283825f3d2284b2b57fadcfe6a4ebd9992f3c5c7bbAndy Huang
22294e0158f03fd8f430e88394e8b99e1b65ed01838aAndy Huang    public boolean isDestroyed() {
22304e0158f03fd8f430e88394e8b99e1b65ed01838aAndy Huang        return mDestroyed;
22314e0158f03fd8f430e88394e8b99e1b65ed01838aAndy Huang    }
22324e0158f03fd8f430e88394e8b99e1b65ed01838aAndy Huang
223354f120f6ada40c7191811dfff99b151e9e192c78mindyp    @Override
223454f120f6ada40c7191811dfff99b151e9e192c78mindyp    public void commitDestructiveActions(boolean animate) {
2235c6adce3cf6887c4c6dd5005724565702751843d0mindyp        ConversationListFragment fragment = getConversationListFragment();
22361e2573bbba613fb700e8fa01c58c9382c0027b5eMindy Pereira        if (fragment != null) {
2237c6adce3cf6887c4c6dd5005724565702751843d0mindyp            fragment.commitDestructiveActions(animate);
22381e2573bbba613fb700e8fa01c58c9382c0027b5eMindy Pereira        }
22391e2573bbba613fb700e8fa01c58c9382c0027b5eMindy Pereira    }
22401e2573bbba613fb700e8fa01c58c9382c0027b5eMindy Pereira
2241a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    @Override
22421ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    public void onWindowFocusChanged(boolean hasFocus) {
224369b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        final ConversationListFragment convList = getConversationListFragment();
22446422b8d051c93b4f34580c551254fc50c9efa4dbVikram Aggarwal        // hasFocus already ensures that the window is in focus, so we don't need to call
22456422b8d051c93b4f34580c551254fc50c9efa4dbVikram Aggarwal        // AAC.isFragmentVisible(convList) here.
22469f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        if (hasFocus && convList != null && convList.isVisible()) {
22479f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook            // The conversation list is visible.
224869b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            informCursorVisiblity(true);
22499f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        }
2250a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    }
2251a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal
2252ca716f160fa033728fd4e60bcd13a4dc8693ee20Vikram Aggarwal    /**
2253ca716f160fa033728fd4e60bcd13a4dc8693ee20Vikram Aggarwal     * Set the account, and carry out all the account-related changes that rely on this.
2254715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @param account new account to set to.
2255ca716f160fa033728fd4e60bcd13a4dc8693ee20Vikram Aggarwal     */
225675181e846462659d1c9b919d0f1250e34647a280Mindy Pereira    private void setAccount(Account account) {
2257b9ca979fb93cc14246b449a2c02dc615c9e0cdd9Andy Huang        if (account == null) {
2258b9ca979fb93cc14246b449a2c02dc615c9e0cdd9Andy Huang            LogUtils.w(LOG_TAG, new Error(),
2259b9ca979fb93cc14246b449a2c02dc615c9e0cdd9Andy Huang                    "AAC ignoring null (presumably invalid) account restoration");
2260b9ca979fb93cc14246b449a2c02dc615c9e0cdd9Andy Huang            return;
2261b9ca979fb93cc14246b449a2c02dc615c9e0cdd9Andy Huang        }
2262b114841b6f2a91d92b8d0af4ec05ce81a5ba5599Andy Huang        LogUtils.d(LOG_TAG, "AbstractActivityController.setAccount(): account = %s", account.uri);
226391e8737780f631a65633434447b0da864beb5baeVikram Aggarwal        mAccount = account;
2264ca716f160fa033728fd4e60bcd13a4dc8693ee20Vikram Aggarwal        // Only change AAC state here. Do *not* modify any other object's state. The object
2265ca716f160fa033728fd4e60bcd13a4dc8693ee20Vikram Aggarwal        // should listen on account changes.
2266177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        restartOptionalLoader(LOADER_RECENT_FOLDERS, mFolderCallbacks, Bundle.EMPTY);
2267ca716f160fa033728fd4e60bcd13a4dc8693ee20Vikram Aggarwal        mActivity.invalidateOptionsMenu();
2268ca716f160fa033728fd4e60bcd13a4dc8693ee20Vikram Aggarwal        disableNotificationsOnAccountChange(mAccount);
2269177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        restartOptionalLoader(LOADER_ACCOUNT_UPDATE_CURSOR, mAccountCallbacks, Bundle.EMPTY);
22702fe343f37226123e7cd5741d40be606dcf4edaabVikram Aggarwal        // The Mail instance can be null during test runs.
22712fe343f37226123e7cd5741d40be606dcf4edaabVikram Aggarwal        final MailAppProvider instance = MailAppProvider.getInstance();
22722fe343f37226123e7cd5741d40be606dcf4edaabVikram Aggarwal        if (instance != null) {
22732fe343f37226123e7cd5741d40be606dcf4edaabVikram Aggarwal            instance.setLastViewedAccount(mAccount.uri.toString());
22742fe343f37226123e7cd5741d40be606dcf4edaabVikram Aggarwal        }
227591e8737780f631a65633434447b0da864beb5baeVikram Aggarwal        if (account.settings == null) {
227691e8737780f631a65633434447b0da864beb5baeVikram Aggarwal            LogUtils.w(LOG_TAG, new Error(), "AAC ignoring account with null settings.");
227791e8737780f631a65633434447b0da864beb5baeVikram Aggarwal            return;
227891e8737780f631a65633434447b0da864beb5baeVikram Aggarwal        }
22797c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal        mAccountObservers.notifyChanged();
228034f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal        perhapsEnterWaitMode();
228175181e846462659d1c9b919d0f1250e34647a280Mindy Pereira    }
228275181e846462659d1c9b919d0f1250e34647a280Mindy Pereira
22831ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    /**
2284161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * Restore the state from the previous bundle. Subclasses should call this
2285161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * method from the parent class, since it performs important UI
2286161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * initialization.
2287161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     *
2288715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @param savedState previous state
22891ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal     */
2290632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
2291632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public void onRestoreInstanceState(Bundle savedState) {
2292a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        mDetachedConvUri = savedState.getParcelable(SAVED_DETACHED_CONV_URI);
2293632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        if (savedState.containsKey(SAVED_CONVERSATION)) {
2294632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            // Open the conversation.
22952bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang            final Conversation conversation = savedState.getParcelable(SAVED_CONVERSATION);
2296534e4a2175233b78a47bf0cd31ab8e4fc005ca71Paul Westbrook            if (conversation != null && conversation.position < 0) {
2297534e4a2175233b78a47bf0cd31ab8e4fc005ca71Paul Westbrook                // Set the position to 0 on this conversation, as we don't know where it is
2298534e4a2175233b78a47bf0cd31ab8e4fc005ca71Paul Westbrook                // in the list
2299534e4a2175233b78a47bf0cd31ab8e4fc005ca71Paul Westbrook                conversation.position = 0;
2300534e4a2175233b78a47bf0cd31ab8e4fc005ca71Paul Westbrook            }
2301ed4fdf001005b0d17a1f3150366e8ba2dc0204a4Andy Huang            showConversation(conversation);
2302632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
2303632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
2304d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira        if (savedState.containsKey(SAVED_TOAST_BAR_OP)) {
23052bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang            ToastBarOperation op = savedState.getParcelable(SAVED_TOAST_BAR_OP);
2306d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira            if (op != null) {
2307d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira                if (op.getType() == ToastBarOperation.UNDO) {
2308d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira                    onUndoAvailable(op);
23099d7519d18e8ddbcdd59b37eec7c85210fca0e8c2Andrew Sapperstein                } else if (op.getType() == ToastBarOperation.ERROR) {
23109d7519d18e8ddbcdd59b37eec7c85210fca0e8c2Andrew Sapperstein                    onError(mFolder, true);
2311d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira                }
2312d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira            }
2313d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira        }
2314b10212ef23a4e2317fc43c62dff9ad177b5bcf2cScott Kennedy        mFolderListFolder = savedState.getParcelable(SAVED_HIERARCHICAL_FOLDER);
2315649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal        final ConversationListFragment convListFragment = getConversationListFragment();
2316d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira        if (convListFragment != null) {
2317649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal            convListFragment.getAnimatedAdapter().onRestoreInstanceState(savedState);
2318d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira        }
2319b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal        /*
2320632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang         * Restore the state of selected conversations. This needs to be done after the correct mode
2321632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang         * is set and the action bar is fully initialized. If not, several key pieces of state
2322632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang         * information will be missing, and the split views may not be initialized correctly.
2323632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang         */
2324632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        restoreSelectedConversations(savedState);
2325b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal        // Order is important!!!
2326b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal        // The dialog listener needs to happen *after* the selected set is restored.
2327b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal
2328b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal        // If there has been an orientation change, and we need to recreate the listener for the
2329b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal        // confirm dialog fragment (delete/archive/...), then do it here.
2330b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal        if (mDialogAction != -1) {
233130c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            makeDialogListener(mDialogAction, mDialogFromSelectedSet,
233230c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                    getUndoCallbackForDestructiveActionsWithAutoAdvance(
233330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                            mDialogAction, mCurrentConversation));
2334b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal        }
2335ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang
2336ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang        mInbox = savedState.getParcelable(SAVED_INBOX_KEY);
2337f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy
2338f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy        mConversationListScrollPositions.clear();
2339f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy        mConversationListScrollPositions.putAll(
2340f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy                savedState.getBundle(SAVED_CONVERSATION_LIST_SCROLL_POSITIONS));
2341632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
2342632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
2343649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal    /**
2344649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal     * Handle an intent to open the app. This method is called only when there is no saved state,
2345649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal     * so we need to set state that wasn't set before. It is correct to change the viewmode here
2346649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal     * since it has not been previously set.
23476aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *
23486aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     * This method is called for a subset of the reasons mentioned in
23496aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     * {@link #onCreate(android.os.Bundle)}. Notably, this is called when launching the app from
23506aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     * notifications, widgets, and shortcuts.
2351715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @param intent intent passed to the activity.
2352649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal     */
2353632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    private void handleIntent(Intent intent) {
2354e6459424b3e46d768865c908af1caa932f783095Andy Huang        LogUtils.d(LOG_TAG, "IN AAC.handleIntent. action=%s", intent.getAction());
2355632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        if (Intent.ACTION_VIEW.equals(intent.getAction())) {
2356632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            if (intent.hasExtra(Utils.EXTRA_ACCOUNT)) {
235726a20758413f0f8629ea71a0943304e3ae740165Tony Mantler                setAccount(Account.newInstance(intent.getStringExtra(Utils.EXTRA_ACCOUNT)));
2358632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            }
2359b9ca979fb93cc14246b449a2c02dc615c9e0cdd9Andy Huang            if (mAccount == null) {
2360b9ca979fb93cc14246b449a2c02dc615c9e0cdd9Andy Huang                return;
23615e478d2c4087b50764756b0edc95941cb6094f68Mindy Pereira            }
23621a249e094e961f463ff392390d591a1d37106e69Vikram Aggarwal            final boolean isConversationMode = intent.hasExtra(Utils.EXTRA_CONVERSATION);
23634fe0af81874976a1995191321e35c844b2229811Andy Huang
23644fe0af81874976a1995191321e35c844b2229811Andy Huang            if (intent.getBooleanExtra(Utils.EXTRA_FROM_NOTIFICATION, false)) {
23654fe0af81874976a1995191321e35c844b2229811Andy Huang                Analytics.getInstance().setCustomDimension(Analytics.CD_INDEX_ACCOUNT_TYPE,
236679b1156803585952a4851221dc9fa9974e606433Tony Mantler                        AnalyticsUtils.getAccountTypeForAccount(mAccount.getEmailAddress()));
23674fe0af81874976a1995191321e35c844b2229811Andy Huang                Analytics.getInstance().sendEvent("notification_click",
23684fe0af81874976a1995191321e35c844b2229811Andy Huang                        isConversationMode ? "conversation" : "conversation_list", null, 0);
23694fe0af81874976a1995191321e35c844b2229811Andy Huang            }
23704fe0af81874976a1995191321e35c844b2229811Andy Huang
2371649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal            if (isConversationMode && mViewMode.getMode() == ViewMode.UNKNOWN) {
23721a249e094e961f463ff392390d591a1d37106e69Vikram Aggarwal                mViewMode.enterConversationMode();
23731a249e094e961f463ff392390d591a1d37106e69Vikram Aggarwal            } else {
23741a249e094e961f463ff392390d591a1d37106e69Vikram Aggarwal                mViewMode.enterConversationListMode();
23751a249e094e961f463ff392390d591a1d37106e69Vikram Aggarwal            }
2376177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            // Put the folder and conversation, and ask the loader to create this folder.
2377177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            final Bundle args = new Bundle();
237848cfe4613549cafdf36e2a524afba730522bf291Scott Kennedy
237948cfe4613549cafdf36e2a524afba730522bf291Scott Kennedy            final Uri folderUri;
238048cfe4613549cafdf36e2a524afba730522bf291Scott Kennedy            if (intent.hasExtra(Utils.EXTRA_FOLDER_URI)) {
23815bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein                folderUri = intent.getParcelableExtra(Utils.EXTRA_FOLDER_URI);
238248cfe4613549cafdf36e2a524afba730522bf291Scott Kennedy            } else if (intent.hasExtra(Utils.EXTRA_FOLDER)) {
238348cfe4613549cafdf36e2a524afba730522bf291Scott Kennedy                final Folder folder =
238448cfe4613549cafdf36e2a524afba730522bf291Scott Kennedy                        Folder.fromString(intent.getStringExtra(Utils.EXTRA_FOLDER));
2385259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy                folderUri = folder.folderUri.fullUri;
238648cfe4613549cafdf36e2a524afba730522bf291Scott Kennedy            } else {
238772727efe0bac563c72c208737c381296c35f94a9Scott Kennedy                final Bundle extras = intent.getExtras();
238872727efe0bac563c72c208737c381296c35f94a9Scott Kennedy                LogUtils.d(LOG_TAG, "Couldn't find a folder URI in the extras: %s",
238972727efe0bac563c72c208737c381296c35f94a9Scott Kennedy                        extras == null ? "null" : extras.toString());
239048cfe4613549cafdf36e2a524afba730522bf291Scott Kennedy                folderUri = mAccount.settings.defaultInbox;
239148cfe4613549cafdf36e2a524afba730522bf291Scott Kennedy            }
239248cfe4613549cafdf36e2a524afba730522bf291Scott Kennedy
23935bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein            // Check if we should load all conversations instead of using
23945bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein            // the default behavior which loads an initial subset.
23955bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein            mIgnoreInitialConversationLimit =
23965bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein                    intent.getBooleanExtra(Utils.EXTRA_IGNORE_INITIAL_CONVERSATION_LIMIT, false);
23975bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein
239860593354f1126b9687d09c3b11a852a3459f7118Scott Kennedy            args.putParcelable(Utils.EXTRA_FOLDER_URI, folderUri);
2399177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            args.putParcelable(Utils.EXTRA_CONVERSATION,
2400177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    intent.getParcelableExtra(Utils.EXTRA_CONVERSATION));
2401177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            restartOptionalLoader(LOADER_FIRST_FOLDER, mFolderCallbacks, args);
2402632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        } else if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
2403632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            if (intent.hasExtra(Utils.EXTRA_ACCOUNT)) {
2404f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal                mHaveSearchResults = false;
2405632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                // Save this search query for future suggestions.
2406632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                final String query = intent.getStringExtra(SearchManager.QUERY);
2407632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                final String authority = mContext.getString(R.string.suggestions_authority);
24082b703c6c71a369485efc8ade26d449d25669ceecVikram Aggarwal                final SearchRecentSuggestions suggestions = new SearchRecentSuggestions(
2409632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                        mContext, authority, SuggestionsProvider.MODE);
2410632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                suggestions.saveRecentQuery(query, null);
2411f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal                setAccount((Account) intent.getParcelableExtra(Utils.EXTRA_ACCOUNT));
2412f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal                fetchSearchFolder(intent);
2413f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal                if (shouldEnterSearchConvMode()) {
2414ac2548293e93c14b1d14e6de53328c561ba1cca6Mindy Pereira                    mViewMode.enterSearchResultsConversationMode();
2415ac2548293e93c14b1d14e6de53328c561ba1cca6Mindy Pereira                } else {
2416ac2548293e93c14b1d14e6de53328c561ba1cca6Mindy Pereira                    mViewMode.enterSearchResultsListMode();
2417ac2548293e93c14b1d14e6de53328c561ba1cca6Mindy Pereira                }
2418632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            } else {
2419632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                LogUtils.e(LOG_TAG, "Missing account extra from search intent.  Finishing");
2420632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                mActivity.finish();
24212d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook            }
24221ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal        }
2423632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        if (mAccount != null) {
2424177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            restartOptionalLoader(LOADER_ACCOUNT_UPDATE_CURSOR, mAccountCallbacks, Bundle.EMPTY);
2425b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy        }
2426b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy    }
2427b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy
24284556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang    /**
2429f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal     * Returns true if we should enter conversation mode with search.
2430f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal     */
2431f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal    protected final boolean shouldEnterSearchConvMode() {
2432f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal        return mHaveSearchResults && Utils.showTwoPaneSearchResults(mActivity.getActivityContext());
2433f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal    }
2434f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal
2435f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal    /**
24364556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang     * Copy any selected conversations stored in the saved bundle into our selection set,
24374556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang     * triggering {@link ConversationSetObserver} callbacks as our selection set changes.
24384556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang     *
24394556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang     */
2440715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal    private void restoreSelectedConversations(Bundle savedState) {
2441967ede63e059810d9d4d425c50111d606014c893Mindy Pereira        if (savedState == null) {
24424556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang            mSelectedSet.clear();
2443967ede63e059810d9d4d425c50111d606014c893Mindy Pereira            return;
2444967ede63e059810d9d4d425c50111d606014c893Mindy Pereira        }
2445cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal        final ConversationSelectionSet selectedSet = savedState.getParcelable(SAVED_SELECTED_SET);
24464556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang        if (selectedSet == null || selectedSet.isEmpty()) {
24474556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang            mSelectedSet.clear();
2448967ede63e059810d9d4d425c50111d606014c893Mindy Pereira            return;
2449967ede63e059810d9d4d425c50111d606014c893Mindy Pereira        }
2450632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
2451632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        // putAll will take care of calling our registered onSetPopulated method
2452cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal        mSelectedSet.putAll(selectedSet);
2453967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    }
2454967ede63e059810d9d4d425c50111d606014c893Mindy Pereira
2455ec5cbf79b825c6f96d45e85015319c66470b7e57Vikram Aggarwal    /**
245649e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * Show the conversation provided in the arguments. It is safe to pass a null conversation
245749e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * object, which is a signal to back out of conversation view mode.
245849e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * Child classes must call super.showConversation() <b>before</b> their own implementations.
245959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal     * @param conversation the conversation to be shown, or null if we want to back out to list
246059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal     *                     mode.
2461177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal     * onLoadFinished(Loader, Cursor) on any callback.
2462ec5cbf79b825c6f96d45e85015319c66470b7e57Vikram Aggarwal     */
246330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao    protected void showConversation(Conversation conversation) {
2464d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao        showConversation(conversation, true /* markAsRead */);
2465d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao    }
2466d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao
2467d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao    protected void showConversation(Conversation conversation, boolean markAsRead) {
2468243c23618b066bcdcd0ab9e36d8c01f50db2cbd0Andy Huang        if (conversation != null) {
2469243c23618b066bcdcd0ab9e36d8c01f50db2cbd0Andy Huang            Utils.sConvLoadTimer.start();
2470243c23618b066bcdcd0ab9e36d8c01f50db2cbd0Andy Huang        }
2471243c23618b066bcdcd0ab9e36d8c01f50db2cbd0Andy Huang
247254e925ec6eb14bf711af563e8e40979f07f17a68Andy Huang        MailLogService.log("AbstractActivityController", "showConversation(%s)", conversation);
2473c67182dfb4884e92262a2365ecfcc5d254c7999cVikram Aggarwal        // Set the current conversation just in case it wasn't already set.
2474c67182dfb4884e92262a2365ecfcc5d254c7999cVikram Aggarwal        setCurrentConversation(conversation);
2475ec5cbf79b825c6f96d45e85015319c66470b7e57Vikram Aggarwal    }
2476ec5cbf79b825c6f96d45e85015319c66470b7e57Vikram Aggarwal
2477e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal    /**
24782d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook     * Children can override this method, but they must call super.showWaitForInitialization().
24792d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook     * {@inheritDoc}
24802d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook     */
24812d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook    @Override
24822d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook    public void showWaitForInitialization() {
24832d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook        mViewMode.enterWaitingForInitializationMode();
2484c96efcc4a2f65b11a9b2057320ca828ee80f32ffAndy Huang        mWaitFragment = WaitFragment.newInstance(mAccount, true /* expectingMessages */);
24852d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook    }
24862d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook
2487dd6a7ce32c4003bd0941e2f18fcf5b80b5cd43c5Vikram Aggarwal    private void updateWaitMode() {
24882d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook        final FragmentManager manager = mActivity.getFragmentManager();
24892d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook        final WaitFragment waitFragment =
24906902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal                (WaitFragment)manager.findFragmentByTag(TAG_WAIT);
24912d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook        if (waitFragment != null) {
24922d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook            waitFragment.updateAccount(mAccount);
24932d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook        }
24942d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook    }
24952d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook
2496a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal    /**
2497a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal     * Remove the "Waiting for Initialization" fragment. Child classes are free to override this
2498a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal     * method, though they must call the parent implementation <b>after</b> they do anything.
2499a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal     */
2500a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal    protected void hideWaitForInitialization() {
2501a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal        mWaitFragment = null;
2502a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal    }
2503a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal
2504a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal    /**
2505a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal     * Use the instance variable and the wait fragment's tag to get the wait fragment.  This is
2506a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal     * far superior to using the value of mWaitFragment, which might be invalid or might refer
2507a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal     * to a fragment after it has been destroyed.
2508715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @return a wait fragment that is already attached to the activity, if one exists
2509a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal     */
2510a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal    protected final WaitFragment getWaitFragment() {
2511a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal        final FragmentManager manager = mActivity.getFragmentManager();
2512a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal        final WaitFragment waitFrag = (WaitFragment) manager.findFragmentByTag(TAG_WAIT);
2513a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal        if (waitFrag != null) {
2514a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal            // The Fragment Manager knows better, so use its instance.
2515a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal            mWaitFragment = waitFrag;
2516a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal        }
2517a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal        return mWaitFragment;
2518a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal    }
2519dd6a7ce32c4003bd0941e2f18fcf5b80b5cd43c5Vikram Aggarwal
252048b2a6c5fe209f0603fdf85d35cf399f7f7d462eVikram Aggarwal    /**
252148b2a6c5fe209f0603fdf85d35cf399f7f7d462eVikram Aggarwal     * Returns true if we are waiting for the account to sync, and cannot show any folders or
252248b2a6c5fe209f0603fdf85d35cf399f7f7d462eVikram Aggarwal     * conversation for the current account yet.
252348b2a6c5fe209f0603fdf85d35cf399f7f7d462eVikram Aggarwal     */
2524dd6a7ce32c4003bd0941e2f18fcf5b80b5cd43c5Vikram Aggarwal    private boolean inWaitMode() {
2525a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal        final WaitFragment waitFragment = getWaitFragment();
25262d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook        if (waitFragment != null) {
25272d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook            final Account fragmentAccount = waitFragment.getAccount();
2528339004b1d81b34682ef56f6ea0b3b9fccc133753Paul Westbrook            return fragmentAccount != null && fragmentAccount.uri.equals(mAccount.uri) &&
25292d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook                    mViewMode.getMode() == ViewMode.WAITING_FOR_ACCOUNT_INITIALIZATION;
25302d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook        }
25312d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook        return false;
25322d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook    }
25332d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook
25342d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook    /**
2535e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal     * Children can override this method, but they must call super.showConversationList().
2536e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal     * {@inheritDoc}
2537e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal     */
2538e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal    @Override
2539e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal    public void showConversationList(ConversationListContext listContext) {
2540e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal    }
2541e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal
25421ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    @Override
25430b69338a45faa422ccba8faf64c9816c55d33e4aJin Cao    public void onConversationSelected(Conversation conversation, boolean inLoaderCallbacks) {
2544fe8e081d6230f343dc74b45d69e5f4634a332b5aAlice Yang        final ConversationListFragment convListFragment = getConversationListFragment();
2545fe8e081d6230f343dc74b45d69e5f4634a332b5aAlice Yang        if (convListFragment != null && convListFragment.getAnimatedAdapter() != null) {
2546fe8e081d6230f343dc74b45d69e5f4634a332b5aAlice Yang            convListFragment.getAnimatedAdapter().onConversationSelected();
2547fe8e081d6230f343dc74b45d69e5f4634a332b5aAlice Yang        }
2548aa55bc9cb210a46a950874f368baa07f041a4302mindyp        // Only animate destructive actions if we are going to be showing the
2549aa55bc9cb210a46a950874f368baa07f041a4302mindyp        // conversation list when we show the next conversation.
2550bcb16b98140a83a4db3c51568d21c839595f73dfVikram Aggarwal        commitDestructiveActions(mIsTablet);
255130c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        showConversation(conversation);
25521ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    }
25531ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang
25541ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    @Override
25552f542873183b37fa38a0d468f46bbfebc1f537e2Andrew Sapperstein    public final void onCabModeEntered() {
25562f542873183b37fa38a0d468f46bbfebc1f537e2Andrew Sapperstein        final ConversationListFragment convListFragment = getConversationListFragment();
25572f542873183b37fa38a0d468f46bbfebc1f537e2Andrew Sapperstein        if (convListFragment != null && convListFragment.getAnimatedAdapter() != null) {
25582f542873183b37fa38a0d468f46bbfebc1f537e2Andrew Sapperstein            convListFragment.getAnimatedAdapter().onCabModeEntered();
25592f542873183b37fa38a0d468f46bbfebc1f537e2Andrew Sapperstein        }
25602f542873183b37fa38a0d468f46bbfebc1f537e2Andrew Sapperstein    }
25612f542873183b37fa38a0d468f46bbfebc1f537e2Andrew Sapperstein
25622f542873183b37fa38a0d468f46bbfebc1f537e2Andrew Sapperstein    @Override
2563cc139833bc5829ba70244519e9870de783c41848Scott Kennedy    public final void onCabModeExited() {
2564cc139833bc5829ba70244519e9870de783c41848Scott Kennedy        final ConversationListFragment convListFragment = getConversationListFragment();
2565cc139833bc5829ba70244519e9870de783c41848Scott Kennedy        if (convListFragment != null && convListFragment.getAnimatedAdapter() != null) {
2566cc139833bc5829ba70244519e9870de783c41848Scott Kennedy            convListFragment.getAnimatedAdapter().onCabModeExited();
2567cc139833bc5829ba70244519e9870de783c41848Scott Kennedy        }
2568cc139833bc5829ba70244519e9870de783c41848Scott Kennedy    }
2569cc139833bc5829ba70244519e9870de783c41848Scott Kennedy
2570cc139833bc5829ba70244519e9870de783c41848Scott Kennedy    @Override
25711ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    public Conversation getCurrentConversation() {
25721ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        return mCurrentConversation;
25737d602884ed70d07a42f1adf999fdf51b258f23f9Vikram Aggarwal    }
2574555140c7b7d5129fb2b17df66106cbd6eeef37a0Mindy Pereira
2575c67182dfb4884e92262a2365ecfcc5d254c7999cVikram Aggarwal    /**
2576c67182dfb4884e92262a2365ecfcc5d254c7999cVikram Aggarwal     * Set the current conversation. This is the conversation on which all actions are performed.
2577c67182dfb4884e92262a2365ecfcc5d254c7999cVikram Aggarwal     * Do not modify mCurrentConversation except through this method, which makes it easy to
2578c67182dfb4884e92262a2365ecfcc5d254c7999cVikram Aggarwal     * perform common actions associated with changing the current conversation.
2579715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @param conversation new conversation to view. Passing null indicates that we are backing
2580715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     *                     out to conversation list mode.
2581c67182dfb4884e92262a2365ecfcc5d254c7999cVikram Aggarwal     */
2582632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
2583632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public void setCurrentConversation(Conversation conversation) {
2584a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        // The controller should come out of detached mode if a new conversation is viewed, or if
258581230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal        // we are going back to conversation list mode.
258681230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal        if (mDetachedConvUri != null && (conversation == null
258781230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal                || !mDetachedConvUri.equals(conversation.uri))) {
2588a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal            clearDetachedMode();
2589a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        }
2590a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal
2591a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        // Must happen *before* setting mCurrentConversation because this sets
2592a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        // conversation.position if a cursor is available.
25938883f228890572a8d85d06b1e0bf08c65bd42db4Andy Huang        mTracker.initialize(conversation);
259417f373ecf6961112f77a7516ab1bbcb4fcdf2884Vikram Aggarwal        mCurrentConversation = conversation;
25957d64612104fd861474a09994f13768360f92041dAndy Huang
25967d64612104fd861474a09994f13768360f92041dAndy Huang        if (mCurrentConversation != null) {
25972d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein            mActionBarController.setCurrentConversation(mCurrentConversation);
2598f807ba7ac1011f7fd1e0d382dcc0518ba3529810Yorke Lee            mActivity.invalidateOptionsMenu();
25997d64612104fd861474a09994f13768360f92041dAndy Huang        }
26005040f1a73e3c5684901d8a9e2e30a5d58286509fMindy Pereira    }
26015040f1a73e3c5684901d8a9e2e30a5d58286509fMindy Pereira
2602a9b93f35035dc4badd0ac40f4016e1ff809b26b0Vikram Aggarwal    /**
2603f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huang     * {@link LoaderManager} currently has a bug in
2604f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huang     * {@link LoaderManager#restartLoader(int, Bundle, android.app.LoaderManager.LoaderCallbacks)}
2605f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huang     * where, if a previous onCreateLoader returned a null loader, this method will NPE. Work around
2606f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huang     * this bug by destroying any loaders that may have been created as null (essentially because
2607f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huang     * they are optional loads, and may not apply to a particular account).
2608f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huang     * <p>
2609f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huang     * A simple null check before restarting a loader will not work, because that would not
2610f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huang     * give the controller a chance to invalidate UI corresponding the prior loader result.
2611f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huang     *
2612f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huang     * @param id loader ID to safely restart
2613177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal     * @param handler the LoaderCallback which will handle this loader ID.
2614177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal     * @param args arguments, if any, to be passed to the loader. Use {@link Bundle#EMPTY} if no
2615177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal     *             arguments need to be specified.
2616f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huang     */
2617177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    private void restartOptionalLoader(int id, LoaderManager.LoaderCallbacks handler, Bundle args) {
2618f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huang        final LoaderManager lm = mActivity.getLoaderManager();
2619f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huang        lm.destroyLoader(id);
2620177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        lm.restartLoader(id, args, handler);
262194c94de8f36698be849da8f103387e7e4d744d42Vikram Aggarwal    }
262294c94de8f36698be849da8f103387e7e4d744d42Vikram Aggarwal
2623632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
2624632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public void registerConversationListObserver(DataSetObserver observer) {
2625632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        mConversationListObservable.registerObserver(observer);
2626632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
2627632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
2628632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
2629632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public void unregisterConversationListObserver(DataSetObserver observer) {
2630647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang        try {
2631647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang            mConversationListObservable.unregisterObserver(observer);
2632647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang        } catch (IllegalStateException e) {
2633647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang            // Log instead of crash
2634647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang            LogUtils.e(LOG_TAG, e, "unregisterConversationListObserver called for an observer that "
2635647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang                    + "hasn't been registered");
2636647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang        }
2637632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
2638632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
2639090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    @Override
2640090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    public void registerFolderObserver(DataSetObserver observer) {
2641090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang        mFolderObservable.registerObserver(observer);
2642090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    }
2643090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang
2644090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    @Override
2645090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    public void unregisterFolderObserver(DataSetObserver observer) {
2646647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang        try {
2647647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang            mFolderObservable.unregisterObserver(observer);
2648647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang        } catch (IllegalStateException e) {
2649647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang            // Log instead of crash
2650647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang            LogUtils.e(LOG_TAG, e, "unregisterFolderObserver called for an observer that "
2651647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang                    + "hasn't been registered");
2652647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang        }
2653090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    }
2654090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang
26559d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang    @Override
26569d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang    public void registerConversationLoadedObserver(DataSetObserver observer) {
26579d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang        mPagerController.registerConversationLoadedObserver(observer);
26589d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang    }
26599d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang
26609d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang    @Override
26619d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang    public void unregisterConversationLoadedObserver(DataSetObserver observer) {
2662647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang        try {
2663647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang            mPagerController.unregisterConversationLoadedObserver(observer);
2664647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang        } catch (IllegalStateException e) {
2665647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang            // Log instead of crash
2666647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang            LogUtils.e(LOG_TAG, e, "unregisterConversationLoadedObserver called for an observer "
2667647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang                    + "that hasn't been registered");
2668647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang        }
26699d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang    }
26709d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang
26716006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal    /**
2672c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal     * Returns true if the number of accounts is different, or if the current account has
2673c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal     * changed. This method is meant to filter frequent changes to the list of
2674c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal     * accounts, and only return true if the new list is substantially different from the existing
2675c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal     * list. Returning true is safe here, it leads to more work in creating the
2676c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal     * same account list again.
2677715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @param accountCursor the cursor which points to all the accounts.
2678715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @return true if the number of accounts is changed or current account missing from the list.
26796006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal     */
2680177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    private boolean accountsUpdated(ObjectCursor<Account> accountCursor) {
268123b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        // Check to see if the current account hasn't been set, or the account cursor is empty
268223b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        if (mAccount == null || !accountCursor.moveToFirst()) {
26836c51158ad3269f157424e6c7bd488425c98da08fVikram Aggarwal            return true;
268423b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        }
268523b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook
268623b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        // Check to see if the number of accounts are different, from the number we saw on the last
268723b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        // updated
268823b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        if (mCurrentAccountUris.size() != accountCursor.getCount()) {
268923b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook            return true;
269023b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        }
269123b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook
269223b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        // Check to see if the account list is different or if the current account is not found in
269323b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        // the cursor.
269423b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        boolean foundCurrentAccount = false;
26957dedb952c8bb9c7b50f25444a42047d903e5121bVikram Aggarwal        do {
2696c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal            final Account account = accountCursor.getModel();
2697c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal            if (!foundCurrentAccount && mAccount.uri.equals(account.uri)) {
2698c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal                if (mAccount.settingsDiffer(account)) {
2699c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal                    // Settings changed, and we don't need to look any further.
2700c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal                    return true;
2701c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal                }
270223b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook                foundCurrentAccount = true;
270323b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook            }
27046006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal            // Is there a new account that we do not know about?
2705c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal            if (!mCurrentAccountUris.contains(account.uri)) {
270623b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook                return true;
270723b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook            }
27087dedb952c8bb9c7b50f25444a42047d903e5121bVikram Aggarwal        } while (accountCursor.moveToNext());
270923b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook
271023b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        // As long as we found the current account, the list hasn't been updated
271123b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        return !foundCurrentAccount;
27127dedb952c8bb9c7b50f25444a42047d903e5121bVikram Aggarwal    }
27137dedb952c8bb9c7b50f25444a42047d903e5121bVikram Aggarwal
27147dedb952c8bb9c7b50f25444a42047d903e5121bVikram Aggarwal    /**
27156006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal     * Updates accounts for the app. If the current account is missing, the first
27166006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal     * account in the list is set to the current account (we <em>have</em> to choose something).
2717161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     *
27186c51158ad3269f157424e6c7bd488425c98da08fVikram Aggarwal     * @param accounts cursor into the AccountCache
27197dedb952c8bb9c7b50f25444a42047d903e5121bVikram Aggarwal     * @return true if the update was successful, false otherwise
27207dedb952c8bb9c7b50f25444a42047d903e5121bVikram Aggarwal     */
2721177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    private boolean updateAccounts(ObjectCursor<Account> accounts) {
27227dedb952c8bb9c7b50f25444a42047d903e5121bVikram Aggarwal        if (accounts == null || !accounts.moveToFirst()) {
27237dedb952c8bb9c7b50f25444a42047d903e5121bVikram Aggarwal            return false;
27247dedb952c8bb9c7b50f25444a42047d903e5121bVikram Aggarwal        }
272523b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook
2726a9b93f35035dc4badd0ac40f4016e1ff809b26b0Vikram Aggarwal        final Account[] allAccounts = Account.getAllAccounts(accounts);
27276006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal        // A match for the current account's URI in the list of accounts.
27286006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal        Account currentFromList = null;
272923b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook
2730636c1a1bf144e28a8a1ab022dfcfba83c7b1ca1bVikram Aggarwal        // Save the uris for the accounts and find the current account in the updated cursor.
273123b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        mCurrentAccountUris.clear();
2732636c1a1bf144e28a8a1ab022dfcfba83c7b1ca1bVikram Aggarwal        for (final Account account : allAccounts) {
27336006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal            LogUtils.d(LOG_TAG, "updateAccounts(%s)", account);
273423b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook            mCurrentAccountUris.add(account.uri);
27356006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal            if (mAccount != null && account.uri.equals(mAccount.uri)) {
27366006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal                currentFromList = account;
27376006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal            }
273823b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        }
273923b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook
27406006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal        // 1. current account is already set and is in allAccounts:
27416006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal        //    1a. It has changed -> load the updated account.
27426006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal        //    2b. It is unchanged -> no-op
27430d647354209f4bf57cffa35f008e09853977b189Andy Huang        // 2. current account is set and is not in allAccounts -> pick first (acct was deleted?)
27446006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal        // 3. saved preference has an account -> pick that one
27450d647354209f4bf57cffa35f008e09853977b189Andy Huang        // 4. otherwise just pick first
27460d647354209f4bf57cffa35f008e09853977b189Andy Huang
27476006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal        boolean accountChanged = false;
27486006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal        /// Assume case 4, initialize to first account, and see if we can find anything better.
27496006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal        Account newAccount = allAccounts[0];
27506006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal        if (currentFromList != null) {
27516006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal            // Case 1: Current account exists but has changed
27526006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal            if (!currentFromList.equals(mAccount)) {
27536006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal                newAccount = currentFromList;
27546006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal                accountChanged = true;
27550d647354209f4bf57cffa35f008e09853977b189Andy Huang            }
27566006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal            // Case 1b: else, current account is unchanged: nothing to do.
275723b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        } else {
27586006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal            // Case 2: Current account is not in allAccounts, the account needs to change.
27596006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal            accountChanged = true;
27606006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal            if (mAccount == null) {
27616006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal                // Case 3: Check for last viewed account, and check if it exists in the list.
27626006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal                final String lastAccountUri = MailAppProvider.getInstance().getLastViewedAccount();
27636006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal                if (lastAccountUri != null) {
27646006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal                    for (final Account account : allAccounts) {
27656006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal                        if (lastAccountUri.equals(account.uri.toString())) {
27666006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal                            newAccount = account;
27676006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal                            break;
27686006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal                        }
27690d647354209f4bf57cffa35f008e09853977b189Andy Huang                    }
27700d647354209f4bf57cffa35f008e09853977b189Andy Huang                }
27710d647354209f4bf57cffa35f008e09853977b189Andy Huang            }
277223b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        }
27736006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal        if (accountChanged) {
27745fd8afd5c1b40580c069a5a97039ca0601208216Vikram Aggarwal            changeAccount(newAccount);
27756006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal        }
277607dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal
27776006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal        // Whether we have updated the current account or not, we need to update the list of
27786006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal        // accounts in the ActionBar.
2779b905f0ea18ee0dc5774adb29c57cd7c122058c71Rohan Shah        mAllAccounts = allAccounts;
278007dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal        mAllAccountObservers.notifyChanged();
2781a9b93f35035dc4badd0ac40f4016e1ff809b26b0Vikram Aggarwal        return (allAccounts.length > 0);
27827dedb952c8bb9c7b50f25444a42047d903e5121bVikram Aggarwal    }
27837dedb952c8bb9c7b50f25444a42047d903e5121bVikram Aggarwal
27846ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook    private void disableNotifications() {
27856ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook        mNewEmailReceiver.activate(mContext, this);
27866ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook    }
27876ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook
27886ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook    private void enableNotifications() {
27896ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook        mNewEmailReceiver.deactivate();
27906ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook    }
27916ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook
27926ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook    private void disableNotificationsOnAccountChange(Account account) {
27936ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook        // If the new mail suppression receiver is activated for a different account, we want to
27946ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook        // activate it for the new account.
27956ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook        if (mNewEmailReceiver.activated() &&
27966ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook                !mNewEmailReceiver.notificationsDisabledForAccount(account)) {
27976ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook            // Deactivate the current receiver, otherwise multiple receivers may be registered.
27986ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook            mNewEmailReceiver.deactivate();
27996ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook            mNewEmailReceiver.activate(mContext, this);
28006ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook        }
28016ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook    }
28026ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook
2803a9b93f35035dc4badd0ac40f4016e1ff809b26b0Vikram Aggarwal    /**
2804c7694221dfa5cec3f4ae290f2266b081b2639d80Vikram Aggarwal     * Destructive actions on Conversations. This class should only be created by controllers, and
2805c7694221dfa5cec3f4ae290f2266b081b2639d80Vikram Aggarwal     * clients should only require {@link DestructiveAction}s, not specific implementations of the.
2806c7694221dfa5cec3f4ae290f2266b081b2639d80Vikram Aggarwal     * Only the controllers should know what kind of destructive actions are being created.
2807c7694221dfa5cec3f4ae290f2266b081b2639d80Vikram Aggarwal     */
2808de3e74a82043733243c7391d7f983a5af8842891Mindy Pereira    public class ConversationAction implements DestructiveAction {
2809acaa3c0aa2335e0a635601e09c955388d698dfdaVikram Aggarwal        /**
2810acaa3c0aa2335e0a635601e09c955388d698dfdaVikram Aggarwal         * The action to be performed. This is specified as the resource ID of the menu item
2811acaa3c0aa2335e0a635601e09c955388d698dfdaVikram Aggarwal         * corresponding to this action: R.id.delete, R.id.report_spam, etc.
2812acaa3c0aa2335e0a635601e09c955388d698dfdaVikram Aggarwal         */
28137f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal        private final int mAction;
2814bc67bb1211654e69b1233e0f8759771873ca773eVikram Aggarwal        /** The action will act upon these conversations */
281577eee625cd8c317c605acfd06cd3a7e22120a0fdPaul Westbrook        private final Collection<Conversation> mTarget;
28167f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal        /** Whether this destructive action has already been performed */
28177f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal        private boolean mCompleted;
2818c41139509b1eb6dfc9bdae88ccc7d16eac85e2cfVikram Aggarwal        /** Whether this is an action on the currently selected set. */
2819c41139509b1eb6dfc9bdae88ccc7d16eac85e2cfVikram Aggarwal        private final boolean mIsSelectedSet;
2820e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal
282130c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        private UndoCallback mCallback;
282230c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao
2823fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira        /**
2824715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal         * Create a listener object.
2825715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal         * @param action action is one of four constants: R.id.y_button (archive),
2826fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira         * R.id.delete , R.id.mute, and R.id.report_spam.
2827bc67bb1211654e69b1233e0f8759771873ca773eVikram Aggarwal         * @param target Conversation that we want to apply the action to.
2828c41139509b1eb6dfc9bdae88ccc7d16eac85e2cfVikram Aggarwal         * @param isBatch whether the conversations are in the currently selected batch set.
2829c41139509b1eb6dfc9bdae88ccc7d16eac85e2cfVikram Aggarwal         */
2830c41139509b1eb6dfc9bdae88ccc7d16eac85e2cfVikram Aggarwal        public ConversationAction(int action, Collection<Conversation> target, boolean isBatch) {
2831fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira            mAction = action;
283277eee625cd8c317c605acfd06cd3a7e22120a0fdPaul Westbrook            mTarget = ImmutableList.copyOf(target);
2833c41139509b1eb6dfc9bdae88ccc7d16eac85e2cfVikram Aggarwal            mIsSelectedSet = isBatch;
2834fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira        }
2835fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira
283630c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        @Override
283730c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        public void setUndoCallback(UndoCallback undoCallback) {
283830c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            mCallback = undoCallback;
283930c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        }
284030c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao
2841acaa3c0aa2335e0a635601e09c955388d698dfdaVikram Aggarwal        /**
2842acaa3c0aa2335e0a635601e09c955388d698dfdaVikram Aggarwal         * The action common to child classes. This performs the action specified in the constructor
2843acaa3c0aa2335e0a635601e09c955388d698dfdaVikram Aggarwal         * on the conversations given here.
2844acaa3c0aa2335e0a635601e09c955388d698dfdaVikram Aggarwal         */
2845bc67bb1211654e69b1233e0f8759771873ca773eVikram Aggarwal        @Override
2846bc67bb1211654e69b1233e0f8759771873ca773eVikram Aggarwal        public void performAction() {
28477f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal            if (isPerformed()) {
28487f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal                return;
28497f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal            }
28503cb28b5e1598f74dedbdb87b187d9bd7e132ff0fMindy Pereira            boolean undoEnabled = mAccount.supportsCapability(AccountCapabilities.UNDO);
28517dd054e39986de84a213c56a3c11ac94731402e6Vikram Aggarwal
28527dd054e39986de84a213c56a3c11ac94731402e6Vikram Aggarwal            // Are we destroying the currently shown conversation? Show the next one.
28537dd054e39986de84a213c56a3c11ac94731402e6Vikram Aggarwal            if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)){
28548742a614a238c5ae55d767461000d0a10d1da4d4Vikram Aggarwal                LogUtils.d(LOG_TAG, "ConversationAction.performAction():"
28558742a614a238c5ae55d767461000d0a10d1da4d4Vikram Aggarwal                        + "\nmTarget=%s\nCurrent=%s",
28567dd054e39986de84a213c56a3c11ac94731402e6Vikram Aggarwal                        Conversation.toString(mTarget), mCurrentConversation);
28577dd054e39986de84a213c56a3c11ac94731402e6Vikram Aggarwal            }
28587dd054e39986de84a213c56a3c11ac94731402e6Vikram Aggarwal
2859e1221d2807826e2ee272df81c271a0f444522890Paul Westbrook            if (mConversationListCursor == null) {
2860e1221d2807826e2ee272df81c271a0f444522890Paul Westbrook                LogUtils.e(LOG_TAG, "null ConversationCursor in ConversationAction.performAction():"
2861e1221d2807826e2ee272df81c271a0f444522890Paul Westbrook                        + "\nmTarget=%s\nCurrent=%s",
2862e1221d2807826e2ee272df81c271a0f444522890Paul Westbrook                        Conversation.toString(mTarget), mCurrentConversation);
2863e1221d2807826e2ee272df81c271a0f444522890Paul Westbrook                return;
2864e1221d2807826e2ee272df81c271a0f444522890Paul Westbrook            }
2865e1221d2807826e2ee272df81c271a0f444522890Paul Westbrook
28662b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            if (mAction == R.id.archive) {
28672b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                LogUtils.d(LOG_TAG, "Archiving");
286830c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                mConversationListCursor.archive(mTarget, mCallback);
28692b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            } else if (mAction == R.id.delete) {
28702b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                LogUtils.d(LOG_TAG, "Deleting");
287130c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                mConversationListCursor.delete(mTarget, mCallback);
28722b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                if (mFolder.supportsCapability(FolderCapabilities.DELETE_ACTION_FINAL)) {
28732b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                    undoEnabled = false;
28742b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                }
28752b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            } else if (mAction == R.id.mute) {
28762b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                LogUtils.d(LOG_TAG, "Muting");
28772b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                if (mFolder.supportsCapability(FolderCapabilities.DESTRUCTIVE_MUTE)) {
28782b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                    for (Conversation c : mTarget) {
28792b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                        c.localDeleteOnUpdate = true;
2880bc67bb1211654e69b1233e0f8759771873ca773eVikram Aggarwal                    }
28812b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                }
288230c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                mConversationListCursor.mute(mTarget, mCallback);
28832b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            } else if (mAction == R.id.report_spam) {
28842b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                LogUtils.d(LOG_TAG, "Reporting spam");
288530c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                mConversationListCursor.reportSpam(mTarget, mCallback);
28862b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            } else if (mAction == R.id.mark_not_spam) {
28872b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                LogUtils.d(LOG_TAG, "Marking not spam");
288830c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                mConversationListCursor.reportNotSpam(mTarget, mCallback);
28892b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            } else if (mAction == R.id.report_phishing) {
28902b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                LogUtils.d(LOG_TAG, "Reporting phishing");
289130c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                mConversationListCursor.reportPhishing(mTarget, mCallback);
28922b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            } else if (mAction == R.id.remove_star) {
28932b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                LogUtils.d(LOG_TAG, "Removing star");
28942b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                // Star removal is destructive in the Starred folder.
28952b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                mConversationListCursor.updateBoolean(mTarget, ConversationColumns.STARRED,
28962b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                        false);
28972b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            } else if (mAction == R.id.mark_not_important) {
28982b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                LogUtils.d(LOG_TAG, "Marking not-important");
28992b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                // Marking not important is destructive in a mailbox
29002b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                // containing only important messages
29012b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                if (mFolder != null && mFolder.isImportantOnly()) {
29022b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                    for (Conversation conv : mTarget) {
29032b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                        conv.localDeleteOnUpdate = true;
2904445be2114aed6b28cab53b488735d4bb812353a5Mindy Pereira                    }
29052b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                }
29062b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                mConversationListCursor.updateInt(mTarget, ConversationColumns.PRIORITY,
29072b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                        UIProvider.ConversationPriority.LOW);
29082b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            } else if (mAction == R.id.discard_drafts) {
29092b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                LogUtils.d(LOG_TAG, "Discarding draft messages");
29102b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                // Discarding draft messages is destructive in a "draft" mailbox
29112b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                if (mFolder != null && mFolder.isDraft()) {
29122b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                    for (Conversation conv : mTarget) {
29132b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                        conv.localDeleteOnUpdate = true;
2914ef3625472596326b910a4445307d1a8eb8c9cc3fPaul Westbrook                    }
29152b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                }
29162b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                mConversationListCursor.discardDrafts(mTarget);
29172b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                // We don't support undoing discarding drafts
29182b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                undoEnabled = false;
2919512821c11d89d49908f3cfdee0b582601f500f3dJin Cao            } else if (mAction == R.id.discard_outbox) {
2920512821c11d89d49908f3cfdee0b582601f500f3dJin Cao                LogUtils.d(LOG_TAG, "Discarding failed messages in Outbox");
2921512821c11d89d49908f3cfdee0b582601f500f3dJin Cao                mConversationListCursor.moveFailedIntoDrafts(mTarget);
2922512821c11d89d49908f3cfdee0b582601f500f3dJin Cao                undoEnabled = false;
29233d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal            }
29243d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal            if (undoEnabled) {
2925ead5039b6d606fcc39668a472f8973cbd0bef4e1mindyp                mHandler.postDelayed(new Runnable() {
2926ead5039b6d606fcc39668a472f8973cbd0bef4e1mindyp                    @Override
2927ead5039b6d606fcc39668a472f8973cbd0bef4e1mindyp                    public void run() {
2928ead5039b6d606fcc39668a472f8973cbd0bef4e1mindyp                        onUndoAvailable(new ToastBarOperation(mTarget.size(), mAction,
29296a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                                ToastBarOperation.UNDO, mIsSelectedSet, mFolder));
2930ead5039b6d606fcc39668a472f8973cbd0bef4e1mindyp                    }
2931ead5039b6d606fcc39668a472f8973cbd0bef4e1mindyp                }, mShowUndoBarDelay);
29323d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal            }
2933bc67bb1211654e69b1233e0f8759771873ca773eVikram Aggarwal            refreshConversationList();
2934c41139509b1eb6dfc9bdae88ccc7d16eac85e2cfVikram Aggarwal            if (mIsSelectedSet) {
2935c41139509b1eb6dfc9bdae88ccc7d16eac85e2cfVikram Aggarwal                mSelectedSet.clear();
2936c41139509b1eb6dfc9bdae88ccc7d16eac85e2cfVikram Aggarwal            }
2937fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira        }
29387f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal
29397f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal        /**
29407f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal         * Returns true if this action has been performed, false otherwise.
2941839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang         *
29427f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal         */
29437f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal        private synchronized boolean isPerformed() {
29447f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal            if (mCompleted) {
29457f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal                return true;
29467f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal            }
29477f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal            mCompleted = true;
29487f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal            return false;
29497f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal        }
2950fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira    }
2951e5f4dc0661dad02e2cb39ffa62ff2157147ac387Mindy Pereira
2952d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal    // Called from the FolderSelectionDialog after a user is done selecting folders to assign the
2953d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal    // conversations to.
2954e5f4dc0661dad02e2cb39ffa62ff2157147ac387Mindy Pereira    @Override
29558db7e407109532557718c6b8064792f2df7a073dMindy Pereira    public final void assignFolder(Collection<FolderOperation> folderOps,
29566a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy            Collection<Conversation> target, boolean batch, boolean showUndo,
29576a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy            final boolean isMoveTo) {
2958479753fdbb4d71558c4d2c8d9cc8c7eb803a7837Jin Cao        // Actions are destructive only when the current folder can be un-assigned from and
29598db7e407109532557718c6b8064792f2df7a073dMindy Pereira        // when the list of folders contains the current folder.
29608db7e407109532557718c6b8064792f2df7a073dMindy Pereira        final boolean isDestructive = mFolder
2961479753fdbb4d71558c4d2c8d9cc8c7eb803a7837Jin Cao                .supportsCapability(FolderCapabilities.ALLOWS_REMOVE_CONVERSATION)
29628db7e407109532557718c6b8064792f2df7a073dMindy Pereira                && FolderOperation.isDestructive(folderOps, mFolder);
2963d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal        LogUtils.d(LOG_TAG, "onFolderChangesCommit: isDestructive = %b", isDestructive);
2964d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal        if (isDestructive) {
2965d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal            for (final Conversation c : target) {
2966d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal                c.localDeleteOnUpdate = true;
29676778f4663a790d3e9d4c6a2f43292eef43c1e0bdMindy Pereira            }
2968e5f4dc0661dad02e2cb39ffa62ff2157147ac387Mindy Pereira        }
2969c84759cc974256b6af5545c4bd993b81a0a98f6fmindyp        final DestructiveAction folderChange;
297030c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        final UndoCallback undoCallback = isMoveTo ?
297130c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                getUndoCallbackForDestructiveActionsWithAutoAdvance(R.id.move_to,
297230c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                        mCurrentConversation)
297330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                : null;
2974d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal        // Update the UI elements depending no their visibility and availability
29753d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal        // TODO(viki): Consolidate this into a single method requestDelete.
2976d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal        if (isDestructive) {
29776a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy            /*
29786a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy             * If this is a MOVE operation, we want the action folder to be the destination folder.
29796a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy             * Otherwise, we want it to be the current folder.
29806a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy             *
29816a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy             * A set of folder operations is a move if there are exactly two operations: an add and
29826a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy             * a remove.
29836a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy             */
29846a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy            final Folder actionFolder;
29856a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy            if (folderOps.size() != 2) {
29866a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                actionFolder = mFolder;
29876a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy            } else {
29886a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                Folder addedFolder = null;
29896a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                boolean hasRemove = false;
29906a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                for (final FolderOperation folderOperation : folderOps) {
29916a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                    if (folderOperation.mAdd) {
29926a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                        addedFolder = folderOperation.mFolder;
29936a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                    } else {
29946a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                        hasRemove = true;
29956a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                    }
29966a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                }
29976a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy
29986a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                if (hasRemove && addedFolder != null) {
29996a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                    actionFolder = addedFolder;
30006a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                } else {
30016a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                    actionFolder = mFolder;
30026a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                }
30036a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy            }
30046a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy
3005c84759cc974256b6af5545c4bd993b81a0a98f6fmindyp            folderChange = getDeferredFolderChange(target, folderOps, isDestructive,
300630c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                    batch, showUndo, isMoveTo, actionFolder, undoCallback);
3007caaeed3837316175111bb16e6b60aeb0d523debfScott Kennedy            delete(0, target, folderChange, batch);
30086902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal        } else {
3009c84759cc974256b6af5545c4bd993b81a0a98f6fmindyp            folderChange = getFolderChange(target, folderOps, isDestructive,
301030c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                    batch, showUndo, false /* isMoveTo */, mFolder, undoCallback);
3011715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal            requestUpdate(folderChange);
3012e5f4dc0661dad02e2cb39ffa62ff2157147ac387Mindy Pereira        }
3013e5f4dc0661dad02e2cb39ffa62ff2157147ac387Mindy Pereira    }
3014e5f4dc0661dad02e2cb39ffa62ff2157147ac387Mindy Pereira
3015967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    @Override
30163d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal    public final void onRefreshRequired() {
30175390fcabb7baf683fc14bd68d8612913dac70a81mindyp        if (isAnimating() || isDragging()) {
3018f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang            final ConversationListFragment f = getConversationListFragment();
3019f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang            LogUtils.w(ConversationCursor.LOG_TAG,
3020f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang                    "onRefreshRequired: delay until animating done. cursor=%s adapter=%s",
3021f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang                    mConversationListCursor, (f != null) ? f.getAnimatedAdapter() : null);
302269e88dd6779a8f88212a049d49d69503cd746ef7Mindy Pereira            return;
302369e88dd6779a8f88212a049d49d69503cd746ef7Mindy Pereira        }
3024bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        // Refresh the query in the background
3025cff1aeaff92efb9f7a03203d9f757a2820b87686Paul Westbrook        if (mConversationListCursor.isRefreshRequired()) {
3026cff1aeaff92efb9f7a03203d9f757a2820b87686Paul Westbrook            mConversationListCursor.refresh();
3027cff1aeaff92efb9f7a03203d9f757a2820b87686Paul Westbrook        }
3028bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    }
3029bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank
30305390fcabb7baf683fc14bd68d8612913dac70a81mindyp    @Override
30315390fcabb7baf683fc14bd68d8612913dac70a81mindyp    public void startDragMode() {
30325390fcabb7baf683fc14bd68d8612913dac70a81mindyp        mIsDragHappening = true;
30335390fcabb7baf683fc14bd68d8612913dac70a81mindyp    }
30345390fcabb7baf683fc14bd68d8612913dac70a81mindyp
30355390fcabb7baf683fc14bd68d8612913dac70a81mindyp    @Override
30365390fcabb7baf683fc14bd68d8612913dac70a81mindyp    public void stopDragMode() {
30375390fcabb7baf683fc14bd68d8612913dac70a81mindyp        mIsDragHappening = false;
30385390fcabb7baf683fc14bd68d8612913dac70a81mindyp        if (mConversationListCursor.isRefreshReady()) {
3039c1922a93fb9540d4ff8c28a30d1ae58d3a3d73f9Andy Huang            LogUtils.i(ConversationCursor.LOG_TAG, "Stopped dragging: try sync");
30405390fcabb7baf683fc14bd68d8612913dac70a81mindyp            onRefreshReady();
30415390fcabb7baf683fc14bd68d8612913dac70a81mindyp        }
30425390fcabb7baf683fc14bd68d8612913dac70a81mindyp
30435390fcabb7baf683fc14bd68d8612913dac70a81mindyp        if (mConversationListCursor.isRefreshRequired()) {
3044c1922a93fb9540d4ff8c28a30d1ae58d3a3d73f9Andy Huang            LogUtils.i(ConversationCursor.LOG_TAG, "Stopped dragging: refresh");
30455390fcabb7baf683fc14bd68d8612913dac70a81mindyp            mConversationListCursor.refresh();
30465390fcabb7baf683fc14bd68d8612913dac70a81mindyp        }
30475390fcabb7baf683fc14bd68d8612913dac70a81mindyp    }
30485390fcabb7baf683fc14bd68d8612913dac70a81mindyp
30495390fcabb7baf683fc14bd68d8612913dac70a81mindyp    private boolean isDragging() {
30505390fcabb7baf683fc14bd68d8612913dac70a81mindyp        return mIsDragHappening;
30515390fcabb7baf683fc14bd68d8612913dac70a81mindyp    }
30525390fcabb7baf683fc14bd68d8612913dac70a81mindyp
30536f54e1b0e8beae2e624852f9f2f2b1b38745e3bemindyp    @Override
30546f54e1b0e8beae2e624852f9f2f2b1b38745e3bemindyp    public boolean isAnimating() {
305569e88dd6779a8f88212a049d49d69503cd746ef7Mindy Pereira        boolean isAnimating = false;
305669e88dd6779a8f88212a049d49d69503cd746ef7Mindy Pereira        ConversationListFragment convListFragment = getConversationListFragment();
305769e88dd6779a8f88212a049d49d69503cd746ef7Mindy Pereira        if (convListFragment != null) {
305848ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang            isAnimating = convListFragment.isAnimating();
305969e88dd6779a8f88212a049d49d69503cd746ef7Mindy Pereira        }
306069e88dd6779a8f88212a049d49d69503cd746ef7Mindy Pereira        return isAnimating;
306169e88dd6779a8f88212a049d49d69503cd746ef7Mindy Pereira    }
306269e88dd6779a8f88212a049d49d69503cd746ef7Mindy Pereira
3063bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    /**
3064bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank     * Called when the {@link ConversationCursor} is changed or has new data in it.
3065bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank     * <p>
3066bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank     * {@inheritDoc}
3067bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank     */
3068bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    @Override
30693d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal    public final void onRefreshReady() {
30705c1d8354ce98be2fc77d458bcc5c3346765046a0mindyp        LogUtils.d(LOG_TAG, "Received refresh ready callback for folder %s",
30715c1d8354ce98be2fc77d458bcc5c3346765046a0mindyp                mFolder != null ? mFolder.id : "-1");
3072b2ef9c18d1f68543d9c782e1952b70d3bbcd94d6Andy Huang
3073b2ef9c18d1f68543d9c782e1952b70d3bbcd94d6Andy Huang        if (mDestroyed) {
3074b2ef9c18d1f68543d9c782e1952b70d3bbcd94d6Andy Huang            LogUtils.i(LOG_TAG, "ignoring onRefreshReady on destroyed AAC");
3075b2ef9c18d1f68543d9c782e1952b70d3bbcd94d6Andy Huang            return;
3076b2ef9c18d1f68543d9c782e1952b70d3bbcd94d6Andy Huang        }
3077b2ef9c18d1f68543d9c782e1952b70d3bbcd94d6Andy Huang
3078cff1aeaff92efb9f7a03203d9f757a2820b87686Paul Westbrook        if (!isAnimating()) {
3079bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank            // Swap cursors
3080bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank            mConversationListCursor.sync();
3081f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang        } else {
3082f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang            // (CLF guaranteed to be non-null due to check in isAnimating)
3083f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang            LogUtils.w(LOG_TAG,
3084f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang                    "AAC.onRefreshReady suppressing sync() due to animation. cursor=%s aa=%s",
3085f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang                    mConversationListCursor, getConversationListFragment().getAnimatedAdapter());
3086bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        }
3087937c94f025ed55c19bd46c7c077fa3575ab6be52Paul Westbrook        mTracker.onCursorUpdated();
3088f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal        perhapsShowFirstSearchResult();
3089bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    }
3090bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank
3091bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    @Override
30923d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal    public final void onDataSetChanged() {
30939f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        updateConversationListFragment();
3094632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        mConversationListObservable.notifyChanged();
3095a13b3742522987f768ef9a1a1cddd32ff8105f0ePaul Westbrook        mSelectedSet.validateAgainstCursor(mConversationListCursor);
3096bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    }
3097bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank
30983d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal    /**
30993d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal     * If the Conversation List Fragment is visible, updates the fragment.
31003d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal     */
3101715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal    private void updateConversationListFragment() {
3102bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        final ConversationListFragment convList = getConversationListFragment();
3103bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        if (convList != null) {
310475daee52f08c9bf66652b9bddf42cfbd1ca71987Vikram Aggarwal            refreshConversationList();
31056422b8d051c93b4f34580c551254fc50c9efa4dbVikram Aggarwal            if (isFragmentVisible(convList)) {
310669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal                informCursorVisiblity(true);
31079f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook            }
3108bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        }
3109bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    }
3110bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank
3111bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    /**
3112bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank     * This class handles throttled refresh of the conversation list
3113bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank     */
3114bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    static class RefreshTimerTask extends TimerTask {
3115bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        final Handler mHandler;
3116bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        final AbstractActivityController mController;
3117bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank
3118bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        RefreshTimerTask(AbstractActivityController controller, Handler handler) {
3119bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank            mHandler = handler;
3120bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank            mController = controller;
3121bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        }
3122bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank
3123bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        @Override
3124bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        public void run() {
3125bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank            mHandler.post(new Runnable() {
3126bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank                @Override
3127bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank                public void run() {
3128bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank                    LogUtils.d(LOG_TAG, "Delay done... calling onRefreshRequired");
3129bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank                    mController.onRefreshRequired();
3130bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank                }});
3131bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        }
3132bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    }
3133bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank
3134bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    /**
3135bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank     * Cancel the refresh task, if it's running
3136bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank     */
3137bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    private void cancelRefreshTask () {
3138bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        if (mConversationListRefreshTask != null) {
3139bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank            mConversationListRefreshTask.cancel();
3140bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank            mConversationListRefreshTask = null;
3141bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        }
3142bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    }
3143bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank
3144bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    @Override
3145cff1aeaff92efb9f7a03203d9f757a2820b87686Paul Westbrook    public void onAnimationEnd(AnimatedAdapter animatedAdapter) {
3146f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang        if (animatedAdapter != null) {
3147f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang            LogUtils.i(LOG_TAG, "AAC.onAnimationEnd. cursor=%s adapter=%s", mConversationListCursor,
3148f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang                    animatedAdapter);
3149f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang        }
3150026139c958c563266294b7788ed54f57345626b8Paul Westbrook        if (mConversationListCursor == null) {
3151026139c958c563266294b7788ed54f57345626b8Paul Westbrook            LogUtils.e(LOG_TAG, "null ConversationCursor in onAnimationEnd");
3152026139c958c563266294b7788ed54f57345626b8Paul Westbrook            return;
3153026139c958c563266294b7788ed54f57345626b8Paul Westbrook        }
3154cff1aeaff92efb9f7a03203d9f757a2820b87686Paul Westbrook        if (mConversationListCursor.isRefreshReady()) {
3155c1922a93fb9540d4ff8c28a30d1ae58d3a3d73f9Andy Huang            LogUtils.i(ConversationCursor.LOG_TAG, "Stopped animating: try sync");
3156cff1aeaff92efb9f7a03203d9f757a2820b87686Paul Westbrook            onRefreshReady();
3157bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        }
3158bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank
3159cff1aeaff92efb9f7a03203d9f757a2820b87686Paul Westbrook        if (mConversationListCursor.isRefreshRequired()) {
3160c1922a93fb9540d4ff8c28a30d1ae58d3a3d73f9Andy Huang            LogUtils.i(ConversationCursor.LOG_TAG, "Stopped animating: refresh");
3161cff1aeaff92efb9f7a03203d9f757a2820b87686Paul Westbrook            mConversationListCursor.refresh();
3162cff1aeaff92efb9f7a03203d9f757a2820b87686Paul Westbrook        }
31636f54e1b0e8beae2e624852f9f2f2b1b38745e3bemindyp        if (mRecentsDataUpdated) {
31646f54e1b0e8beae2e624852f9f2f2b1b38745e3bemindyp            mRecentsDataUpdated = false;
31656f54e1b0e8beae2e624852f9f2f2b1b38745e3bemindyp            mRecentFolderObservers.notifyChanged();
31666f54e1b0e8beae2e624852f9f2f2b1b38745e3bemindyp        }
3167bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    }
3168bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank
3169bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    @Override
3170967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    public void onSetEmpty() {
31716cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal        // There are no selected conversations. Ensure that the listener and its associated actions
31726cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal        // are blanked out.
31736cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal        setListener(null, -1);
3174967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    }
3175967ede63e059810d9d4d425c50111d606014c893Mindy Pereira
3176967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    @Override
3177967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    public void onSetPopulated(ConversationSelectionSet set) {
31787704d79def5e5f36a1cbe22fbbe6e9520503899cVikram Aggarwal        mCabActionMenu = new SelectedConversationsActionMenu(mActivity, set, mFolder);
31795b9ed2b056e0757ccc26e51aba7d5ff12378baaaVikram Aggarwal        if (mViewMode.isListMode() || (mIsTablet && mViewMode.isConversationMode())) {
31807704d79def5e5f36a1cbe22fbbe6e9520503899cVikram Aggarwal            enableCabMode();
31816902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal        }
3182967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    }
3183967ede63e059810d9d4d425c50111d606014c893Mindy Pereira
3184967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    @Override
3185967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    public void onSetChanged(ConversationSelectionSet set) {
3186967ede63e059810d9d4d425c50111d606014c893Mindy Pereira        // Do nothing. We don't care about changes to the set.
3187967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    }
3188967ede63e059810d9d4d425c50111d606014c893Mindy Pereira
3189967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    @Override
3190967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    public ConversationSelectionSet getSelectedSet() {
3191967ede63e059810d9d4d425c50111d606014c893Mindy Pereira        return mSelectedSet;
3192967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    }
3193967ede63e059810d9d4d425c50111d606014c893Mindy Pereira
3194e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal    /**
3195e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal     * Disable the Contextual Action Bar (CAB). The selected set is not changed.
3196e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal     */
3197e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal    protected void disableCabMode() {
31988937bf1552a86853efc798a4d8df34c01115cdfdMindy Pereira        // Commit any previous destructive actions when entering/ exiting CAB mode.
3199c6adce3cf6887c4c6dd5005724565702751843d0mindyp        commitDestructiveActions(true);
3200e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal        if (mCabActionMenu != null) {
3201e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal            mCabActionMenu.deactivate();
3202e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal        }
3203e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal    }
3204e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal
3205e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal    /**
3206e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal     * Re-enable the CAB menu if required. The selection set is not changed.
3207e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal     */
3208e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal    protected void enableCabMode() {
32093d6751ad5328d5a17fa637e3157974d776591b26Tony Mantler        if (mCabActionMenu != null &&
32103d6751ad5328d5a17fa637e3157974d776591b26Tony Mantler                !(isDrawerEnabled() && mDrawerContainer.isDrawerOpen(mDrawerPullout))) {
3211e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal            mCabActionMenu.activate();
3212e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal        }
3213e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal    }
3214e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal
32154eb5271e0f0a48ea624e048eb69fbe86d6c62dc3Vikram Aggarwal    /**
321643fab3224230c235db5be395f96096e1cac6b376Tony Mantler     * Re-enable CAB mode only if we have an active selection
321743fab3224230c235db5be395f96096e1cac6b376Tony Mantler     */
321843fab3224230c235db5be395f96096e1cac6b376Tony Mantler    protected void maybeEnableCabMode() {
321943fab3224230c235db5be395f96096e1cac6b376Tony Mantler        if (!mSelectedSet.isEmpty()) {
3220a703d8a647a7272347c47bbb394b1a4c403106a5Tony Mantler            if (mCabActionMenu != null) {
3221a703d8a647a7272347c47bbb394b1a4c403106a5Tony Mantler                mCabActionMenu.activate();
3222a703d8a647a7272347c47bbb394b1a4c403106a5Tony Mantler            }
322343fab3224230c235db5be395f96096e1cac6b376Tony Mantler        }
322443fab3224230c235db5be395f96096e1cac6b376Tony Mantler    }
322543fab3224230c235db5be395f96096e1cac6b376Tony Mantler
322643fab3224230c235db5be395f96096e1cac6b376Tony Mantler    /**
32274eb5271e0f0a48ea624e048eb69fbe86d6c62dc3Vikram Aggarwal     * Unselect conversations and exit CAB mode.
32284eb5271e0f0a48ea624e048eb69fbe86d6c62dc3Vikram Aggarwal     */
32294eb5271e0f0a48ea624e048eb69fbe86d6c62dc3Vikram Aggarwal    protected final void exitCabMode() {
32304eb5271e0f0a48ea624e048eb69fbe86d6c62dc3Vikram Aggarwal        mSelectedSet.clear();
32314eb5271e0f0a48ea624e048eb69fbe86d6c62dc3Vikram Aggarwal    }
32324eb5271e0f0a48ea624e048eb69fbe86d6c62dc3Vikram Aggarwal
3233967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    @Override
3234fd0c2974af56ad244006faab2651e054974a4b7bMindy Pereira    public void startSearch() {
323535f19d7c052ca8074b48b2ab9d8689a72dc369c5Vikram Aggarwal        if (mAccount == null) {
323635f19d7c052ca8074b48b2ab9d8689a72dc369c5Vikram Aggarwal            // We cannot search if there is no account. Drop the request to the floor.
323735f19d7c052ca8074b48b2ab9d8689a72dc369c5Vikram Aggarwal            LogUtils.d(LOG_TAG, "AbstractActivityController.startSearch(): null account");
323835f19d7c052ca8074b48b2ab9d8689a72dc369c5Vikram Aggarwal            return;
323935f19d7c052ca8074b48b2ab9d8689a72dc369c5Vikram Aggarwal        }
32403531d7ee9b36c9f9dd909be79ab899dd60cd71aeJames Lemieux        if (mAccount.supportsSearch()) {
32412d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein            mActionBarController.expandSearch();
3242fd0c2974af56ad244006faab2651e054974a4b7bMindy Pereira        } else {
3243fd0c2974af56ad244006faab2651e054974a4b7bMindy Pereira            Toast.makeText(mActivity.getActivityContext(), mActivity.getActivityContext()
3244a46c29980f2d81da3529b2f1dfddf7291c94a102Mindy Pereira                    .getString(R.string.search_unsupported), Toast.LENGTH_SHORT).show();
3245fd0c2974af56ad244006faab2651e054974a4b7bMindy Pereira        }
3246fd0c2974af56ad244006faab2651e054974a4b7bMindy Pereira    }
3247acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereira
32480dda573e51786dd907b2674baddbca20ffa5c725Vikram Aggarwal    @Override
32490dda573e51786dd907b2674baddbca20ffa5c725Vikram Aggarwal    public void exitSearchMode() {
32500dda573e51786dd907b2674baddbca20ffa5c725Vikram Aggarwal        if (mViewMode.getMode() == ViewMode.SEARCH_RESULTS_LIST) {
32510dda573e51786dd907b2674baddbca20ffa5c725Vikram Aggarwal            mActivity.finish();
32520dda573e51786dd907b2674baddbca20ffa5c725Vikram Aggarwal        }
32530dda573e51786dd907b2674baddbca20ffa5c725Vikram Aggarwal    }
32540dda573e51786dd907b2674baddbca20ffa5c725Vikram Aggarwal
3255acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereira    /**
3256acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereira     * Supports dragging conversations to a folder.
3257acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereira     */
3258acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereira    @Override
3259acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereira    public boolean supportsDrag(DragEvent event, Folder folder) {
3260acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereira        return (folder != null
3261acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereira                && event != null
3262acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereira                && event.getClipDescription() != null
3263acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereira                && folder.supportsCapability
3264acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereira                    (UIProvider.FolderCapabilities.CAN_ACCEPT_MOVED_MESSAGES)
3265259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy                && !mFolder.equals(folder));
3266acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereira    }
3267acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereira
3268acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereira    /**
32696c2663dc9cade8aff34c462d244e3d72c475c664Mindy Pereira     * Handles dropping conversations to a folder.
3270acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereira     */
3271acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereira    @Override
3272acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereira    public void handleDrop(DragEvent event, final Folder folder) {
3273acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereira        if (!supportsDrag(event, folder)) {
3274acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereira            return;
3275acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereira        }
32768c1058ee75ec4a5824a68c3c5275bc48d56bbad8Scott Kennedy        if (folder.isType(UIProvider.FolderType.STARRED)) {
3277ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp            // Moving a conversation to the starred folder adds the star and
3278ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp            // removes the current label
3279ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp            handleDropInStarred(folder);
3280ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp            return;
3281ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp        }
32828c1058ee75ec4a5824a68c3c5275bc48d56bbad8Scott Kennedy        if (mFolder.isType(UIProvider.FolderType.STARRED)) {
3283ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp            handleDragFromStarred(folder);
3284ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp            return;
3285ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp        }
3286a8492633481c1724d2f6d418594faf6e982d3dc9mindyp        final ArrayList<FolderOperation> dragDropOperations = new ArrayList<FolderOperation>();
3287ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp        final Collection<Conversation> conversations = mSelectedSet.values();
3288a8492633481c1724d2f6d418594faf6e982d3dc9mindyp        // Add the drop target folder.
3289a8492633481c1724d2f6d418594faf6e982d3dc9mindyp        dragDropOperations.add(new FolderOperation(folder, true));
3290a8492633481c1724d2f6d418594faf6e982d3dc9mindyp        // Remove the current folder unless the user is viewing "all".
3291a8492633481c1724d2f6d418594faf6e982d3dc9mindyp        // That operation should just add the new folder.
3292a8492633481c1724d2f6d418594faf6e982d3dc9mindyp        boolean isDestructive = !mFolder.isViewAll()
3293a8492633481c1724d2f6d418594faf6e982d3dc9mindyp                && mFolder.supportsCapability
3294a8492633481c1724d2f6d418594faf6e982d3dc9mindyp                    (UIProvider.FolderCapabilities.CAN_ACCEPT_MOVED_MESSAGES);
3295a8492633481c1724d2f6d418594faf6e982d3dc9mindyp        if (isDestructive) {
3296a8492633481c1724d2f6d418594faf6e982d3dc9mindyp            dragDropOperations.add(new FolderOperation(mFolder, false));
3297a8492633481c1724d2f6d418594faf6e982d3dc9mindyp        }
32988db7e407109532557718c6b8064792f2df7a073dMindy Pereira        // Drag and drop is destructive: we remove conversations from the
32998db7e407109532557718c6b8064792f2df7a073dMindy Pereira        // current folder.
33006a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy        final DestructiveAction action =
33016a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                getFolderChange(conversations, dragDropOperations, isDestructive,
330230c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                        true /* isBatch */, true /* showUndo */, true /* isMoveTo */, folder,
330330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                        null /* undoCallback */);
3304a8492633481c1724d2f6d418594faf6e982d3dc9mindyp        if (isDestructive) {
3305caaeed3837316175111bb16e6b60aeb0d523debfScott Kennedy            delete(0, conversations, action, true);
3306a8492633481c1724d2f6d418594faf6e982d3dc9mindyp        } else {
3307a8492633481c1724d2f6d418594faf6e982d3dc9mindyp            action.performAction();
3308a8492633481c1724d2f6d418594faf6e982d3dc9mindyp        }
3309acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereira    }
33100963ef8394e1f0bca3f931f930b127b00790361aMindy Pereira
3311ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp    private void handleDragFromStarred(Folder folder) {
3312ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp        final Collection<Conversation> conversations = mSelectedSet.values();
3313ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp        // The conversation list deletes and performs the action if it exists.
3314ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp        final ConversationListFragment convListFragment = getConversationListFragment();
3315ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp        // There should always be a convlistfragment, or the user could not have
3316ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp        // dragged/ dropped conversations.
3317ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp        if (convListFragment != null) {
3318ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp            LogUtils.d(LOG_TAG, "AAC.requestDelete: ListFragment is handling delete.");
3319ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp            ArrayList<ConversationOperation> ops = new ArrayList<ConversationOperation>();
3320cb0b30ee1d5cfcc267bc7de1e6ad78ed766c1e50mindyp            ArrayList<Uri> folderUris;
3321cb0b30ee1d5cfcc267bc7de1e6ad78ed766c1e50mindyp            ArrayList<Boolean> adds;
3322ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp            for (Conversation target : conversations) {
3323cb0b30ee1d5cfcc267bc7de1e6ad78ed766c1e50mindyp                folderUris = new ArrayList<Uri>();
3324cb0b30ee1d5cfcc267bc7de1e6ad78ed766c1e50mindyp                adds = new ArrayList<Boolean>();
3325259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy                folderUris.add(folder.folderUri.fullUri);
3326cb0b30ee1d5cfcc267bc7de1e6ad78ed766c1e50mindyp                adds.add(Boolean.TRUE);
332726746eb4acb159f3ce0d411c10d85a1de3e958a0Paul Westbrook                final HashMap<Uri, Folder> targetFolders =
332826746eb4acb159f3ce0d411c10d85a1de3e958a0Paul Westbrook                        Folder.hashMapForFolders(target.getRawFolders());
3329259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy                targetFolders.put(folder.folderUri.fullUri, folder);
333026746eb4acb159f3ce0d411c10d85a1de3e958a0Paul Westbrook                ops.add(mConversationListCursor.getConversationFolderOperation(target,
333126746eb4acb159f3ce0d411c10d85a1de3e958a0Paul Westbrook                        folderUris, adds, targetFolders.values()));
3332ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp            }
3333ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp            if (mConversationListCursor != null) {
33349e2d407fdafeb874e640eb84017feaf784309075Scott Kennedy                mConversationListCursor.updateBulkValues(ops);
3335ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp            }
3336ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp            refreshConversationList();
3337ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp            mSelectedSet.clear();
3338ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp        }
3339ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp    }
3340ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp
3341ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp    private void handleDropInStarred(Folder folder) {
3342ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp        final Collection<Conversation> conversations = mSelectedSet.values();
3343ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp        // The conversation list deletes and performs the action if it exists.
3344ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp        final ConversationListFragment convListFragment = getConversationListFragment();
3345ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp        // There should always be a convlistfragment, or the user could not have
3346ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp        // dragged/ dropped conversations.
3347ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp        if (convListFragment != null) {
3348ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp            LogUtils.d(LOG_TAG, "AAC.requestDelete: ListFragment is handling delete.");
33496c570db71b8adc43d7dc2f31e6fa63cf50c1426eAndrew Sapperstein            convListFragment.requestDelete(R.id.change_folders, conversations,
33505cc0ab20009100e3ef259fe3d2e3ddc357f79285mindyp                    new DroppedInStarredAction(conversations, mFolder, folder));
3351ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp        }
3352ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp    }
3353ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp
3354ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp    // When dragging conversations to the starred folder, remove from the
3355ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp    // original folder and add a star
3356ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp    private class DroppedInStarredAction implements DestructiveAction {
3357e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon        private final Collection<Conversation> mConversations;
3358e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon        private final Folder mInitialFolder;
3359e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon        private final Folder mStarred;
3360ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp
33615cc0ab20009100e3ef259fe3d2e3ddc357f79285mindyp        public DroppedInStarredAction(Collection<Conversation> conversations, Folder initialFolder,
33625cc0ab20009100e3ef259fe3d2e3ddc357f79285mindyp                Folder starredFolder) {
3363ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp            mConversations = conversations;
33645cc0ab20009100e3ef259fe3d2e3ddc357f79285mindyp            mInitialFolder = initialFolder;
33655cc0ab20009100e3ef259fe3d2e3ddc357f79285mindyp            mStarred = starredFolder;
3366ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp        }
3367ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp
3368ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp        @Override
336930c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        public void setUndoCallback(UndoCallback undoCallback) {
337030c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            return;     // currently not applicable
337130c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        }
337230c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao
337330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        @Override
3374ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp        public void performAction() {
3375ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp            ToastBarOperation undoOp = new ToastBarOperation(mConversations.size(),
33766c570db71b8adc43d7dc2f31e6fa63cf50c1426eAndrew Sapperstein                    R.id.change_folders, ToastBarOperation.UNDO, true /* batch */, mInitialFolder);
3377ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp            onUndoAvailable(undoOp);
3378ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp            ArrayList<ConversationOperation> ops = new ArrayList<ConversationOperation>();
3379ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp            ContentValues values = new ContentValues();
3380cb0b30ee1d5cfcc267bc7de1e6ad78ed766c1e50mindyp            ArrayList<Uri> folderUris;
3381cb0b30ee1d5cfcc267bc7de1e6ad78ed766c1e50mindyp            ArrayList<Boolean> adds;
33825cc0ab20009100e3ef259fe3d2e3ddc357f79285mindyp            ConversationOperation operation;
3383ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp            for (Conversation target : mConversations) {
3384cb0b30ee1d5cfcc267bc7de1e6ad78ed766c1e50mindyp                folderUris = new ArrayList<Uri>();
3385cb0b30ee1d5cfcc267bc7de1e6ad78ed766c1e50mindyp                adds = new ArrayList<Boolean>();
3386259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy                folderUris.add(mStarred.folderUri.fullUri);
33875cc0ab20009100e3ef259fe3d2e3ddc357f79285mindyp                adds.add(Boolean.TRUE);
3388259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy                folderUris.add(mInitialFolder.folderUri.fullUri);
3389cb0b30ee1d5cfcc267bc7de1e6ad78ed766c1e50mindyp                adds.add(Boolean.FALSE);
33905cc0ab20009100e3ef259fe3d2e3ddc357f79285mindyp                final HashMap<Uri, Folder> targetFolders =
33915cc0ab20009100e3ef259fe3d2e3ddc357f79285mindyp                        Folder.hashMapForFolders(target.getRawFolders());
3392259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy                targetFolders.put(mStarred.folderUri.fullUri, mStarred);
3393259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy                targetFolders.remove(mInitialFolder.folderUri.fullUri);
33945cc0ab20009100e3ef259fe3d2e3ddc357f79285mindyp                values.put(ConversationColumns.STARRED, true);
33955cc0ab20009100e3ef259fe3d2e3ddc357f79285mindyp                operation = mConversationListCursor.getConversationFolderOperation(target,
33965cc0ab20009100e3ef259fe3d2e3ddc357f79285mindyp                        folderUris, adds, targetFolders.values(), values);
33975cc0ab20009100e3ef259fe3d2e3ddc357f79285mindyp                ops.add(operation);
3398ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp            }
3399ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp            if (mConversationListCursor != null) {
34009e2d407fdafeb874e640eb84017feaf784309075Scott Kennedy                mConversationListCursor.updateBulkValues(ops);
3401ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp            }
3402ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp            refreshConversationList();
3403ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp            mSelectedSet.clear();
3404ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp        }
3405ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp    }
3406ae7e6a0786a2d890b77c783d7ac39a90523b8154mindyp
34070963ef8394e1f0bca3f931f930b127b00790361aMindy Pereira    @Override
34080963ef8394e1f0bca3f931f930b127b00790361aMindy Pereira    public void onTouchEvent(MotionEvent event) {
34090963ef8394e1f0bca3f931f930b127b00790361aMindy Pereira        if (event.getAction() == MotionEvent.ACTION_DOWN) {
3410c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein            if (mToastBar != null && !mToastBar.isEventInToastBar(event)) {
34119a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux                // if the toast bar is still animating, ignore this attempt to hide it
34129a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux                if (mToastBar.isAnimating()) {
34139a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux                    return;
34149a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux                }
34159a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux
34169a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux                // if the toast bar has not been seen long enough, ignore this attempt to hide it
34179a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux                if (mToastBar.cannotBeHidden()) {
34189a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux                    return;
34199a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux                }
34209a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux
34219a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux                // hide the toast bar
34229a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux                mToastBar.hide(true /* animated */, false /* actionClicked */);
34230963ef8394e1f0bca3f931f930b127b00790361aMindy Pereira            }
34240963ef8394e1f0bca3f931f930b127b00790361aMindy Pereira        }
34250963ef8394e1f0bca3f931f930b127b00790361aMindy Pereira    }
3426b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang
3427632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
34283b965d78774a42358ce6bbdcc43b4c8df130a60eScott Kennedy    public void onConversationSeen() {
34293b965d78774a42358ce6bbdcc43b4c8df130a60eScott Kennedy        mPagerController.onConversationSeen();
3430632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
3431632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
34329d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang    @Override
34339d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang    public boolean isInitialConversationLoading() {
34349d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang        return mPagerController.isInitialConversationLoading();
34359d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang    }
34369d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang
34376422b8d051c93b4f34580c551254fc50c9efa4dbVikram Aggarwal    /**
34386422b8d051c93b4f34580c551254fc50c9efa4dbVikram Aggarwal     * Check if the fragment given here is visible. Checking {@link Fragment#isVisible()} is
34396422b8d051c93b4f34580c551254fc50c9efa4dbVikram Aggarwal     * insufficient because that doesn't check if the window is currently in focus or not.
34406422b8d051c93b4f34580c551254fc50c9efa4dbVikram Aggarwal     */
3441715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal    private boolean isFragmentVisible(Fragment in) {
34426422b8d051c93b4f34580c551254fc50c9efa4dbVikram Aggarwal        return in != null && in.isVisible() && mActivity.hasWindowFocus();
34436422b8d051c93b4f34580c551254fc50c9efa4dbVikram Aggarwal    }
34446422b8d051c93b4f34580c551254fc50c9efa4dbVikram Aggarwal
3445d70fe49cabf896e87574bcb2e57e8a32608537beVikram Aggarwal    /**
3446d70fe49cabf896e87574bcb2e57e8a32608537beVikram Aggarwal     * This class handles callbacks that create a {@link ConversationCursor}.
3447d70fe49cabf896e87574bcb2e57e8a32608537beVikram Aggarwal     */
3448b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang    private class ConversationListLoaderCallbacks implements
3449b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang        LoaderManager.LoaderCallbacks<ConversationCursor> {
3450b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang
3451b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang        @Override
3452b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang        public Loader<ConversationCursor> onCreateLoader(int id, Bundle args) {
3453a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal            final Account account = args.getParcelable(BUNDLE_ACCOUNT_KEY);
3454a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal            final Folder folder = args.getParcelable(BUNDLE_FOLDER_KEY);
34555bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein            final boolean ignoreInitialConversationLimit =
34565bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein                    args.getBoolean(BUNDLE_IGNORE_INITIAL_CONVERSATION_LIMIT_KEY, false);
3457a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal            if (account == null || folder == null) {
3458a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal                return null;
3459a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal            }
34605bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein            return new ConversationCursorLoader(mActivity, account,
3461f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang                    folder.conversationListUri, folder.getTypeDescription(),
3462f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang                    ignoreInitialConversationLimit);
3463b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang        }
3464b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang
3465b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang        @Override
3466b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang        public void onLoadFinished(Loader<ConversationCursor> loader, ConversationCursor data) {
3467144bfe739b93afdee0a1700a34806b0b287e5887Andy Huang            LogUtils.d(LOG_TAG,
3468144bfe739b93afdee0a1700a34806b0b287e5887Andy Huang                    "IN AAC.ConversationCursor.onLoadFinished, data=%s loader=%s this=%s",
3469144bfe739b93afdee0a1700a34806b0b287e5887Andy Huang                    data, loader, this);
3470a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein            if (isDrawerEnabled() && mDrawerListener.getDrawerState() != DrawerLayout.STATE_IDLE) {
3471a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal                LogUtils.d(LOG_TAG, "ConversationListLoaderCallbacks.onLoadFinished: ignoring.");
3472a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein                mConversationListLoadFinishedIgnored = true;
3473a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal                return;
3474a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal            }
3475e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal            // Clear our all pending destructive actions before swapping the conversation cursor
3476e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal            destroyPending(null);
3477b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang            mConversationListCursor = data;
3478bf232c3735f65b1a4746943e4a134e59e36f0bdePaul Westbrook            mConversationListCursor.addListener(AbstractActivityController.this);
3479144bfe739b93afdee0a1700a34806b0b287e5887Andy Huang            mDrawIdler.setListener(mConversationListCursor);
3480937c94f025ed55c19bd46c7c077fa3575ab6be52Paul Westbrook            mTracker.onCursorUpdated();
3481e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang            mConversationListObservable.notifyChanged();
34827c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu            // Handle actions that were deferred until after the conversation list was loaded.
34837c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu            for (LoadFinishedCallback callback : mConversationListLoadFinishedCallbacks) {
34847c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu                callback.onLoadFinished();
34857c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu            }
34867c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu            mConversationListLoadFinishedCallbacks.clear();
34879f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook
348817f373ecf6961112f77a7516ab1bbcb4fcdf2884Vikram Aggarwal            final ConversationListFragment convList = getConversationListFragment();
34896422b8d051c93b4f34580c551254fc50c9efa4dbVikram Aggarwal            if (isFragmentVisible(convList)) {
349017f373ecf6961112f77a7516ab1bbcb4fcdf2884Vikram Aggarwal                // The conversation list is already listening to list changes and gets notified
349117f373ecf6961112f77a7516ab1bbcb4fcdf2884Vikram Aggarwal                // in the mConversationListObservable.notifyChanged() line above. We only need to
349217f373ecf6961112f77a7516ab1bbcb4fcdf2884Vikram Aggarwal                // check and inform the cursor of the change in visibility here.
349317f373ecf6961112f77a7516ab1bbcb4fcdf2884Vikram Aggarwal                informCursorVisiblity(true);
3494b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang            }
3495f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal            perhapsShowFirstSearchResult();
3496b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang        }
3497b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang
3498b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang        @Override
3499b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang        public void onLoaderReset(Loader<ConversationCursor> loader) {
3500144bfe739b93afdee0a1700a34806b0b287e5887Andy Huang            LogUtils.d(LOG_TAG,
3501144bfe739b93afdee0a1700a34806b0b287e5887Andy Huang                    "IN AAC.ConversationCursor.onLoaderReset, data=%s loader=%s this=%s",
3502144bfe739b93afdee0a1700a34806b0b287e5887Andy Huang                    mConversationListCursor, loader, this);
35039a70e919125d63971604e6dd3de8bc998e9c86daPaul Westbrook
35049a70e919125d63971604e6dd3de8bc998e9c86daPaul Westbrook            if (mConversationListCursor != null) {
35059a70e919125d63971604e6dd3de8bc998e9c86daPaul Westbrook                // Unregister the listener
35069a70e919125d63971604e6dd3de8bc998e9c86daPaul Westbrook                mConversationListCursor.removeListener(AbstractActivityController.this);
3507144bfe739b93afdee0a1700a34806b0b287e5887Andy Huang                mDrawIdler.setListener(null);
35089a70e919125d63971604e6dd3de8bc998e9c86daPaul Westbrook                mConversationListCursor = null;
35099a70e919125d63971604e6dd3de8bc998e9c86daPaul Westbrook
35109a70e919125d63971604e6dd3de8bc998e9c86daPaul Westbrook                // Inform anyone who is interested about the change
35119a70e919125d63971604e6dd3de8bc998e9c86daPaul Westbrook                mTracker.onCursorUpdated();
35129a70e919125d63971604e6dd3de8bc998e9c86daPaul Westbrook                mConversationListObservable.notifyChanged();
3513b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang            }
3514b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang        }
3515bf232c3735f65b1a4746943e4a134e59e36f0bdePaul Westbrook    }
3516bf232c3735f65b1a4746943e4a134e59e36f0bdePaul Westbrook
3517e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal    /**
3518177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal     * Class to perform {@link LoaderManager.LoaderCallbacks} for creating {@link Folder} objects.
3519177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal     */
3520177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    private class FolderLoads implements LoaderManager.LoaderCallbacks<ObjectCursor<Folder>> {
3521177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        @Override
3522177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        public Loader<ObjectCursor<Folder>> onCreateLoader(int id, Bundle args) {
3523177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            final String[] everything = UIProvider.FOLDERS_PROJECTION;
3524177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            switch (id) {
3525177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                case LOADER_FOLDER_CURSOR:
3526177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    LogUtils.d(LOG_TAG, "LOADER_FOLDER_CURSOR created");
3527177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    final ObjectCursorLoader<Folder> loader = new
3528177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            ObjectCursorLoader<Folder>(
3529259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy                            mContext, mFolder.folderUri.fullUri, everything, Folder.FACTORY);
3530177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    loader.setUpdateThrottle(mFolderItemUpdateDelayMs);
3531177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    return loader;
3532177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                case LOADER_RECENT_FOLDERS:
3533177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    LogUtils.d(LOG_TAG, "LOADER_RECENT_FOLDERS created");
3534f2632b979d7f17a548c34cccae94b2a0046cee32Yu Ping Hu                    if (mAccount != null && mAccount.recentFolderListUri != null
3535f2632b979d7f17a548c34cccae94b2a0046cee32Yu Ping Hu                            && !mAccount.recentFolderListUri.equals(Uri.EMPTY)) {
3536177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        return new ObjectCursorLoader<Folder>(mContext,
3537177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                mAccount.recentFolderListUri, everything, Folder.FACTORY);
3538177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3539177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    break;
3540177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                case LOADER_ACCOUNT_INBOX:
3541177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    LogUtils.d(LOG_TAG, "LOADER_ACCOUNT_INBOX created");
3542177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    final Uri defaultInbox = Settings.getDefaultInboxUri(mAccount.settings);
3543177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    final Uri inboxUri = defaultInbox.equals(Uri.EMPTY) ?
3544177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            mAccount.folderListUri : defaultInbox;
3545177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    LogUtils.d(LOG_TAG, "Loading the default inbox: %s", inboxUri);
3546177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    if (inboxUri != null) {
3547177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        return new ObjectCursorLoader<Folder>(mContext, inboxUri,
3548177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                everything, Folder.FACTORY);
3549177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3550177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    break;
3551177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                case LOADER_SEARCH:
3552177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    LogUtils.d(LOG_TAG, "LOADER_SEARCH created");
3553177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    return Folder.forSearchResults(mAccount,
3554177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            args.getString(ConversationListContext.EXTRA_SEARCH_QUERY),
35551a864cce6321f46031d6cd43e205278d0f219f92Jin Cao                            // We can just use current time as a unique identifier for this search
35561a864cce6321f46031d6cd43e205278d0f219f92Jin Cao                            Long.toString(SystemClock.uptimeMillis()),
3557177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            mActivity.getActivityContext());
3558177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                case LOADER_FIRST_FOLDER:
3559177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    LogUtils.d(LOG_TAG, "LOADER_FIRST_FOLDER created");
3560177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    final Uri folderUri = args.getParcelable(Utils.EXTRA_FOLDER_URI);
3561177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    mConversationToShow = args.getParcelable(Utils.EXTRA_CONVERSATION);
3562177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    if (mConversationToShow != null && mConversationToShow.position < 0){
3563177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        mConversationToShow.position = 0;
3564177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3565177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    return new ObjectCursorLoader<Folder>(mContext, folderUri,
3566177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            everything, Folder.FACTORY);
3567177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                default:
3568177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    LogUtils.wtf(LOG_TAG, "FolderLoads.onCreateLoader(%d) for invalid id", id);
3569177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    return null;
3570177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            }
3571177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            return null;
3572177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        }
3573177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal
3574177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        @Override
3575177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        public void onLoadFinished(Loader<ObjectCursor<Folder>> loader, ObjectCursor<Folder> data) {
3576177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            if (data == null) {
3577177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                LogUtils.e(LOG_TAG, "Received null cursor from loader id: %d", loader.getId());
3578177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            }
3579177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            switch (loader.getId()) {
3580177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                case LOADER_FOLDER_CURSOR:
3581177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    if (data != null && data.moveToFirst()) {
3582177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        final Folder folder = data.getModel();
3583177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        setHasFolderChanged(folder);
3584177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        mFolder = folder;
3585177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        mFolderObservable.notifyChanged();
3586177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    } else {
3587177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        LogUtils.d(LOG_TAG, "Unable to get the folder %s",
358826a20758413f0f8629ea71a0943304e3ae740165Tony Mantler                                mFolder != null ? mFolder.name : "");
3589177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3590177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    break;
3591177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                case LOADER_RECENT_FOLDERS:
3592177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    // Few recent folders and we are running on a phone? Populate the default
3593177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    // recents. The number of default recent folders is at least 2: every provider
3594177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    // has at least two folders, and the recent folder count never decreases.
3595177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    // Having a single recent folder is an erroneous case, and we can gracefully
3596177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    // recover by populating default recents. The default recents will not stomp on
3597177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    // the existing value: it will be shown in addition to the default folders:
3598177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    // the max number of recent folders is more than 1+num(defaultRecents).
3599177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    if (data != null && data.getCount() <= 1 && !mIsTablet) {
3600177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        final class PopulateDefault extends AsyncTask<Uri, Void, Void> {
3601177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            @Override
3602177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            protected Void doInBackground(Uri... uri) {
3603177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                // Asking for an update on the URI and ignore the result.
3604177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                final ContentResolver resolver = mContext.getContentResolver();
3605177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                resolver.update(uri[0], null, null, null);
3606177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                return null;
3607177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            }
3608177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        }
3609177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        final Uri uri = mAccount.defaultRecentFolderListUri;
3610177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        LogUtils.v(LOG_TAG, "Default recents at %s", uri);
3611177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        new PopulateDefault().execute(uri);
3612177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        break;
3613177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3614177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    LogUtils.v(LOG_TAG, "Reading recent folders from the cursor.");
3615177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    mRecentFolderList.loadFromUiProvider(data);
3616177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    if (isAnimating()) {
3617177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        mRecentsDataUpdated = true;
3618177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    } else {
3619177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        mRecentFolderObservers.notifyChanged();
3620177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3621177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    break;
3622177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                case LOADER_ACCOUNT_INBOX:
3623177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    if (data != null && !data.isClosed() && data.moveToFirst()) {
3624177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        final Folder inbox = data.getModel();
3625ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                        onFolderChanged(inbox, false /* force */);
3626177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        // Just want to get the inbox, don't care about updates to it
3627177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        // as this will be tracked by the folder change listener.
3628177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        mActivity.getLoaderManager().destroyLoader(LOADER_ACCOUNT_INBOX);
3629177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    } else {
3630177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        LogUtils.d(LOG_TAG, "Unable to get the account inbox for account %s",
363126a20758413f0f8629ea71a0943304e3ae740165Tony Mantler                                mAccount != null ? mAccount.getEmailAddress() : "");
3632177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3633177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    break;
3634177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                case LOADER_SEARCH:
3635177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    if (data != null && data.getCount() > 0) {
3636177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        data.moveToFirst();
3637177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        final Folder search = data.getModel();
3638177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        updateFolder(search);
3639177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        mConvListContext = ConversationListContext.forSearchQuery(mAccount, mFolder,
3640177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                mActivity.getIntent()
3641177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                        .getStringExtra(UIProvider.SearchQueryParameters.QUERY));
3642177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        showConversationList(mConvListContext);
3643177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        mActivity.invalidateOptionsMenu();
3644177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        mHaveSearchResults = search.totalCount > 0;
3645177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        mActivity.getLoaderManager().destroyLoader(LOADER_SEARCH);
3646177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    } else {
3647177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        LogUtils.e(LOG_TAG, "Null/empty cursor returned by LOADER_SEARCH loader");
3648177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3649177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    break;
3650177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                case LOADER_FIRST_FOLDER:
3651177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    if (data == null || data.getCount() <=0 || !data.moveToFirst()) {
3652177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        return;
3653177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3654177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    final Folder folder = data.getModel();
3655177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    boolean handled = false;
3656177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    if (folder != null) {
3657ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                        onFolderChanged(folder, false /* force */);
3658177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        handled = true;
3659177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3660177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    if (mConversationToShow != null) {
3661177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        // Open the conversation.
3662177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        showConversation(mConversationToShow);
3663177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        handled = true;
3664177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3665177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    if (!handled) {
3666177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        // We have an account, but nothing else: load the default inbox.
3667177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        loadAccountInbox();
3668177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3669177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    mConversationToShow = null;
3670177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    // And don't run this anymore.
3671177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    mActivity.getLoaderManager().destroyLoader(LOADER_FIRST_FOLDER);
3672177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    break;
3673177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            }
3674177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        }
3675177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal
3676177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        @Override
3677177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        public void onLoaderReset(Loader<ObjectCursor<Folder>> loader) {
3678177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        }
3679177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    }
3680177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal
3681177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    /**
3682177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal     * Class to perform {@link LoaderManager.LoaderCallbacks} for creating {@link Account} objects.
3683177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal     */
3684177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    private class AccountLoads implements LoaderManager.LoaderCallbacks<ObjectCursor<Account>> {
3685177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        final String[] mProjection = UIProvider.ACCOUNTS_PROJECTION;
3686177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        final CursorCreator<Account> mFactory = Account.FACTORY;
3687177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal
3688177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        @Override
3689177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        public Loader<ObjectCursor<Account>> onCreateLoader(int id, Bundle args) {
3690177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            switch (id) {
3691177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                case LOADER_ACCOUNT_CURSOR:
3692177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    LogUtils.d(LOG_TAG,  "LOADER_ACCOUNT_CURSOR created");
3693177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    return new ObjectCursorLoader<Account>(mContext,
3694177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            MailAppProvider.getAccountsUri(), mProjection, mFactory);
3695177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                case LOADER_ACCOUNT_UPDATE_CURSOR:
3696177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    LogUtils.d(LOG_TAG,  "LOADER_ACCOUNT_UPDATE_CURSOR created");
3697177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    return new ObjectCursorLoader<Account>(mContext, mAccount.uri, mProjection,
3698177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            mFactory);
3699177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                default:
3700177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    LogUtils.wtf(LOG_TAG, "Got an id  (%d) that I cannot create!", id);
3701177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    break;
3702177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            }
3703177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            return null;
3704177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        }
3705177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal
3706177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        @Override
3707177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        public void onLoadFinished(Loader<ObjectCursor<Account>> loader,
3708177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                ObjectCursor<Account> data) {
3709177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            if (data == null) {
3710177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                LogUtils.e(LOG_TAG, "Received null cursor from loader id: %d", loader.getId());
3711177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            }
3712177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            switch (loader.getId()) {
3713177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                case LOADER_ACCOUNT_CURSOR:
3714d70fe49cabf896e87574bcb2e57e8a32608537beVikram Aggarwal                    // We have received an update on the list of accounts.
3715177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    if (data == null) {
3716177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        // Nothing useful to do if we have no valid data.
3717177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        break;
3718177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3719761522c1ac7a8fa05acde714f0e26fde47f3415fAndy Huang                    final long count = data.getCount();
3720761522c1ac7a8fa05acde714f0e26fde47f3415fAndy Huang                    if (count == 0) {
3721177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        // If an empty cursor is returned, the MailAppProvider is indicating that
3722177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        // no accounts have been specified.  We want to navigate to the
3723177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        // "add account" activity that will handle the intent returned by the
3724177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        // MailAppProvider
3725177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal
3726177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        // If the MailAppProvider believes that all accounts have been loaded,
3727177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        // and the account list is still empty, we want to prompt the user to add
3728177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        // an account.
3729177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        final Bundle extras = data.getExtras();
3730177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        final boolean accountsLoaded =
3731177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                extras.getInt(AccountCursorExtraKeys.ACCOUNTS_LOADED) != 0;
3732177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal
3733177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        if (accountsLoaded) {
3734177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            final Intent noAccountIntent = MailAppProvider.getNoAccountIntent
3735177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                    (mContext);
3736177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            if (noAccountIntent != null) {
3737177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                mActivity.startActivityForResult(noAccountIntent,
3738177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                        ADD_ACCOUNT_REQUEST_CODE);
3739177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            }
3740177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        }
3741177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    } else {
3742177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        final boolean accountListUpdated = accountsUpdated(data);
3743c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal                        if (!mHaveAccountList || accountListUpdated) {
3744c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal                            mHaveAccountList = updateAccounts(data);
3745177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        }
3746761522c1ac7a8fa05acde714f0e26fde47f3415fAndy Huang                        Analytics.getInstance().setCustomDimension(Analytics.CD_INDEX_ACCOUNT_COUNT,
3747761522c1ac7a8fa05acde714f0e26fde47f3415fAndy Huang                                Long.toString(count));
3748177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3749177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    break;
3750177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                case LOADER_ACCOUNT_UPDATE_CURSOR:
3751177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    // We have received an update for current account.
3752177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    if (data != null && data.moveToFirst()) {
3753177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        final Account updatedAccount = data.getModel();
3754d70fe49cabf896e87574bcb2e57e8a32608537beVikram Aggarwal                        // Make sure that this is an update for the current account
3755177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        if (updatedAccount.uri.equals(mAccount.uri)) {
3756177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            final Settings previousSettings = mAccount.settings;
3757177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal
3758177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            // Update the controller's reference to the current account
3759177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            mAccount = updatedAccount;
3760177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            LogUtils.d(LOG_TAG, "AbstractActivityController.onLoadFinished(): "
3761177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                    + "mAccount = %s", mAccount.uri);
3762177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal
3763177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            // Only notify about a settings change if something differs
3764177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            if (!Objects.equal(mAccount.settings, previousSettings)) {
3765177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                mAccountObservers.notifyChanged();
3766177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            }
3767177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            perhapsEnterWaitMode();
376862e1496cb42c21b5348ebe795cd2fabd7574d3e1Régis Décamps                            perhapsStartWelcomeTour();
3769177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        } else {
3770177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            LogUtils.e(LOG_TAG, "Got update for account: %s with current account:"
3771177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                    + " %s", updatedAccount.uri, mAccount.uri);
3772177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            // We need to restart the loader, so the correct account information
3773177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            // will be returned.
3774177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            restartOptionalLoader(LOADER_ACCOUNT_UPDATE_CURSOR, this, Bundle.EMPTY);
3775177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        }
3776177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3777177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    break;
3778177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            }
3779177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        }
3780177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal
3781177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        @Override
3782177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        public void onLoaderReset(Loader<ObjectCursor<Account>> loader) {
3783d70fe49cabf896e87574bcb2e57e8a32608537beVikram Aggarwal            // Do nothing. In onLoadFinished() we copy the relevant data from the cursor.
3784177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        }
3785177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    }
3786177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal
3787177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    /**
378862e1496cb42c21b5348ebe795cd2fabd7574d3e1Régis Décamps     * Loads the preference that tells whether the welcome tour should be displayed,
378962e1496cb42c21b5348ebe795cd2fabd7574d3e1Régis Décamps     * and calls the callback with this value.
379062e1496cb42c21b5348ebe795cd2fabd7574d3e1Régis Décamps     * For this to function, the account must have been synced.
379162e1496cb42c21b5348ebe795cd2fabd7574d3e1Régis Décamps     */
379262e1496cb42c21b5348ebe795cd2fabd7574d3e1Régis Décamps    private void perhapsStartWelcomeTour() {
3793550f15f6ab58abe8dca08ac8a0032b8e8930093dPaul Westbrook        new AsyncTask<Void, Void, Boolean>() {
3794550f15f6ab58abe8dca08ac8a0032b8e8930093dPaul Westbrook            @Override
3795550f15f6ab58abe8dca08ac8a0032b8e8930093dPaul Westbrook            protected Boolean doInBackground(Void... params) {
3796550f15f6ab58abe8dca08ac8a0032b8e8930093dPaul Westbrook                if (mActivity.wasLatestWelcomeTourShownOnDeviceForAllAccounts()) {
3797550f15f6ab58abe8dca08ac8a0032b8e8930093dPaul Westbrook                    // No need to go through the WelcomeStateLoader machinery.
3798550f15f6ab58abe8dca08ac8a0032b8e8930093dPaul Westbrook                    return false;
3799550f15f6ab58abe8dca08ac8a0032b8e8930093dPaul Westbrook                }
3800550f15f6ab58abe8dca08ac8a0032b8e8930093dPaul Westbrook                return true;
3801550f15f6ab58abe8dca08ac8a0032b8e8930093dPaul Westbrook            }
3802f318bc3cb806774a07421a413a918aa6afb8792fRégis Décamps
3803550f15f6ab58abe8dca08ac8a0032b8e8930093dPaul Westbrook            @Override
3804550f15f6ab58abe8dca08ac8a0032b8e8930093dPaul Westbrook            protected void onPostExecute(Boolean result) {
3805550f15f6ab58abe8dca08ac8a0032b8e8930093dPaul Westbrook                if (result) {
3806550f15f6ab58abe8dca08ac8a0032b8e8930093dPaul Westbrook                    if (mAccount != null && mAccount.isAccountReady()) {
3807550f15f6ab58abe8dca08ac8a0032b8e8930093dPaul Westbrook                        LoaderManager.LoaderCallbacks<?> welcomeLoaderCallbacks =
3808550f15f6ab58abe8dca08ac8a0032b8e8930093dPaul Westbrook                                mActivity.getWelcomeCallbacks();
3809550f15f6ab58abe8dca08ac8a0032b8e8930093dPaul Westbrook                        if (welcomeLoaderCallbacks != null) {
3810550f15f6ab58abe8dca08ac8a0032b8e8930093dPaul Westbrook                            // The callback is responsible for showing the tour when appropriate.
3811550f15f6ab58abe8dca08ac8a0032b8e8930093dPaul Westbrook                            mActivity.getLoaderManager().initLoader(LOADER_WELCOME_TOUR_ACCOUNTS,
3812550f15f6ab58abe8dca08ac8a0032b8e8930093dPaul Westbrook                                    Bundle.EMPTY, welcomeLoaderCallbacks);
3813550f15f6ab58abe8dca08ac8a0032b8e8930093dPaul Westbrook                        }
3814550f15f6ab58abe8dca08ac8a0032b8e8930093dPaul Westbrook                    }
3815550f15f6ab58abe8dca08ac8a0032b8e8930093dPaul Westbrook                }
381662e1496cb42c21b5348ebe795cd2fabd7574d3e1Régis Décamps            }
3817550f15f6ab58abe8dca08ac8a0032b8e8930093dPaul Westbrook        }.execute();
381862e1496cb42c21b5348ebe795cd2fabd7574d3e1Régis Décamps    }
381962e1496cb42c21b5348ebe795cd2fabd7574d3e1Régis Décamps
382062e1496cb42c21b5348ebe795cd2fabd7574d3e1Régis Décamps    /**
3821f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal     * Updates controller state based on search results and shows first conversation if required.
3822f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal     */
3823715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal    private void perhapsShowFirstSearchResult() {
3824d27009ac6644a85ebcd1c705f40d2b5e9fd03573mindyp        if (mCurrentConversation == null) {
3825d27009ac6644a85ebcd1c705f40d2b5e9fd03573mindyp            // Shown for search results in two-pane mode only.
3826d27009ac6644a85ebcd1c705f40d2b5e9fd03573mindyp            mHaveSearchResults = Intent.ACTION_SEARCH.equals(mActivity.getIntent().getAction())
3827d27009ac6644a85ebcd1c705f40d2b5e9fd03573mindyp                    && mConversationListCursor.getCount() > 0;
3828d27009ac6644a85ebcd1c705f40d2b5e9fd03573mindyp            if (!shouldShowFirstConversation()) {
3829d27009ac6644a85ebcd1c705f40d2b5e9fd03573mindyp                return;
3830d27009ac6644a85ebcd1c705f40d2b5e9fd03573mindyp            }
3831d27009ac6644a85ebcd1c705f40d2b5e9fd03573mindyp            mConversationListCursor.moveToPosition(0);
3832d27009ac6644a85ebcd1c705f40d2b5e9fd03573mindyp            final Conversation conv = new Conversation(mConversationListCursor);
3833d27009ac6644a85ebcd1c705f40d2b5e9fd03573mindyp            conv.position = 0;
3834d27009ac6644a85ebcd1c705f40d2b5e9fd03573mindyp            onConversationSelected(conv, true /* checkSafeToModifyFragments */);
3835f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal        }
3836f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal    }
3837f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal
3838f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal    /**
3839e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal     * Destroy the pending {@link DestructiveAction} till now and assign the given action as the
3840e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal     * next destructive action..
3841e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal     * @param nextAction the next destructive action to be performed. This can be null.
3842e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal     */
3843715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal    private void destroyPending(DestructiveAction nextAction) {
3844e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal        // If there is a pending action, perform that first.
3845e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal        if (mPendingDestruction != null) {
3846e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal            mPendingDestruction.performAction();
3847e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal        }
3848e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal        mPendingDestruction = nextAction;
3849e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal    }
3850e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal
3851e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal    /**
3852e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal     * Register a destructive action with the controller. This performs the previous destructive
3853acaa3c0aa2335e0a635601e09c955388d698dfdaVikram Aggarwal     * action as a side effect. This method is final because we don't want the child classes to
38543d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal     * embellish this method any more.
3855715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @param action the action to register.
3856e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal     */
3857715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal    private void registerDestructiveAction(DestructiveAction action) {
3858e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal        // TODO(viki): This is not a good idea. The best solution is for clients to request a
3859e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal        // destructive action from the controller and for the controller to own the action. This is
3860e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal        // a half-way solution while refactoring DestructiveAction.
3861e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal        destroyPending(action);
3862e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal    }
3863e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal
3864531488ed848766708252a5fdc01c4ecf0fd167d1Vikram Aggarwal    @Override
386530c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao    public final DestructiveAction getBatchAction(int action, UndoCallback undoCallback) {
3866c41139509b1eb6dfc9bdae88ccc7d16eac85e2cfVikram Aggarwal        final DestructiveAction da = new ConversationAction(action, mSelectedSet.values(), true);
386730c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        da.setUndoCallback(undoCallback);
3868e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal        registerDestructiveAction(da);
3869e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal        return da;
3870e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal    }
387141e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal
3872de3e74a82043733243c7391d7f983a5af8842891Mindy Pereira    @Override
387330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao    public final DestructiveAction getDeferredBatchAction(int action, UndoCallback undoCallback) {
387430c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        return getDeferredAction(action, mSelectedSet.values(), true, undoCallback);
3875f0656a1ed2aaf07ee738505faa80b0723c8d217fmindyp    }
3876f0656a1ed2aaf07ee738505faa80b0723c8d217fmindyp
3877f0656a1ed2aaf07ee738505faa80b0723c8d217fmindyp    /**
3878f0656a1ed2aaf07ee738505faa80b0723c8d217fmindyp     * Get a destructive action for a menu action. This is a temporary method,
3879f0656a1ed2aaf07ee738505faa80b0723c8d217fmindyp     * to control the profusion of {@link DestructiveAction} classes that are
3880f0656a1ed2aaf07ee738505faa80b0723c8d217fmindyp     * created. Please do not copy this paradigm.
3881f0656a1ed2aaf07ee738505faa80b0723c8d217fmindyp     * @param action the resource ID of the menu action: R.id.delete, for
3882f0656a1ed2aaf07ee738505faa80b0723c8d217fmindyp     *            example
3883f0656a1ed2aaf07ee738505faa80b0723c8d217fmindyp     * @param target the conversations to act upon.
3884f0656a1ed2aaf07ee738505faa80b0723c8d217fmindyp     * @return a {@link DestructiveAction} that performs the specified action.
3885f0656a1ed2aaf07ee738505faa80b0723c8d217fmindyp     */
3886715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal    private DestructiveAction getDeferredAction(int action, Collection<Conversation> target,
388730c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            boolean batch, UndoCallback callback) {
388830c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        ConversationAction cAction = new ConversationAction(action, target, batch);
388930c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        cAction.setUndoCallback(callback);
389030c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        return cAction;
3891de3e74a82043733243c7391d7f983a5af8842891Mindy Pereira    }
3892de3e74a82043733243c7391d7f983a5af8842891Mindy Pereira
3893bc67bb1211654e69b1233e0f8759771873ca773eVikram Aggarwal    /**
3894bc67bb1211654e69b1233e0f8759771873ca773eVikram Aggarwal     * Class to change the folders that are assigned to a set of conversations. This is destructive
3895bc67bb1211654e69b1233e0f8759771873ca773eVikram Aggarwal     * because the user can remove the current folder from the conversation, in which case it has
3896bc67bb1211654e69b1233e0f8759771873ca773eVikram Aggarwal     * to be animated away from the current folder.
3897bc67bb1211654e69b1233e0f8759771873ca773eVikram Aggarwal     */
389841e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal    private class FolderDestruction implements DestructiveAction {
389977eee625cd8c317c605acfd06cd3a7e22120a0fdPaul Westbrook        private final Collection<Conversation> mTarget;
39008db7e407109532557718c6b8064792f2df7a073dMindy Pereira        private final ArrayList<FolderOperation> mFolderOps = new ArrayList<FolderOperation>();
390141e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal        private final boolean mIsDestructive;
39027f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal        /** Whether this destructive action has already been performed */
39037f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal        private boolean mCompleted;
3904e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon        private final boolean mIsSelectedSet;
3905e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon        private final boolean mShowUndo;
3906e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon        private final int mAction;
39076a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy        private final Folder mActionFolder;
390841e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal
390930c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        private UndoCallback mUndoCallback;
391030c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao
391141e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal        /**
391241e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal         * Create a new folder destruction object to act on the given conversations.
3913715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal         * @param target conversations to act upon.
39146a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy         * @param actionFolder the {@link Folder} being acted upon, used for displaying the undo bar
391541e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal         */
39167f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal        private FolderDestruction(final Collection<Conversation> target,
39178db7e407109532557718c6b8064792f2df7a073dMindy Pereira                final Collection<FolderOperation> folders, boolean isDestructive, boolean isBatch,
39186a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                boolean showUndo, int action, final Folder actionFolder) {
391977eee625cd8c317c605acfd06cd3a7e22120a0fdPaul Westbrook            mTarget = ImmutableList.copyOf(target);
39208db7e407109532557718c6b8064792f2df7a073dMindy Pereira            mFolderOps.addAll(folders);
392141e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal            mIsDestructive = isDestructive;
3922f3a455644f0899a01fff4782811fcff473f9aff0Mindy Pereira            mIsSelectedSet = isBatch;
392306642fab1bf4ab95b5dd97a65b262845cf60c865Mindy Pereira            mShowUndo = showUndo;
392401f30509c4c8ad0d13b7635447b7f023c7a0cab7Mindy Pereira            mAction = action;
39256a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy            mActionFolder = actionFolder;
392641e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal        }
392741e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal
392841e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal        @Override
392930c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        public void setUndoCallback(UndoCallback undoCallback) {
393030c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            mUndoCallback = undoCallback;
393130c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        }
393230c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao
393330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        @Override
393441e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal        public void performAction() {
39357f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal            if (isPerformed()) {
39367f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal                return;
39377f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal            }
393806642fab1bf4ab95b5dd97a65b262845cf60c865Mindy Pereira            if (mIsDestructive && mShowUndo) {
3939cb0b30ee1d5cfcc267bc7de1e6ad78ed766c1e50mindyp                ToastBarOperation undoOp = new ToastBarOperation(mTarget.size(), mAction,
39406a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                        ToastBarOperation.UNDO, mIsSelectedSet, mActionFolder);
394141e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal                onUndoAvailable(undoOp);
394241e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal            }
39438db7e407109532557718c6b8064792f2df7a073dMindy Pereira            // For each conversation, for each operation, add/ remove the
39448db7e407109532557718c6b8064792f2df7a073dMindy Pereira            // appropriate folders.
3945cb0b30ee1d5cfcc267bc7de1e6ad78ed766c1e50mindyp            ArrayList<ConversationOperation> ops = new ArrayList<ConversationOperation>();
3946cb0b30ee1d5cfcc267bc7de1e6ad78ed766c1e50mindyp            ArrayList<Uri> folderUris;
3947cb0b30ee1d5cfcc267bc7de1e6ad78ed766c1e50mindyp            ArrayList<Boolean> adds;
39488db7e407109532557718c6b8064792f2df7a073dMindy Pereira            for (Conversation target : mTarget) {
3949cb0b30ee1d5cfcc267bc7de1e6ad78ed766c1e50mindyp                HashMap<Uri, Folder> targetFolders = Folder.hashMapForFolders(target
3950cb0b30ee1d5cfcc267bc7de1e6ad78ed766c1e50mindyp                        .getRawFolders());
3951cb0b30ee1d5cfcc267bc7de1e6ad78ed766c1e50mindyp                folderUris = new ArrayList<Uri>();
3952cb0b30ee1d5cfcc267bc7de1e6ad78ed766c1e50mindyp                adds = new ArrayList<Boolean>();
395301f30509c4c8ad0d13b7635447b7f023c7a0cab7Mindy Pereira                if (mIsDestructive) {
395401f30509c4c8ad0d13b7635447b7f023c7a0cab7Mindy Pereira                    target.localDeleteOnUpdate = true;
395501f30509c4c8ad0d13b7635447b7f023c7a0cab7Mindy Pereira                }
39568db7e407109532557718c6b8064792f2df7a073dMindy Pereira                for (FolderOperation op : mFolderOps) {
3957259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy                    folderUris.add(op.mFolder.folderUri.fullUri);
3958cb0b30ee1d5cfcc267bc7de1e6ad78ed766c1e50mindyp                    adds.add(op.mAdd ? Boolean.TRUE : Boolean.FALSE);
39598db7e407109532557718c6b8064792f2df7a073dMindy Pereira                    if (op.mAdd) {
3960259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy                        targetFolders.put(op.mFolder.folderUri.fullUri, op.mFolder);
39618db7e407109532557718c6b8064792f2df7a073dMindy Pereira                    } else {
3962259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy                        targetFolders.remove(op.mFolder.folderUri.fullUri);
39638db7e407109532557718c6b8064792f2df7a073dMindy Pereira                    }
39648db7e407109532557718c6b8064792f2df7a073dMindy Pereira                }
396526746eb4acb159f3ce0d411c10d85a1de3e958a0Paul Westbrook                ops.add(mConversationListCursor.getConversationFolderOperation(target,
396630c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                        folderUris, adds, targetFolders.values(), mUndoCallback));
3967389f0b21a9b21bc3607b810c88272d06d48d0fe6mindyp            }
3968389f0b21a9b21bc3607b810c88272d06d48d0fe6mindyp            if (mConversationListCursor != null) {
39699e2d407fdafeb874e640eb84017feaf784309075Scott Kennedy                mConversationListCursor.updateBulkValues(ops);
39708db7e407109532557718c6b8064792f2df7a073dMindy Pereira            }
39717f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal            refreshConversationList();
3972f3a455644f0899a01fff4782811fcff473f9aff0Mindy Pereira            if (mIsSelectedSet) {
3973f3a455644f0899a01fff4782811fcff473f9aff0Mindy Pereira                mSelectedSet.clear();
3974f3a455644f0899a01fff4782811fcff473f9aff0Mindy Pereira            }
39757f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal        }
39768db7e407109532557718c6b8064792f2df7a073dMindy Pereira
39777f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal        /**
39787f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal         * Returns true if this action has been performed, false otherwise.
3979839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang         *
39807f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal         */
39817f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal        private synchronized boolean isPerformed() {
39827f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal            if (mCompleted) {
39837f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal                return true;
398441e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal            }
39857f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal            mCompleted = true;
39867f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal            return false;
398741e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal        }
398841e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal    }
39897f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal
3990c84759cc974256b6af5545c4bd993b81a0a98f6fmindyp    public final DestructiveAction getFolderChange(Collection<Conversation> target,
3991c84759cc974256b6af5545c4bd993b81a0a98f6fmindyp            Collection<FolderOperation> folders, boolean isDestructive, boolean isBatch,
399230c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            boolean showUndo, final boolean isMoveTo, final Folder actionFolder,
399330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            UndoCallback undoCallback) {
3994c84759cc974256b6af5545c4bd993b81a0a98f6fmindyp        final DestructiveAction da = getDeferredFolderChange(target, folders, isDestructive,
399530c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                isBatch, showUndo, isMoveTo, actionFolder, undoCallback);
3996c84759cc974256b6af5545c4bd993b81a0a98f6fmindyp        registerDestructiveAction(da);
3997c84759cc974256b6af5545c4bd993b81a0a98f6fmindyp        return da;
3998c84759cc974256b6af5545c4bd993b81a0a98f6fmindyp    }
3999c84759cc974256b6af5545c4bd993b81a0a98f6fmindyp
4000c84759cc974256b6af5545c4bd993b81a0a98f6fmindyp    public final DestructiveAction getDeferredFolderChange(Collection<Conversation> target,
40018db7e407109532557718c6b8064792f2df7a073dMindy Pereira            Collection<FolderOperation> folders, boolean isDestructive, boolean isBatch,
400230c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            boolean showUndo, final boolean isMoveTo, final Folder actionFolder,
400330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            UndoCallback undoCallback) {
400430c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        final DestructiveAction fd = new FolderDestruction(target, folders, isDestructive, isBatch,
400530c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                showUndo, isMoveTo ? R.id.move_folder : R.id.change_folders, actionFolder);
400630c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        fd.setUndoCallback(undoCallback);
400730c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        return fd;
400801f30509c4c8ad0d13b7635447b7f023c7a0cab7Mindy Pereira    }
400901f30509c4c8ad0d13b7635447b7f023c7a0cab7Mindy Pereira
401001f30509c4c8ad0d13b7635447b7f023c7a0cab7Mindy Pereira    @Override
401101f30509c4c8ad0d13b7635447b7f023c7a0cab7Mindy Pereira    public final DestructiveAction getDeferredRemoveFolder(Collection<Conversation> target,
401201f30509c4c8ad0d13b7635447b7f023c7a0cab7Mindy Pereira            Folder toRemove, boolean isDestructive, boolean isBatch,
401330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            boolean showUndo, UndoCallback undoCallback) {
401401f30509c4c8ad0d13b7635447b7f023c7a0cab7Mindy Pereira        Collection<FolderOperation> folderOps = new ArrayList<FolderOperation>();
401501f30509c4c8ad0d13b7635447b7f023c7a0cab7Mindy Pereira        folderOps.add(new FolderOperation(toRemove, false));
401630c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        final DestructiveAction da = new FolderDestruction(target, folderOps, isDestructive, isBatch,
40176a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                showUndo, R.id.remove_folder, mFolder);
401830c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        da.setUndoCallback(undoCallback);
401930c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        return da;
402001f30509c4c8ad0d13b7635447b7f023c7a0cab7Mindy Pereira    }
402101f30509c4c8ad0d13b7635447b7f023c7a0cab7Mindy Pereira
40224f4782bcc53e13b30f1fd417beb1a6c04a54faedVikram Aggarwal    @Override
40234f4782bcc53e13b30f1fd417beb1a6c04a54faedVikram Aggarwal    public final void refreshConversationList() {
402475daee52f08c9bf66652b9bddf42cfbd1ca71987Vikram Aggarwal        final ConversationListFragment convList = getConversationListFragment();
402575daee52f08c9bf66652b9bddf42cfbd1ca71987Vikram Aggarwal        if (convList == null) {
402675daee52f08c9bf66652b9bddf42cfbd1ca71987Vikram Aggarwal            return;
402775daee52f08c9bf66652b9bddf42cfbd1ca71987Vikram Aggarwal        }
402875daee52f08c9bf66652b9bddf42cfbd1ca71987Vikram Aggarwal        convList.requestListRefresh();
402975daee52f08c9bf66652b9bddf42cfbd1ca71987Vikram Aggarwal    }
4030c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein
4031c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein    protected final ActionClickedListener getUndoClickedListener(
4032c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein            final AnimatedAdapter listAdapter) {
4033c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein        return new ActionClickedListener() {
4034c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein            @Override
4035f8ccdcfc26f362d9c20771e2295a0c0e8d1195f3Andrew Sapperstein            public void onActionClicked(Context context) {
4036c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                if (mAccount.undoUri != null) {
4037c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                    // NOTE: We might want undo to return the messages affected, in which case
4038c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                    // the resulting cursor might be interesting...
4039c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                    // TODO: Use UIProvider.SEQUENCE_QUERY_PARAMETER to indicate the set of
4040c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                    // commands to undo
4041c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                    if (mConversationListCursor != null) {
4042c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                        mConversationListCursor.undo(
4043c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                                mActivity.getActivityContext(), mAccount.undoUri);
4044c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                    }
4045c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                    if (listAdapter != null) {
4046c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                        listAdapter.setUndo(true);
4047c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                    }
4048c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                }
4049c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein            }
4050c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein        };
4051c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein    }
4052c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein
405341b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal    /**
405441b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal     * Shows an error toast in the bottom when a folder was not fetched successfully.
405541b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal     * @param folder the folder which could not be fetched.
405641b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal     * @param replaceVisibleToast if true, this should replace any currently visible toast.
405741b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal     */
40589d7519d18e8ddbcdd59b37eec7c85210fca0e8c2Andrew Sapperstein    protected final void showErrorToast(final Folder folder, boolean replaceVisibleToast) {
405900179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein
406041b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal        final ActionClickedListener listener;
406141b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal        final int actionTextResourceId;
406200179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein        final int lastSyncResult = folder.lastSyncResult;
406341b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal        switch (lastSyncResult & 0x0f) {
406400179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein            case UIProvider.LastSyncResult.CONNECTION_ERROR:
406541b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal                // The sync request that caused this failure.
406641b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal                final int syncRequest = lastSyncResult >> 4;
406741b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal                // Show: User explicitly pressed the refresh button and there is no connection
406841b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal                // Show: The first time the user enters the app and there is no connection
406941b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal                //       TODO(viki): Implement this.
407041b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal                // Reference: http://b/7202801
407141b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal                final boolean showToast = (syncRequest & UIProvider.SyncStatus.USER_REFRESH) != 0;
407241b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal                // Don't show: Already in the app; user switches to a synced label
407341b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal                // Don't show: In a live label and a background sync fails
407441b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal                final boolean avoidToast = !showToast && (folder.syncWindow > 0
407541b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal                        || (syncRequest & UIProvider.SyncStatus.BACKGROUND_SYNC) != 0);
407641b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal                if (avoidToast) {
407741b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal                    return;
407841b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal                }
407900179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                listener = getRetryClickedListener(folder);
408000179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                actionTextResourceId = R.string.retry;
408100179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                break;
408200179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein            case UIProvider.LastSyncResult.AUTH_ERROR:
408300179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                listener = getSignInClickedListener();
408400179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                actionTextResourceId = R.string.signin;
408500179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                break;
408600179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein            case UIProvider.LastSyncResult.SECURITY_ERROR:
408700179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                return; // Currently we do nothing for security errors.
408800179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein            case UIProvider.LastSyncResult.STORAGE_ERROR:
408900179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                listener = getStorageErrorClickedListener();
409000179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                actionTextResourceId = R.string.info;
409100179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                break;
409200179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein            case UIProvider.LastSyncResult.INTERNAL_ERROR:
409300179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                listener = getInternalErrorClickedListener();
409400179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                actionTextResourceId = R.string.report;
409500179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                break;
409600179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein            default:
409700179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                return;
409800179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein        }
409941b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal        mToastBar.show(listener,
410041b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal                Utils.getSyncStatusText(mActivity.getActivityContext(), lastSyncResult),
410100179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                actionTextResourceId,
41029d7519d18e8ddbcdd59b37eec7c85210fca0e8c2Andrew Sapperstein                replaceVisibleToast,
41036a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                new ToastBarOperation(1, 0, ToastBarOperation.ERROR, false, folder));
4104c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein    }
4105c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein
410600179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein    private ActionClickedListener getRetryClickedListener(final Folder folder) {
4107c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein        return new ActionClickedListener() {
4108c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein            @Override
4109f8ccdcfc26f362d9c20771e2295a0c0e8d1195f3Andrew Sapperstein            public void onActionClicked(Context context) {
4110c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                final Uri uri = folder.refreshUri;
4111c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein
4112c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                if (uri != null) {
41134969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook                    startAsyncRefreshTask(uri);
4114c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                }
4115c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein            }
4116c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein        };
4117c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein    }
411800179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein
411900179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein    private ActionClickedListener getSignInClickedListener() {
412000179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein        return new ActionClickedListener() {
412100179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein            @Override
4122f8ccdcfc26f362d9c20771e2295a0c0e8d1195f3Andrew Sapperstein            public void onActionClicked(Context context) {
4123122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                promptUserForAuthentication(mAccount);
412400179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein            }
412500179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein        };
412600179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein    }
412700179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein
412800179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein    private ActionClickedListener getStorageErrorClickedListener() {
412900179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein        return new ActionClickedListener() {
413000179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein            @Override
4131f8ccdcfc26f362d9c20771e2295a0c0e8d1195f3Andrew Sapperstein            public void onActionClicked(Context context) {
41324969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook                showStorageErrorDialog();
413300179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein            }
413400179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein        };
413500179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein    }
413600179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein
41374969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook    private void showStorageErrorDialog() {
41384969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook        DialogFragment fragment = (DialogFragment)
41394969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook                mFragmentManager.findFragmentByTag(SYNC_ERROR_DIALOG_FRAGMENT_TAG);
41404969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook        if (fragment == null) {
41414969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook            fragment = SyncErrorDialogFragment.newInstance();
41424969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook        }
41434969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook        fragment.show(mFragmentManager, SYNC_ERROR_DIALOG_FRAGMENT_TAG);
41444969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook    }
41454969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook
414600179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein    private ActionClickedListener getInternalErrorClickedListener() {
414700179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein        return new ActionClickedListener() {
414800179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein            @Override
4149f8ccdcfc26f362d9c20771e2295a0c0e8d1195f3Andrew Sapperstein            public void onActionClicked(Context context) {
415083e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook                Utils.sendFeedback(mActivity, mAccount, true /* reportingProblem */);
415100179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein            }
415200179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein        };
415300179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein    }
41544969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook
41554969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook    @Override
41564969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook    public void onFooterViewErrorActionClick(Folder folder, int errorStatus) {
41574969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook        Uri uri = null;
41584969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook        switch (errorStatus) {
41594969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook            case UIProvider.LastSyncResult.CONNECTION_ERROR:
41604969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook                if (folder != null && folder.refreshUri != null) {
41614969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook                    uri = folder.refreshUri;
41624969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook                }
41634969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook                break;
41644969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook            case UIProvider.LastSyncResult.AUTH_ERROR:
4165122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                promptUserForAuthentication(mAccount);
41664969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook                return;
41674969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook            case UIProvider.LastSyncResult.SECURITY_ERROR:
41684969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook                return; // Currently we do nothing for security errors.
41694969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook            case UIProvider.LastSyncResult.STORAGE_ERROR:
41704969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook                showStorageErrorDialog();
41714969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook                return;
41724969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook            case UIProvider.LastSyncResult.INTERNAL_ERROR:
417383e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook                Utils.sendFeedback(mActivity, mAccount, true /* reportingProblem */);
41744969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook                return;
41754969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook            default:
41764969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook                return;
41774969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook        }
41784969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook
41794969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook        if (uri != null) {
41804969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook            startAsyncRefreshTask(uri);
41814969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook        }
41824969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook    }
41834969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook
41844969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook    @Override
41854969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook    public void onFooterViewLoadMoreClick(Folder folder) {
41864969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook        if (folder != null && folder.loadMoreUri != null) {
41874969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook            startAsyncRefreshTask(folder.loadMoreUri);
41884969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook        }
41894969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook    }
41904969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook
41914969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook    private void startAsyncRefreshTask(Uri uri) {
41924969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook        if (mFolderSyncTask != null) {
41934969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook            mFolderSyncTask.cancel(true);
41944969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook        }
41954969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook        mFolderSyncTask = new AsyncRefreshTask(mActivity.getActivityContext(), uri);
41964969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook        mFolderSyncTask.execute();
41974969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook    }
4198122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook
4199122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook    private void promptUserForAuthentication(Account account) {
4200429399b738f444911117978bf8229036e8814b1ePaul Westbrook        if (account != null && !Utils.isEmpty(account.reauthenticationIntentUri)) {
4201122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook            final Intent authenticationIntent =
4202122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                    new Intent(Intent.ACTION_VIEW, account.reauthenticationIntentUri);
4203122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook            mActivity.startActivityForResult(authenticationIntent, REAUTHENTICATE_REQUEST_CODE);
4204122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook        }
4205122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook    }
4206ca87de41285bde382a894b6cb2d13c112c5d7a2fmindyp
4207ca87de41285bde382a894b6cb2d13c112c5d7a2fmindyp    @Override
4208ca87de41285bde382a894b6cb2d13c112c5d7a2fmindyp    public void onAccessibilityStateChanged() {
4209ca87de41285bde382a894b6cb2d13c112c5d7a2fmindyp        // Clear the cache of objects.
4210ca87de41285bde382a894b6cb2d13c112c5d7a2fmindyp        ConversationItemViewModel.onAccessibilityUpdated();
4211ca87de41285bde382a894b6cb2d13c112c5d7a2fmindyp        // Re-render the list if it exists.
4212a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        final ConversationListFragment frag = getConversationListFragment();
4213ca87de41285bde382a894b6cb2d13c112c5d7a2fmindyp        if (frag != null) {
4214ca87de41285bde382a894b6cb2d13c112c5d7a2fmindyp            AnimatedAdapter adapter = frag.getAnimatedAdapter();
4215ca87de41285bde382a894b6cb2d13c112c5d7a2fmindyp            if (adapter != null) {
4216ca87de41285bde382a894b6cb2d13c112c5d7a2fmindyp                adapter.notifyDataSetInvalidated();
4217ca87de41285bde382a894b6cb2d13c112c5d7a2fmindyp            }
4218ca87de41285bde382a894b6cb2d13c112c5d7a2fmindyp        }
4219ca87de41285bde382a894b6cb2d13c112c5d7a2fmindyp    }
42206cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal
42216cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal    @Override
422230c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao    public void makeDialogListener (final int action, final boolean isBatch,
422330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            UndoCallback undoCallback) {
4224b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal        final Collection<Conversation> target;
4225b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal        if (isBatch) {
4226b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal            target = mSelectedSet.values();
4227b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal        } else {
4228b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal            LogUtils.d(LOG_TAG, "Will act upon %s", mCurrentConversation);
4229b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal            target = Conversation.listOf(mCurrentConversation);
4230b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal        }
423130c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        final DestructiveAction destructiveAction = getDeferredAction(action, target, isBatch,
423230c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                undoCallback);
42336cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal        mDialogAction = action;
4234b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal        mDialogFromSelectedSet = isBatch;
42356cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal        mDialogListener = new AlertDialog.OnClickListener() {
42366cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal            @Override
42376cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal            public void onClick(DialogInterface dialog, int which) {
4238caaeed3837316175111bb16e6b60aeb0d523debfScott Kennedy                delete(action, target, destructiveAction, isBatch);
42396cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal                // Afterwards, let's remove references to the listener and the action.
42406cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal                setListener(null, -1);
42416cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal            }
42426cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal        };
42436cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal    }
42446cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal
42456cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal    @Override
42466cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal    public AlertDialog.OnClickListener getListener() {
42476cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal        return mDialogListener;
42486cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal    }
42496cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal
42506cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal    /**
42516cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal     * Sets the listener for the positive action on a confirmation dialog.  Since only a single
42526cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal     * confirmation dialog can be shown, this overwrites the previous listener.  It is safe to
42536cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal     * unset the listener; in which case action should be set to -1.
4254715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @param listener the listener that will perform the task for this dialog's positive action.
4255715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @param action the action that created this dialog.
42566cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal     */
42576cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal    private void setListener(AlertDialog.OnClickListener listener, final int action){
42586cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal        mDialogListener = listener;
42596cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal        mDialogAction = action;
42606cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal    }
426169a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal
426269a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    @Override
426369a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    public VeiledAddressMatcher getVeiledAddressMatcher() {
426469a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal        return mVeiledMatcher;
4265a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal    }
4266a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal
4267a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal    @Override
4268a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal    public void setDetachedMode() {
4269a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        // Tell the conversation list not to select anything.
4270a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        final ConversationListFragment frag = getConversationListFragment();
4271a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        if (frag != null) {
4272a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal            frag.setChoiceNone();
427381230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal        } else if (mIsTablet) {
427481230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal            // How did we ever land here? Detached mode, and no CLF on tablet???
4275a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal            LogUtils.e(LOG_TAG, "AAC.setDetachedMode(): CLF = null!");
4276a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        }
4277a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        mDetachedConvUri = mCurrentConversation.uri;
4278a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal    }
4279a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal
4280a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal    private void clearDetachedMode() {
4281a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        // Tell the conversation list to go back to its usual selection behavior.
4282a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        final ConversationListFragment frag = getConversationListFragment();
4283a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        if (frag != null) {
4284a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal            frag.revertChoiceMode();
428581230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal        } else if (mIsTablet) {
428681230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal            // How did we ever land here? Detached mode, and no CLF on tablet???
428781230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal            LogUtils.e(LOG_TAG, "AAC.clearDetachedMode(): CLF = null on tablet!");
4288a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        }
4289a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        mDetachedConvUri = null;
4290a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal    }
429112b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang
429261f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang    @Override
429361f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang    public DrawerController getDrawerController() {
429461f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang        return mDrawerListener;
429561f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang    }
429661f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang
429761f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang    private class MailDrawerListener extends Observable<DrawerLayout.DrawerListener>
429861f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang            implements DrawerLayout.DrawerListener, DrawerController {
4299a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein        private int mDrawerState;
4300fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein        private float mOldSlideOffset;
4301a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein
4302a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein        public MailDrawerListener() {
4303a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein            mDrawerState = DrawerLayout.STATE_IDLE;
4304fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            mOldSlideOffset = 0.f;
4305a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein        }
4306a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein
430712b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        @Override
43083e95fe9af0d1f8d7557d6b8e9daf4cf08aade140Andy Huang        public boolean isDrawerEnabled() {
43093e95fe9af0d1f8d7557d6b8e9daf4cf08aade140Andy Huang            return AbstractActivityController.this.isDrawerEnabled();
43103e95fe9af0d1f8d7557d6b8e9daf4cf08aade140Andy Huang        }
43113e95fe9af0d1f8d7557d6b8e9daf4cf08aade140Andy Huang
43123e95fe9af0d1f8d7557d6b8e9daf4cf08aade140Andy Huang        @Override
431361f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang        public void registerDrawerListener(DrawerLayout.DrawerListener l) {
431461f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang            registerObserver(l);
431561f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang        }
431661f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang
431761f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang        @Override
431861f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang        public void unregisterDrawerListener(DrawerLayout.DrawerListener l) {
431961f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang            unregisterObserver(l);
432061f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang        }
432161f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang
432261f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang        @Override
432361f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang        public boolean isDrawerOpen() {
432461f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang            return isDrawerEnabled() && mDrawerContainer.isDrawerOpen(mDrawerPullout);
432561f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang        }
432661f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang
432761f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang        @Override
432861f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang        public boolean isDrawerVisible() {
432961f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang            return isDrawerEnabled() && mDrawerContainer.isDrawerVisible(mDrawerPullout);
433061f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang        }
433161f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang
433261f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang        @Override
4333f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang        public void toggleDrawerState() {
4334f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang            AbstractActivityController.this.toggleDrawerState();
4335f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang        }
4336f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang
4337f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang        @Override
433812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        public void onDrawerOpened(View drawerView) {
433912b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            mDrawerToggle.onDrawerOpened(drawerView);
434061f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang
434161f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang            for (DrawerLayout.DrawerListener l : mObservers) {
434261f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang                l.onDrawerOpened(drawerView);
434361f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang            }
434412b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
434512b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang
434612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        @Override
434712b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        public void onDrawerClosed(View drawerView) {
434812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            mDrawerToggle.onDrawerClosed(drawerView);
434912b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            if (mHasNewAccountOrFolder) {
435012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang                refreshDrawer();
435112b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            }
435210c99efe431335ce8db9924c3dcb0f977f3c63f3Scott Kennedy
435310c99efe431335ce8db9924c3dcb0f977f3c63f3Scott Kennedy            // When closed, we want to use either the burger, or up, based on where we are
435410c99efe431335ce8db9924c3dcb0f977f3c63f3Scott Kennedy            final int mode = mViewMode.getMode();
43559695e0046f79bd2d7166a411d6feff4cf0fb2539Jin Cao            final boolean isTopLevel = Folder.isRoot(mFolder);
435610c99efe431335ce8db9924c3dcb0f977f3c63f3Scott Kennedy            mDrawerToggle.setDrawerIndicatorEnabled(getShouldShowDrawerIndicator(mode, isTopLevel));
435761f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang
435861f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang            for (DrawerLayout.DrawerListener l : mObservers) {
435961f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang                l.onDrawerClosed(drawerView);
436061f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang            }
436112b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
436212b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang
436312b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        /**
436412b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         * As part of the overriden function, it will animate the alpha of the conversation list
436512b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         * view along with the drawer sliding when we're in the process of switching accounts or
436612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         * folders. Note, this is the same amount of work done as {@link ValueAnimator#ofFloat}.
436712b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         */
436812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        @Override
436912b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        public void onDrawerSlide(View drawerView, float slideOffset) {
437012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            mDrawerToggle.onDrawerSlide(drawerView, slideOffset);
437112b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            if (mHasNewAccountOrFolder && mListViewForAnimating != null) {
437212b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang                mListViewForAnimating.setAlpha(slideOffset);
437312b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            }
4374fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein
4375fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            // This code handles when to change the visibility of action items
4376fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            // based on drawer state. The basic logic is that right when we
4377fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            // open the drawer, we hide the action items. We show the action items
4378fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            // when the drawer closes. However, due to the animation of the drawer closing,
4379fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            // to make the reshowing of the action items feel right, we make the items visible
4380fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            // slightly sooner.
4381fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            //
4382fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            // However, to make the animating behavior work properly, we have to know whether
4383fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            // we're animating open or closed. Only if we're animating closed do we want to
4384fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            // show the action items early. We save the last slide offset so that we can compare
4385fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            // the current slide offset to it to determine if we're opening or closing.
4386fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            if (mDrawerState == DrawerLayout.STATE_SETTLING) {
4387fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein                if (mHideMenuItems && slideOffset < 0.15f && mOldSlideOffset > slideOffset) {
4388fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein                    mHideMenuItems = false;
438952882ff08fe9420fe2dbb3f681545a330222e9c8Andrew Sapperstein                    mActivity.supportInvalidateOptionsMenu();
439043fab3224230c235db5be395f96096e1cac6b376Tony Mantler                    maybeEnableCabMode();
4391fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein                } else if (!mHideMenuItems && slideOffset > 0.f && mOldSlideOffset < slideOffset) {
4392fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein                    mHideMenuItems = true;
439352882ff08fe9420fe2dbb3f681545a330222e9c8Andrew Sapperstein                    mActivity.supportInvalidateOptionsMenu();
439443fab3224230c235db5be395f96096e1cac6b376Tony Mantler                    disableCabMode();
4395fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein                }
4396fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            } else {
4397fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein                if (mHideMenuItems && Float.compare(slideOffset, 0.f) == 0) {
4398fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein                    mHideMenuItems = false;
439952882ff08fe9420fe2dbb3f681545a330222e9c8Andrew Sapperstein                    mActivity.supportInvalidateOptionsMenu();
440043fab3224230c235db5be395f96096e1cac6b376Tony Mantler                    maybeEnableCabMode();
4401fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein                } else if (!mHideMenuItems && slideOffset > 0.f) {
4402fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein                    mHideMenuItems = true;
440352882ff08fe9420fe2dbb3f681545a330222e9c8Andrew Sapperstein                    mActivity.supportInvalidateOptionsMenu();
440443fab3224230c235db5be395f96096e1cac6b376Tony Mantler                    disableCabMode();
4405fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein                }
4406fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            }
4407fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein
4408fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            mOldSlideOffset = slideOffset;
440910c99efe431335ce8db9924c3dcb0f977f3c63f3Scott Kennedy
441010c99efe431335ce8db9924c3dcb0f977f3c63f3Scott Kennedy            // If we're sliding, we always want to show the burger
441110c99efe431335ce8db9924c3dcb0f977f3c63f3Scott Kennedy            mDrawerToggle.setDrawerIndicatorEnabled(true /* enable */);
441261f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang
441361f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang            for (DrawerLayout.DrawerListener l : mObservers) {
441461f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang                l.onDrawerSlide(drawerView, slideOffset);
441561f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang            }
441612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
441712b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang
441812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        /**
441912b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         * This condition here should only be called when the drawer is stuck in a weird state
442012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         * and doesn't register the onDrawerClosed, but shows up as idle. Make sure to refresh
442112b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         * and, more importantly, unlock the drawer when this is the case.
442212b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         */
442312b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        @Override
442412b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        public void onDrawerStateChanged(int newState) {
4425371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon            LogUtils.d(LOG_TAG, "AAC onDrawerStateChanged %d", newState);
4426a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein            mDrawerState = newState;
4427a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein            mDrawerToggle.onDrawerStateChanged(mDrawerState);
442861f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang
442961f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang            for (DrawerLayout.DrawerListener l : mObservers) {
443061f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang                l.onDrawerStateChanged(newState);
443161f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang            }
443261f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang
4433e764cfd130c4f92ae823fd1f361ab70439e8ec95Andy Huang            if (mViewMode.isSearchMode()) {
4434371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                return;
4435371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon            }
4436a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein            if (mDrawerState == DrawerLayout.STATE_IDLE) {
4437a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein                if (mHasNewAccountOrFolder) {
4438a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein                    refreshDrawer();
4439a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein                }
4440a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein                if (mConversationListLoadFinishedIgnored) {
4441a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein                    mConversationListLoadFinishedIgnored = false;
4442a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein                    final Bundle args = new Bundle();
4443a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein                    args.putParcelable(BUNDLE_ACCOUNT_KEY, mAccount);
4444a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein                    args.putParcelable(BUNDLE_FOLDER_KEY, mFolder);
4445a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein                    mActivity.getLoaderManager().initLoader(
4446a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein                            LOADER_CONVERSATION_LIST, args, mListCursorCallbacks);
4447a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein                }
444812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            }
444912b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
445012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang
445112b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        /**
445212b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         * If we've reached a stable drawer state, unlock the drawer for usage, clear the
445312b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         * conversation list, and finish end actions. Also, make
445412b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         * {@link #mHasNewAccountOrFolder} false to reflect we're done changing.
445512b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         */
445612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        public void refreshDrawer() {
445712b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            mHasNewAccountOrFolder = false;
445812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            mDrawerContainer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
445912b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            ConversationListFragment conversationList = getConversationListFragment();
446012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            if (conversationList != null) {
446112b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang                conversationList.clear();
446212b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            }
446354022ee4c98f02401ca567e643f24ed42aebe203Tony Mantler            mFolderOrAccountObservers.notifyChanged();
446412b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
4465a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein
4466a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein        /**
4467a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein         * Returns the most recent update of the {@link DrawerLayout}'s state provided
4468a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein         * by {@link #onDrawerStateChanged(int)}.
4469a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein         * @return The {@link DrawerLayout}'s current state. One of
4470a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein         * {@link DrawerLayout#STATE_DRAGGING}, {@link DrawerLayout#STATE_IDLE},
4471a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein         * or {@link DrawerLayout#STATE_SETTLING}.
4472a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein         */
4473a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein        public int getDrawerState() {
4474a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein            return mDrawerState;
4475a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein        }
447612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang    }
447712b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang
44788a72b8590d9166ada55f69eeb083d2b8c5c915c9Scott Kennedy    @Override
44798a72b8590d9166ada55f69eeb083d2b8c5c915c9Scott Kennedy    public boolean isDrawerPullEnabled() {
4480371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon        return true;
44818a72b8590d9166ada55f69eeb083d2b8c5c915c9Scott Kennedy    }
44825747e152fa606c20261b18431f4a9f0ff3be7b5aAndrew Sapperstein
44835747e152fa606c20261b18431f4a9f0ff3be7b5aAndrew Sapperstein    @Override
44845747e152fa606c20261b18431f4a9f0ff3be7b5aAndrew Sapperstein    public boolean shouldHideMenuItems() {
44855747e152fa606c20261b18431f4a9f0ff3be7b5aAndrew Sapperstein        return mHideMenuItems;
44865747e152fa606c20261b18431f4a9f0ff3be7b5aAndrew Sapperstein    }
4487ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang
4488ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang    protected void navigateUpFolderHierarchy() {
4489ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang        new AsyncTask<Void, Void, Folder>() {
4490ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang            @Override
4491ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang            protected Folder doInBackground(final Void... params) {
4492ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                if (mInbox == null) {
4493ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                    // We don't have an inbox, but we need it
4494ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                    final Cursor cursor = mContext.getContentResolver().query(
4495ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                            mAccount.settings.defaultInbox, UIProvider.FOLDERS_PROJECTION, null,
4496ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                            null, null);
4497ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang
4498ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                    if (cursor != null) {
4499ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                        try {
4500ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                            if (cursor.moveToFirst()) {
4501ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                                mInbox = new Folder(cursor);
4502ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                            }
4503ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                        } finally {
4504ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                            cursor.close();
4505ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                        }
4506ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                    }
4507ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                }
4508ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang
4509ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                // Now try to load our parent
4510ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                final Folder folder;
4511ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang
45125ac10036fd45cd2a6da32abd02e942617edec50dAlice Yang                if (mFolder != null) {
4513e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon                    Cursor cursor = null;
4514e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon                    try {
4515e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon                        cursor = mContext.getContentResolver().query(mFolder.parent,
4516e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon                                UIProvider.FOLDERS_PROJECTION, null, null, null);
4517e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon
4518e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon                        if (cursor == null || !cursor.moveToFirst()) {
4519e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon                            // We couldn't load the parent, so use the inbox
4520e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon                            folder = mInbox;
4521e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon                        } else {
45225ac10036fd45cd2a6da32abd02e942617edec50dAlice Yang                            folder = new Folder(cursor);
4523e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon                        }
4524e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon                    } finally {
4525e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon                        if (cursor != null) {
45265ac10036fd45cd2a6da32abd02e942617edec50dAlice Yang                            cursor.close();
45275ac10036fd45cd2a6da32abd02e942617edec50dAlice Yang                        }
4528ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                    }
45295ac10036fd45cd2a6da32abd02e942617edec50dAlice Yang                } else {
45305ac10036fd45cd2a6da32abd02e942617edec50dAlice Yang                    folder = mInbox;
4531ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                }
4532ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang
4533ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                return folder;
4534ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang            }
4535ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang
4536ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang            @Override
4537ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang            protected void onPostExecute(final Folder result) {
4538ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                onFolderSelected(result);
4539ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang            }
4540ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
4541ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang    }
4542f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy
4543f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy    @Override
4544f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy    public Parcelable getConversationListScrollPosition(final String folderUri) {
4545f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy        return mConversationListScrollPositions.getParcelable(folderUri);
4546f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy    }
4547f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy
4548f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy    @Override
4549f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy    public void setConversationListScrollPosition(final String folderUri,
4550f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy            final Parcelable savedPosition) {
4551f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy        mConversationListScrollPositions.putParcelable(folderUri, savedPosition);
4552f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy    }
455353de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein
455453de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein    @Override
455553de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein    public View.OnClickListener getNavigationViewClickListener() {
455653de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein        return mHomeButtonListener;
455753de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein    }
455853de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein
455953de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein    // TODO: Fold this into the outer class when b/16627877 is fixed
456053de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein    private class HomeButtonListener implements View.OnClickListener {
456153de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein
456253de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein        @Override
456353de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein        public void onClick(View v) {
456453de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein            onUpPressed();
456553de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein        }
456653de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein    }
45674a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal}
4568