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;
49c6801eb828627c37b8992584767c095dfe11df62Jin Caoimport android.speech.RecognizerIntent;
5012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huangimport android.support.v4.widget.DrawerLayout;
5152882ff08fe9420fe2dbb3f681545a330222e9c8Andrew Sappersteinimport android.support.v7.app.ActionBar;
52355dd90d120bc105f5ad37aa5a5c7449e665a566Andrew Sappersteinimport android.support.v7.app.ActionBarDrawerToggle;
5312b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huangimport android.view.Gravity;
54a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwalimport android.view.KeyEvent;
55a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwalimport android.view.Menu;
5628d5f72e0ad0db75b6243ee8125bfe3aadbdcb85Mindy Pereiraimport android.view.MenuInflater;
57a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwalimport android.view.MenuItem;
58a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwalimport android.view.MotionEvent;
59d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereiraimport android.view.View;
6012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huangimport android.widget.ListView;
61fd0c2974af56ad244006faab2651e054974a4b7bMindy Pereiraimport android.widget.Toast;
62a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal
63b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwalimport com.android.mail.ConversationListContext;
6459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwalimport com.android.mail.MailLogService;
65f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huangimport com.android.mail.R;
66761522c1ac7a8fa05acde714f0e26fde47f3415fAndy Huangimport com.android.mail.analytics.Analytics;
67779dd6012a6c5b4d71255085059e57d94eab1a3fJin Caoimport com.android.mail.analytics.AnalyticsTimer;
686cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwalimport com.android.mail.browse.ConfirmDialogFragment;
69967ede63e059810d9d4d425c50111d606014c893Mindy Pereiraimport com.android.mail.browse.ConversationCursor;
707c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Huimport com.android.mail.browse.ConversationCursor.ConversationOperation;
71ca87de41285bde382a894b6cb2d13c112c5d7a2fmindypimport com.android.mail.browse.ConversationItemViewModel;
728812d3c50e35c4f2a02d29c35c76082c4ebec0cdAndrew Sappersteinimport com.android.mail.browse.ConversationMessage;
7387a8982af83168453b6dc219fe984bff68047bebAndy Huangimport com.android.mail.browse.ConversationPagerAdapter;
74bf232c3735f65b1a4746943e4a134e59e36f0bdePaul Westbrookimport com.android.mail.browse.ConversationPagerController;
757c9f6acdf2b992fecaf0b3ad0afe9ec30ec86f33Marc Blankimport com.android.mail.browse.SelectedConversationsActionMenu;
76991f453c8905ec683458f8b4ea0e2737832425d9Andy Huangimport com.android.mail.browse.SyncErrorDialogFragment;
7730c881a7d31203fc7b233abf4a0c1413de4cd517Jin Caoimport com.android.mail.browse.UndoCallback;
789b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereiraimport com.android.mail.compose.ComposeActivity;
79177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwalimport com.android.mail.content.CursorCreator;
80177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwalimport com.android.mail.content.ObjectCursor;
81177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwalimport com.android.mail.content.ObjectCursorLoader;
821ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwalimport com.android.mail.providers.Account;
839b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereiraimport com.android.mail.providers.Conversation;
84839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huangimport com.android.mail.providers.ConversationInfo;
854a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwalimport com.android.mail.providers.Folder;
867a5d95a08464fb82b0310c82f3c39bfb8b63539fVikram Aggarwalimport com.android.mail.providers.FolderWatcher;
87c2074c4442eacf5604dd3cecd06b363e8fc574dePaul Westbrookimport com.android.mail.providers.MailAppProvider;
88dac00547ce8d44a964e4510315168ee413e55a75Mindy Pereiraimport com.android.mail.providers.Settings;
8980aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwalimport com.android.mail.providers.UIProvider;
903cb28b5e1598f74dedbdb87b187d9bd7e132ff0fMindy Pereiraimport com.android.mail.providers.UIProvider.AccountCapabilities;
912388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrookimport com.android.mail.providers.UIProvider.AccountCursorExtraKeys;
920d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedyimport com.android.mail.providers.UIProvider.AutoAdvance;
93c9d59184da271d5a6974edb709e3b39a5a970fa7Mindy Pereiraimport com.android.mail.providers.UIProvider.ConversationColumns;
945109c516b5cac279b4f99271953a27dd9a4828d8Paul Westbrookimport com.android.mail.providers.UIProvider.ConversationOperations;
9554452ae7a9cdb607ed1db5e74077b5ebd95c46a2Vikram Aggarwalimport com.android.mail.providers.UIProvider.FolderCapabilities;
96a158ac8a41239300a2f50b38ed17167aeef4ff4bScott Kennedyimport com.android.mail.providers.UIProvider.FolderType;
97c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sappersteinimport com.android.mail.ui.ActionableToastBar.ActionClickedListener;
98839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huangimport com.android.mail.utils.ContentProviderTask;
99144bfe739b93afdee0a1700a34806b0b287e5887Andy Huangimport com.android.mail.utils.DrawIdler;
100b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrookimport com.android.mail.utils.LogTag;
1015cb0c3e684150d6d8ed4d9df4fb73a7606086aaaMindy Pereiraimport com.android.mail.utils.LogUtils;
10261f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huangimport com.android.mail.utils.MailObservable;
103cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedyimport com.android.mail.utils.NotificationActionUtils;
104fa131a2ff399fd1d544f759b063268fb4e8a3e70Vikram Aggarwalimport com.android.mail.utils.Utils;
10569a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwalimport com.android.mail.utils.VeiledAddressMatcher;
106ca08fc170ca3f04572af8999645d5fd9e0bdb725Paul Westbrookimport com.google.common.base.Objects;
10777eee625cd8c317c605acfd06cd3a7e22120a0fdPaul Westbrookimport com.google.common.collect.ImmutableList;
108acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereiraimport com.google.common.collect.Lists;
109167faa8584750e37781f22ad7e6d61003029f954Marc Blankimport com.google.common.collect.Sets;
1104a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal
111167faa8584750e37781f22ad7e6d61003029f954Marc Blankimport java.util.ArrayList;
1129e4ca7993213d296043d20fe9cf4e166b5d595e8Andy Huangimport java.util.Arrays;
113fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereiraimport java.util.Collection;
114839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huangimport java.util.Collections;
1158db7e407109532557718c6b8064792f2df7a073dMindy Pereiraimport java.util.HashMap;
116cc754b102ed65df9d637b4e0a03e645214ea3cccVikram Aggarwalimport java.util.List;
11723b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrookimport java.util.Set;
118bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blankimport java.util.TimerTask;
11923b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook
12023b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook
1214a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal/**
122161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira * This is an abstract implementation of the Activity Controller. This class
123161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira * knows how to respond to menu items, state changes, layout changes, etc. It
124161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira * weaves together the views and listeners, dispatching actions to the
125161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira * respective underlying classes.
126161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira * <p>
127161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira * Even though this class is abstract, it should provide default implementations
128161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira * for most, if not all the methods in the ActivityController interface. This
129161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira * makes the task of the subclasses easier: OnePaneActivityController and
130161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira * TwoPaneActivityController can be concise when the common functionality is in
131161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira * AbstractActivityController.
132161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira * </p>
133a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal * <p>
134161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira * In the Gmail codebase, this was called BaseActivityController
135161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira * </p>
1364a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal */
137ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sappersteinpublic abstract class AbstractActivityController implements ActivityController,
1385d79a913c8289de4ffc1b662b9bea967a7e65b0dJames Lemieux        EmptyFolderDialogFragment.EmptyFolderDialogFragmentListener, View.OnClickListener {
1391ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    // Keys for serialization of various information in Bundles.
140cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal    /** Tag for {@link #mAccount} */
1416c51158ad3269f157424e6c7bd488425c98da08fVikram Aggarwal    private static final String SAVED_ACCOUNT = "saved-account";
142cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal    /** Tag for {@link #mFolder} */
1435e478d2c4087b50764756b0edc95941cb6094f68Mindy Pereira    private static final String SAVED_FOLDER = "saved-folder";
144cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal    /** Tag for {@link #mCurrentConversation} */
14526f23fc8cc0d49bc0bea1eeec4c0c4742928f693Mindy Pereira    private static final String SAVED_CONVERSATION = "saved-conversation";
146ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao    /** Tag for {@link #mCheckedSet} */
147cabd3f227bf97159774e246ad278bb49d4aa2badVikram Aggarwal    private static final String SAVED_SELECTED_SET = "saved-selected-set";
148649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal    /** Tag for {@link ActionableToastBar#getOperation()} */
149d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira    private static final String SAVED_TOAST_BAR_OP = "saved-toast-bar-op";
150649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal    /** Tag for {@link #mFolderListFolder} */
151649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal    private static final String SAVED_HIERARCHICAL_FOLDER = "saved-hierarchical-folder";
152f3341405c97ff6d27a0d5fa37f6031e1c395d161Vikram Aggarwal    /** Tag for {@link ConversationListContext#searchQuery} */
153f3341405c97ff6d27a0d5fa37f6031e1c395d161Vikram Aggarwal    private static final String SAVED_QUERY = "saved-query";
1546cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal    /** Tag for {@link #mDialogAction} */
1556cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal    private static final String SAVED_ACTION = "saved-action";
156b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal    /** Tag for {@link #mDialogFromSelectedSet} */
157b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal    private static final String SAVED_ACTION_FROM_SELECTED = "saved-action-from-selected";
158a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal    /** Tag for {@link #mDetachedConvUri} */
159a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal    private static final String SAVED_DETACHED_CONV_URI = "saved-detached-conv-uri";
160ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang    /** Key to store {@link #mInbox}. */
161f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy    private static final String SAVED_INBOX_KEY = "m-inbox";
162f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy    /** Key to store {@link #mConversationListScrollPositions} */
163f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy    private static final String SAVED_CONVERSATION_LIST_SCROLL_POSITIONS =
164f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy            "saved-conversation-list-scroll-positions";
1655cb0c3e684150d6d8ed4d9df4fb73a7606086aaaMindy Pereira
166ede2e52e725abc625ae963f979639ff8ecc40cf5Greg Bullock    /** Tag used when loading a wait fragment */
1676902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal    protected static final String TAG_WAIT = "wait-fragment";
1686902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal    /** Tag used when loading a conversation list fragment. */
169bf232c3735f65b1a4746943e4a134e59e36f0bdePaul Westbrook    public static final String TAG_CONVERSATION_LIST = "tag-conversation-list";
170103319aaed26bce257de55b2fe93d4f78d3c59b9Scott Kennedy    /** Tag used when loading a custom fragment. */
171103319aaed26bce257de55b2fe93d4f78d3c59b9Scott Kennedy    protected static final String TAG_CUSTOM_FRAGMENT = "tag-custom-fragment";
1726902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal
173a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal    /** Key to store an account in a bundle */
174a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal    private final String BUNDLE_ACCOUNT_KEY = "account";
175a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal    /** Key to store a folder in a bundle */
176a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal    private final String BUNDLE_FOLDER_KEY = "folder";
1775bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein    /**
1785bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein     * Key to set a flag for the ConversationCursorLoader to ignore any
1795bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein     * initial load limit that may be set by the Account. Instead,
1805bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein     * perform a full load instead of the full-stage load.
1815bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein     */
1825bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein    private final String BUNDLE_IGNORE_INITIAL_CONVERSATION_LIMIT_KEY =
1835bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein            "ignore-initial-conversation-limit";
184a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal
185d7a12cd5b5c2639b653d8c671b04844ef02ee01dVikram Aggarwal    protected Account mAccount;
18612a4d8040d64501aa5a24c25865c806331560b74Mindy Pereira    protected Folder mFolder;
187ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang    protected Folder mInbox;
18869b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal    /** True when {@link #mFolder} is first shown to the user. */
18969b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal    private boolean mFolderChanged = false;
1902d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein    protected ActionBarController mActionBarController;
19110fcd645f703799326da326630b663df69f91508James Lemieux    protected final MailActivity mActivity;
192a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    protected final Context mContext;
1936902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal    private final FragmentManager mFragmentManager;
194ec5cbf79b825c6f96d45e85015319c66470b7e57Vikram Aggarwal    protected final RecentFolderList mRecentFolderList;
19580aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwal    protected ConversationListContext mConvListContext;
1969b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira    protected Conversation mCurrentConversation;
197c6801eb828627c37b8992584767c095dfe11df62Jin Cao    protected MaterialSearchViewController mSearchViewController;
198a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal    /**
199a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal     * The hash of {@link #mCurrentConversation} in detached mode. 0 if we are not in detached mode.
200a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal     */
201a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal    private Uri mDetachedConvUri;
202d7a12cd5b5c2639b653d8c671b04844ef02ee01dVikram Aggarwal
203f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy    /** A map of {@link Folder} {@link Uri} to scroll position in the conversation list. */
204f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy    private final Bundle mConversationListScrollPositions = new Bundle();
205f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy
2066ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook    /** A {@link android.content.BroadcastReceiver} that suppresses new e-mail notifications. */
2076ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook    private SuppressNotificationReceiver mNewEmailReceiver = null;
2086ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook
20959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal    /** Handler for all our local runnables. */
210fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira    protected Handler mHandler = new Handler();
211fa995b4bbccd0e650ef48878107ede5e53dd8309Mindy Pereira
212fa131a2ff399fd1d544f759b063268fb4e8a3e70Vikram Aggarwal    /**
213161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * The current mode of the application. All changes in mode are initiated by
214161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * the activity controller. View mode changes are propagated to classes that
215161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * attach themselves as listeners of view mode changes.
216fa131a2ff399fd1d544f759b063268fb4e8a3e70Vikram Aggarwal     */
2171ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    protected final ViewMode mViewMode;
21880aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwal    protected ContentResolver mResolver;
219c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal    protected boolean mHaveAccountList = false;
220b7b33e07325fc6664d8ab803b141b87adaa2d013Mindy Pereira    private AsyncRefreshTask mAsyncRefreshTask;
2215cb0c3e684150d6d8ed4d9df4fb73a7606086aaaMindy Pereira
2224e0158f03fd8f430e88394e8b99e1b65ed01838aAndy Huang    private boolean mDestroyed;
2234e0158f03fd8f430e88394e8b99e1b65ed01838aAndy Huang
224bcb16b98140a83a4db3c51568d21c839595f73dfVikram Aggarwal    /** True if running on tablet */
225bcb16b98140a83a4db3c51568d21c839595f73dfVikram Aggarwal    private final boolean mIsTablet;
226bcb16b98140a83a4db3c51568d21c839595f73dfVikram Aggarwal
2271ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    /**
2281ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang     * Are we in a point in the Activity/Fragment lifecycle where it's safe to execute fragment
2291ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang     * transactions? (including back stack manipulation)
2301ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang     * <p>
2311ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang     * Per docs in {@link FragmentManager#beginTransaction()}, this flag starts out true, switches
2321ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang     * to false after {@link Activity#onSaveInstanceState}, and becomes true again in both onStart
2331ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang     * and onResume.
2341ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang     */
2351ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    private boolean mSafeToModifyFragments = true;
2361ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang
23723b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook    private final Set<Uri> mCurrentAccountUris = Sets.newHashSet();
238967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    protected ConversationCursor mConversationListCursor;
23961f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang    private final DataSetObservable mConversationListObservable = new MailObservable("List");
240bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank
24159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal    /** Runnable that checks the logging level to enable/disable the logging service. */
24259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal    private Runnable mLogServiceChecker = null;
243de60c9d0ad164a5daf358181a05bbd43edd5d5edVikram Aggarwal    /** List of all accounts currently known to the controller. This is never null. */
244de60c9d0ad164a5daf358181a05bbd43edd5d5edVikram Aggarwal    private Account[] mAllAccounts = new Account[0];
24559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal
24677ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal    private FolderWatcher mFolderWatcher;
24777ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal
2485bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein    private boolean mIgnoreInitialConversationLimit;
2495bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein
2507c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu    /**
2517c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu     * Interface for actions that are deferred until after a load completes. This is for handling
2527c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu     * user actions which affect cursors (e.g. marking messages read or unread) that happen before
2537c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu     * that cursor is loaded.
2547c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu     */
2557c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu    private interface LoadFinishedCallback {
2567c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu        void onLoadFinished();
2577c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu    }
2587c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu
2597c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu    /** The deferred actions to execute when mConversationListCursor load completes. */
2607c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu    private final ArrayList<LoadFinishedCallback> mConversationListLoadFinishedCallbacks =
2617c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu            new ArrayList<LoadFinishedCallback>();
2627c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu
263bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    private RefreshTimerTask mConversationListRefreshTask;
264e1d1b07cdb0026097eb80f6c2912a16353aacec1Marc Blank
2657c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal    /** Listeners that are interested in changes to the current account. */
26661f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang    private final DataSetObservable mAccountObservers = new MailObservable("Account");
26758cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal    /** Listeners that are interested in changes to the recent folders. */
26861f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang    private final DataSetObservable mRecentFolderObservers = new MailObservable("RecentFolder");
26907dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal    /** Listeners that are interested in changes to the list of all accounts. */
27061f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang    private final DataSetObservable mAllAccountObservers = new MailObservable("AllAccounts");
27150ff0e50f1a27144e55e4184f48e433439727e7bVikram Aggarwal    /** Listeners that are interested in changes to the current folder. */
27261f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang    private final DataSetObservable mFolderObservable = new MailObservable("CurrentFolder");
27354022ee4c98f02401ca567e643f24ed42aebe203Tony Mantler    /** Listeners that are interested in changes to the Folder or Account selection */
27454022ee4c98f02401ca567e643f24ed42aebe203Tony Mantler    private final DataSetObservable mFolderOrAccountObservers =
27554022ee4c98f02401ca567e643f24ed42aebe203Tony Mantler            new MailObservable("FolderOrAccount");
27658cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal
277967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    /**
278967ede63e059810d9d4d425c50111d606014c893Mindy Pereira     * Selected conversations, if any.
279967ede63e059810d9d4d425c50111d606014c893Mindy Pereira     */
280ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao    private final ConversationCheckedSet mCheckedSet = new ConversationCheckedSet();
281dac00547ce8d44a964e4510315168ee413e55a75Mindy Pereira
282c7a070f90a47ea658c36185c65ba8dcc7f2b09b9Paul Westbrook    private final int mFolderItemUpdateDelayMs;
283c7a070f90a47ea658c36185c65ba8dcc7f2b09b9Paul Westbrook
284135fd02e74ea8449b36c3dc44cd796e06a7e714dVikram Aggarwal    /** Keeps track of selected and unselected conversations */
285937c94f025ed55c19bd46c7c077fa3575ab6be52Paul Westbrook    final protected ConversationPositionTracker mTracker;
286af1ee0c4b6258530d8da30bcdb2a9cb403f70d5eVikram Aggarwal
287e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal    /**
288e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal     * Action menu associated with the selected set.
289e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal     */
290e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal    SelectedConversationsActionMenu mCabActionMenu;
2911a1e9754cce66296ae730f2c483f55fcf1a24359James Lemieux
2921a1e9754cce66296ae730f2c483f55fcf1a24359James Lemieux    /** The compose button floating over the conversation/search lists */
2931a1e9754cce66296ae730f2c483f55fcf1a24359James Lemieux    protected View mFloatingComposeButton;
294c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein    protected ActionableToastBar mToastBar;
295632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    protected ConversationPagerController mPagerController;
296ab4863650794e81f35a0fd9abc16a5564d7b102bMindy Pereira
297d70fe49cabf896e87574bcb2e57e8a32608537beVikram Aggarwal    // This is split out from the general loader dispatcher because its loader doesn't return a
298b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang    // basic Cursor
299d70fe49cabf896e87574bcb2e57e8a32608537beVikram Aggarwal    /** Handles loader callbacks to create a convesation cursor. */
300b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang    private final ConversationListLoaderCallbacks mListCursorCallbacks =
301b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang            new ConversationListLoaderCallbacks();
302b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang
303177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    /** Object that listens to all LoaderCallbacks that result in {@link Folder} creation. */
304177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    private final FolderLoads mFolderCallbacks = new FolderLoads();
305177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    /** Object that listens to all LoaderCallbacks that result in {@link Account} creation. */
306177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    private final AccountLoads mAccountCallbacks = new AccountLoads();
307177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal
30869a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    /**
30969a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     * Matched addresses that must be shielded from users because they are temporary. Even though
31069a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     * this is instantiated from settings, this matcher is valid for all accounts, and is expected
31169a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     * to live past the life of an account.
31269a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal     */
31369a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    private final VeiledAddressMatcher mVeiledMatcher;
31469a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal
315b334c9035e9b7a38766bb66c29da2208525d1e11Paul Westbrook    protected static final String LOG_TAG = LogTag.getLogTag();
316d70fe49cabf896e87574bcb2e57e8a32608537beVikram Aggarwal
317aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal    // Loader constants: Accounts
318aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal    /**
319aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * The list of accounts. This loader is started early in the application life-cycle since
320aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * the list of accounts is central to all other data the application needs: unread counts for
321aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * folders, critical UI settings like show/hide checkboxes, ...
322aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * The loader is started when the application is created: both in
323aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * {@link #onCreate(Bundle)} and in {@link #onActivityResult(int, int, Intent)}. It is never
324aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * destroyed since the cursor is needed through the life of the application. When the list of
325aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * accounts changes, we notify {@link #mAllAccountObservers}.
326aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     */
327fa4b47e4a1962010ec3c8ea7476ac1e701f461b4Vikram Aggarwal    private static final int LOADER_ACCOUNT_CURSOR = 0;
328aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal
329aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal    /**
330aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * The current account. This loader is started when we have an account. The mail application
331aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * <b>needs</b> a valid account to function. As soon as we set {@link #mAccount},
332aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * we start a loader to observe for changes on the current account.
333aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * The loader is always restarted when an account is set in {@link #setAccount(Account)}.
334aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * When the current account object changes, we notify {@link #mAccountObservers}.
335aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * A possible performance improvement would be to listen purely on
336aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * {@link #LOADER_ACCOUNT_CURSOR}. The current account is guaranteed to be in the list,
337aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * and would avoid two updates when a single setting on the current account changes.
338aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     */
339004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps    private static final int LOADER_ACCOUNT_UPDATE_CURSOR = 1;
340004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps
341004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps    // Loader constants: Conversations
342004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps
343004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps    /** The conversation cursor over the current conversation list. This loader provides
344004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps     * a cursor over conversation entries from a folder to display a conversation
345004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps     * list.
346004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps     * This loader is started when the user switches folders (in {@link #updateFolder(Folder)},
347004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps     * or when the controller is told that a folder/account change is imminent
348004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps     * (in {@link #preloadConvList(Account, Folder)}. The loader is maintained for the life of
349004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps     * the current folder. When the user switches folders, the old loader is destroyed and a new
350004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps     * one is created.
351004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps     *
352004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps     * When the conversation list changes, we notify {@link #mConversationListObservable}.
353004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps     */
354004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps    private static final int LOADER_CONVERSATION_LIST = 10;
355004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps
356004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps    // Loader constants: misc
357004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps    /**
358004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps     * The loader that determines whether the Warm welcome tour should be displayed for the user.
359004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps     */
360004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps    public static final int LOADER_WELCOME_TOUR = 20;
361d70fe49cabf896e87574bcb2e57e8a32608537beVikram Aggarwal
362944ef3b09d15d28f13cf63e3410850e246b1d54fGreg Bullock    /**
363944ef3b09d15d28f13cf63e3410850e246b1d54fGreg Bullock     * The load which loads accounts for the welcome tour.
364944ef3b09d15d28f13cf63e3410850e246b1d54fGreg Bullock     */
365944ef3b09d15d28f13cf63e3410850e246b1d54fGreg Bullock    public static final int LOADER_WELCOME_TOUR_ACCOUNTS = 21;
366944ef3b09d15d28f13cf63e3410850e246b1d54fGreg Bullock
367aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal    // Loader constants: Folders
368004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps
369aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal    /** The current folder. This loader watches for updates to the current folder in a manner
370aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * analogous to the {@link #LOADER_ACCOUNT_UPDATE_CURSOR}. Updates to the current folder
371aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * might be due to server-side changes (unread count), or local changes (sync window or sync
372aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * status change).
373aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * The change of current folder calls {@link #updateFolder(Folder)}.
374aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * This is responsible for restarting a loader using the URI of the provided folder. When the
375aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * loader returns, the current folder is updated and consumers, if any, are notified.
376aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * When the current folder changes, we notify {@link #mFolderObservable}
377aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     */
378004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps    private static final int LOADER_FOLDER_CURSOR = 30;
379004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps
380aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal    /**
381aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * The list of recent folders. Recent folders are shown in the DrawerFragment. The recent
382aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * folders are tied to the current account being viewed. When the account is changed,
383aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * we restart this loader to retrieve the recent accounts. Recents are pre-populated for
384aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * phones historically, when they were displayed in the spinner. On the tablet,
385aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * they showed in the {@link FolderListFragment} and were not-populated.  The code to
386aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * pre-populate the recents is somewhat convoluted: when the loader returns a short list of
387aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * recent folders, it issues an update on the Recent Folder URI. The underlying provider then
388aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * does the appropriate thing to populate recent folders, and notify of a change on the cursor.
389aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * Recent folders are needed for the life of the current account.
390aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * When the recent folders change, we notify {@link #mRecentFolderObservers}.
391aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     */
392004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps    private static final int LOADER_RECENT_FOLDERS = 31;
393aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal    /**
394aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * The primary inbox for the current account. The mechanism to load the default inbox for the
395aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * current account is (sadly) different from loading other folders. The method
396aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * {@link #loadAccountInbox()} is called, and it restarts this loader. When the loader returns
397aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * a valid cursor, we create a folder, call {@link #onFolderChanged{Folder)} eventually
398aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * calling {@link #updateFolder(Folder)} which starts a loader {@link #LOADER_FOLDER_CURSOR}
399aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * over the current folder.
400aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * When we have a valid cursor, we destroy this loader, This convoluted flow is historical.
401aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     */
402004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps    private static final int LOADER_ACCOUNT_INBOX = 32;
403004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps
404aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal    /**
405aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * The fake folder of search results for a term. When we search for a term,
406aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * a new activity is created with {@link Intent#ACTION_SEARCH}. For this new activity,
407aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * we start a loader which returns conversations that match the user-provided query.
408aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * We destroy the loader when we obtain a valid cursor since subsequent searches will create
409aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * a new activity.
410aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     */
411004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps    private static final int LOADER_SEARCH = 33;
412aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal    /**
413aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * The initial folder at app start. When the application is launched from an intent that
414aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * specifies the initial folder (notifications/widgets/shortcuts),
415aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * then we extract the folder URI from the intent, but we cannot trust the folder object. Since
416aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * shortcuts and widgets persist past application update, they might have incorrect
417aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * information encoded in them. So, to obtain a {@link Folder} object from a {@link Uri},
418aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * we need to start another loader. Upon obtaining a valid cursor, the loader is destroyed.
419aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * An additional complication arises if we have to view a specific conversation within this
420aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * folder. This is the case when launching the app from a single conversation notification
421aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * or tapping on a specific conversation in the widget. In these cases, the conversation is
422aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * saved in {@link #mConversationToShow} and is retrieved when the loader returns.
423aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     */
424004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps    public static final int LOADER_FIRST_FOLDER = 34;
425de21e898901a86acdcf62ea46d358184bfd063b3Régis Décamps
426de21e898901a86acdcf62ea46d358184bfd063b3Régis Décamps    /**
4277a5d95a08464fb82b0310c82f3c39bfb8b63539fVikram Aggarwal     * Guaranteed to be the last loader ID used by the activity. Loaders are owned by Activity or
4287a5d95a08464fb82b0310c82f3c39bfb8b63539fVikram Aggarwal     * fragments, and within an activity, loader IDs need to be unique. A hack to ensure that the
4297a5d95a08464fb82b0310c82f3c39bfb8b63539fVikram Aggarwal     * {@link FolderWatcher} can create its folder loaders without clashing with the IDs of those
4307a5d95a08464fb82b0310c82f3c39bfb8b63539fVikram Aggarwal     * of the {@link AbstractActivityController}. Currently, the {@link FolderWatcher} is the only
4317a5d95a08464fb82b0310c82f3c39bfb8b63539fVikram Aggarwal     * other class that uses this activity's LoaderManager. If another class needs activity-level
4327a5d95a08464fb82b0310c82f3c39bfb8b63539fVikram Aggarwal     * loaders, consider consolidating the loaders in a central location: a UI-less fragment
4337a5d95a08464fb82b0310c82f3c39bfb8b63539fVikram Aggarwal     * perhaps.
4347a5d95a08464fb82b0310c82f3c39bfb8b63539fVikram Aggarwal     */
435004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps    public static final int LAST_LOADER_ID = 35;
436004b46f0fe9945d349c7a11126ed6aab0095c272Régis Décamps
4377c8325de829ca029ce4547e4f0fa266124301367Scott Kennedy    /**
4387c8325de829ca029ce4547e4f0fa266124301367Scott Kennedy     * Guaranteed to be the last loader ID used by the Fragment. Loaders are owned by Activity or
4397c8325de829ca029ce4547e4f0fa266124301367Scott Kennedy     * fragments, and within an activity, loader IDs need to be unique. Currently,
4402d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein     * SectionedInboxTeaserView is the only class that uses the
4417c8325de829ca029ce4547e4f0fa266124301367Scott Kennedy     * {@link ConversationListFragment}'s LoaderManager.
4427c8325de829ca029ce4547e4f0fa266124301367Scott Kennedy     */
4437c8325de829ca029ce4547e4f0fa266124301367Scott Kennedy    public static final int LAST_FRAGMENT_LOADER_ID = 1000;
4444a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal
4456aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal    /** Code returned after an account has been added. */
4462388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook    private static final int ADD_ACCOUNT_REQUEST_CODE = 1;
4476aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal    /** Code returned when the user has to enter the new password on an existing account. */
448122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook    private static final int REAUTHENTICATE_REQUEST_CODE = 2;
449371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon    /** Code returned when the previous activity needs to navigate to a different folder
450371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon     *  or account */
451371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon    private static final int CHANGE_NAVIGATION_REQUEST_CODE = 3;
452371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon
453406ae08b92f78f26fe83830897570c9db1ab23aaGreg Bullock    /** Code returned from voice search intent */
454406ae08b92f78f26fe83830897570c9db1ab23aaGreg Bullock    public static final int VOICE_SEARCH_REQUEST_CODE = 4;
455406ae08b92f78f26fe83830897570c9db1ab23aaGreg Bullock
456371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon    public static final String EXTRA_FOLDER = "extra-folder";
457371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon    public static final String EXTRA_ACCOUNT = "extra-account";
4582388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook
459e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal    /** The pending destructive action to be carried out before swapping the conversation cursor.*/
460e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal    private DestructiveAction mPendingDestruction;
461c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein    protected AsyncRefreshTask mFolderSyncTask;
462c975e8416b64a1ebdec6adee7e65dd1d0928e8d4Mindy Pereira    private Folder mFolderListFolder;
463e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon    private final int mShowUndoBarDelay;
4646f54e1b0e8beae2e624852f9f2f2b1b38745e3bemindyp    private boolean mRecentsDataUpdated;
465a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal    /** A wait fragment we added, if any. */
466a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal    private WaitFragment mWaitFragment;
467f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal    /** True if we have results from a search query */
46887a8982af83168453b6dc219fe984bff68047bebAndy Huang    protected boolean mHaveSearchResults = false;
4696cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal    /** If a confirmation dialog is being show, the listener for the positive action. */
4706cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal    private OnClickListener mDialogListener;
4716cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal    /**
4726cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal     * If a confirmation dialog is being show, the resource of the action: R.id.delete, etc.  This
4736cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal     * is used to create a new {@link #mDialogListener} on orientation changes.
4746cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal     */
4756cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal    private int mDialogAction = -1;
476b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal    /**
477b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal     * If a confirmation dialog is being shown, this is true if the dialog acts on the selected set
478b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal     * and false if it acts on the currently selected conversation
479b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal     */
480b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal    private boolean mDialogFromSelectedSet;
4816cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal
482177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    /** Which conversation to show, if started from widget/notification. */
483177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    private Conversation mConversationToShow = null;
484177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal
485c94d07f4460d00db898237603de311d226707a96Andy Huang    /**
486c94d07f4460d00db898237603de311d226707a96Andy Huang     * A temporary reference to the pending destructive action that was deferred due to an
487c94d07f4460d00db898237603de311d226707a96Andy Huang     * auto-advance transition in progress.
488c94d07f4460d00db898237603de311d226707a96Andy Huang     * <p>
489c94d07f4460d00db898237603de311d226707a96Andy Huang     * In detail: when auto-advance triggers a mode change, we must wait until the transition
490c94d07f4460d00db898237603de311d226707a96Andy Huang     * completes before executing the destructive action to ensure a smooth mode change transition.
491c94d07f4460d00db898237603de311d226707a96Andy Huang     * This member variable houses the pending destructive action work to be run upon completion.
492c94d07f4460d00db898237603de311d226707a96Andy Huang     */
493c94d07f4460d00db898237603de311d226707a96Andy Huang    private Runnable mAutoAdvanceOp = null;
494c94d07f4460d00db898237603de311d226707a96Andy Huang
49512b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang    protected DrawerLayout mDrawerContainer;
49612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang    protected View mDrawerPullout;
49712b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang    protected ActionBarDrawerToggle mDrawerToggle;
498f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang
49912b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang    protected ListView mListViewForAnimating;
50012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang    protected boolean mHasNewAccountOrFolder;
501a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein    private boolean mConversationListLoadFinishedIgnored;
50261f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang    private final MailDrawerListener mDrawerListener = new MailDrawerListener();
5035747e152fa606c20261b18431f4a9f0ff3be7b5aAndrew Sapperstein    private boolean mHideMenuItems;
50412b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang
505144bfe739b93afdee0a1700a34806b0b287e5887Andy Huang    private final DrawIdler mDrawIdler = new DrawIdler();
506144bfe739b93afdee0a1700a34806b0b287e5887Andy Huang
50700179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein    public static final String SYNC_ERROR_DIALOG_FRAGMENT_TAG = "SyncErrorDialogFragment";
508e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal
509cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedy    private final DataSetObserver mUndoNotificationObserver = new DataSetObserver() {
510cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedy        @Override
511cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedy        public void onChanged() {
512cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedy            super.onChanged();
513cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedy
514cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedy            if (mConversationListCursor != null) {
515cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedy                mConversationListCursor.handleNotificationActions();
516cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedy            }
517cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedy        }
518cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedy    };
519cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedy
52053de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein    private final HomeButtonListener mHomeButtonListener = new HomeButtonListener();
52153de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein
522a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    public AbstractActivityController(MailActivity activity, ViewMode viewMode) {
523a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal        mActivity = activity;
5246902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal        mFragmentManager = mActivity.getFragmentManager();
525a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal        mViewMode = viewMode;
526a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal        mContext = activity.getApplicationContext();
527025eba8bfd4d0b5e248b6de0bda6f1129170fb41Vikram Aggarwal        mRecentFolderList = new RecentFolderList(mContext);
528937c94f025ed55c19bd46c7c077fa3575ab6be52Paul Westbrook        mTracker = new ConversationPositionTracker(this);
529967ede63e059810d9d4d425c50111d606014c893Mindy Pereira        // Allow the fragment to observe changes to its own selection set. No other object is
530967ede63e059810d9d4d425c50111d606014c893Mindy Pereira        // aware of the selected set.
531ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao        mCheckedSet.addObserver(this);
532c7a070f90a47ea658c36185c65ba8dcc7f2b09b9Paul Westbrook
533bcb16b98140a83a4db3c51568d21c839595f73dfVikram Aggarwal        final Resources r = mContext.getResources();
534bcb16b98140a83a4db3c51568d21c839595f73dfVikram Aggarwal        mFolderItemUpdateDelayMs = r.getInteger(R.integer.folder_item_refresh_delay_ms);
535bcb16b98140a83a4db3c51568d21c839595f73dfVikram Aggarwal        mShowUndoBarDelay = r.getInteger(R.integer.show_undo_bar_delay_ms);
53669a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal        mVeiledMatcher = VeiledAddressMatcher.newInstance(activity.getResources());
537bcb16b98140a83a4db3c51568d21c839595f73dfVikram Aggarwal        mIsTablet = Utils.useTabletUI(r);
538a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein        mConversationListLoadFinishedIgnored = false;
5394a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal    }
5404a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal
54187a8982af83168453b6dc219fe984bff68047bebAndy Huang    @Override
54287a8982af83168453b6dc219fe984bff68047bebAndy Huang    public final String toString() {
54387a8982af83168453b6dc219fe984bff68047bebAndy Huang        final StringBuilder sb = new StringBuilder(super.toString());
54487a8982af83168453b6dc219fe984bff68047bebAndy Huang        sb.append("{");
54587a8982af83168453b6dc219fe984bff68047bebAndy Huang        sb.append("mCurrentConversation=");
54687a8982af83168453b6dc219fe984bff68047bebAndy Huang        sb.append(mCurrentConversation);
54787a8982af83168453b6dc219fe984bff68047bebAndy Huang        appendToString(sb);
54887a8982af83168453b6dc219fe984bff68047bebAndy Huang        sb.append("}");
54987a8982af83168453b6dc219fe984bff68047bebAndy Huang        return sb.toString();
55087a8982af83168453b6dc219fe984bff68047bebAndy Huang    }
55187a8982af83168453b6dc219fe984bff68047bebAndy Huang
55287a8982af83168453b6dc219fe984bff68047bebAndy Huang    protected void appendToString(StringBuilder sb) {}
55387a8982af83168453b6dc219fe984bff68047bebAndy Huang
554e9a81035bcef5a372fa13a992f8553eb60e6aaf0Vikram Aggarwal    public Account getCurrentAccount() {
555e9a81035bcef5a372fa13a992f8553eb60e6aaf0Vikram Aggarwal        return mAccount;
5564a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal    }
5574a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal
5581ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    public ConversationListContext getCurrentListContext() {
559e9a81035bcef5a372fa13a992f8553eb60e6aaf0Vikram Aggarwal        return mConvListContext;
5604a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal    }
5614a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal
562a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    @Override
563ef7c992d8cb462be6da34d42965cf185f3bb1b22Vikram Aggarwal    public final ConversationCursor getConversationListCursor() {
564967ede63e059810d9d4d425c50111d606014c893Mindy Pereira        return mConversationListCursor;
565967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    }
566967ede63e059810d9d4d425c50111d606014c893Mindy Pereira
5676902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal    /**
56834a650154646f550435b7976d2ac6fc74c21234fVikram Aggarwal     * Check if the fragment is attached to an activity and has a root view.
569715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @param in fragment to be checked
57034a650154646f550435b7976d2ac6fc74c21234fVikram Aggarwal     * @return true if the fragment is valid, false otherwise
57134a650154646f550435b7976d2ac6fc74c21234fVikram Aggarwal     */
572715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal    private static boolean isValidFragment(Fragment in) {
573715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal        return !(in == null || in.getActivity() == null || in.getView() == null);
57434a650154646f550435b7976d2ac6fc74c21234fVikram Aggarwal    }
57534a650154646f550435b7976d2ac6fc74c21234fVikram Aggarwal
57634a650154646f550435b7976d2ac6fc74c21234fVikram Aggarwal    /**
57749e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * Get the conversation list fragment for this activity. If the conversation list fragment is
57849e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * not attached, this method returns null.
579839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang     *
58049e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * Caution! This method returns the {@link ConversationListFragment} after the fragment has been
58149e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * added, <b>and</b> after the {@link FragmentManager} has run through its queue to add the
58249e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * fragment. There is a non-trivial amount of time after the fragment is instantiated and before
58349e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * this call returns a non-null value, depending on the {@link FragmentManager}. If you
58449e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * need the fragment immediately after adding it, consider making the fragment an observer of
58549e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * the controller and perform the task immediately on {@link Fragment#onActivityCreated(Bundle)}
5866902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal     */
5876902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal    protected ConversationListFragment getConversationListFragment() {
5886902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal        final Fragment fragment = mFragmentManager.findFragmentByTag(TAG_CONVERSATION_LIST);
58934a650154646f550435b7976d2ac6fc74c21234fVikram Aggarwal        if (isValidFragment(fragment)) {
59034a650154646f550435b7976d2ac6fc74c21234fVikram Aggarwal            return (ConversationListFragment) fragment;
5919585d7810519b7e5475d9c7458e7a8a9c12fd024Andy Huang        }
59234a650154646f550435b7976d2ac6fc74c21234fVikram Aggarwal        return null;
5936902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal    }
5946902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal
5956902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal    /**
5966902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal     * Returns the folder list fragment attached with this activity. If no such fragment is attached
5976902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal     * this method returns null.
598839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang     *
59949e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * Caution! This method returns the {@link FolderListFragment} after the fragment has been
60049e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * added, <b>and</b> after the {@link FragmentManager} has run through its queue to add the
60149e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * fragment. There is a non-trivial amount of time after the fragment is instantiated and before
60249e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * this call returns a non-null value, depending on the {@link FragmentManager}. If you
60349e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * need the fragment immediately after adding it, consider making the fragment an observer of
60449e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * the controller and perform the task immediately on {@link Fragment#onActivityCreated(Bundle)}
6056902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal     */
6066902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal    protected FolderListFragment getFolderListFragment() {
60710fcd645f703799326da326630b663df69f91508James Lemieux        final String drawerPulloutTag = mActivity.getString(R.string.drawer_pullout_tag);
60810fcd645f703799326da326630b663df69f91508James Lemieux        final Fragment fragment = mFragmentManager.findFragmentByTag(drawerPulloutTag);
60934a650154646f550435b7976d2ac6fc74c21234fVikram Aggarwal        if (isValidFragment(fragment)) {
61034a650154646f550435b7976d2ac6fc74c21234fVikram Aggarwal            return (FolderListFragment) fragment;
6119585d7810519b7e5475d9c7458e7a8a9c12fd024Andy Huang        }
61234a650154646f550435b7976d2ac6fc74c21234fVikram Aggarwal        return null;
6136902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal    }
6146902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal
6151ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    /**
616161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * Initialize the action bar. This is not visible to OnePaneController and
617161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * TwoPaneController so they cannot override this behavior.
6181ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal     */
619abd24d82e41fd4747945d3f124b4e3a57e5dc6e6Vikram Aggarwal    private void initializeActionBar() {
62052882ff08fe9420fe2dbb3f681545a330222e9c8Andrew Sapperstein        final ActionBar actionBar = mActivity.getSupportActionBar();
6215895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang        if (actionBar == null) {
6225895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang            return;
623abd24d82e41fd4747945d3f124b4e3a57e5dc6e6Vikram Aggarwal        }
6245895f7b0e5fa921f6d46bbaf6d8c7b1a8ebc7804Andy Huang
625c6801eb828627c37b8992584767c095dfe11df62Jin Cao        mActionBarController = new ActionBarController(mContext);
6262d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein        mActionBarController.initialize(mActivity, this, actionBar);
627c6801eb828627c37b8992584767c095dfe11df62Jin Cao        actionBar.setShowHideAnimationEnabled(false);
6281dd054f4e5581b69a52936c9d257fa91cb6daee7Rohan Shah
62912b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        // init the action bar to allow the 'up' affordance.
63012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        // any configurations that disallow 'up' should do that later.
6312d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein        mActionBarController.setBackButton();
632abd24d82e41fd4747945d3f124b4e3a57e5dc6e6Vikram Aggarwal    }
633abd24d82e41fd4747945d3f124b4e3a57e5dc6e6Vikram Aggarwal
634abd24d82e41fd4747945d3f124b4e3a57e5dc6e6Vikram Aggarwal    /**
635abd24d82e41fd4747945d3f124b4e3a57e5dc6e6Vikram Aggarwal     * Attach the action bar to the activity.
636abd24d82e41fd4747945d3f124b4e3a57e5dc6e6Vikram Aggarwal     */
637abd24d82e41fd4747945d3f124b4e3a57e5dc6e6Vikram Aggarwal    private void attachActionBar() {
63852882ff08fe9420fe2dbb3f681545a330222e9c8Andrew Sapperstein        final ActionBar actionBar = mActivity.getSupportActionBar();
639bd09150070b1aa25ebdd9af0a29e8bf9fbbf7af8Tony Mantler        if (actionBar != null) {
6402d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein            // Show a title
6412d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein            final int mask = ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_HOME;
642bc462cadc2dcd11d27e7810e437aeef1505f4cb2Vikram Aggarwal            actionBar.setDisplayOptions(mask, mask);
6432d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein            mActionBarController.setViewModeController(mViewMode);
6441ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal        }
645a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    }
646a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal
647a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    /**
648161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * Returns whether the conversation list fragment is visible or not.
649161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * Different layouts will have their own notion on the visibility of
650161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * fragments, so this method needs to be overriden.
651161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     *
652a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal     */
6531ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    protected abstract boolean isConversationListVisible();
6541ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal
655eb7d329e448eb42195f7c35b978035766b6f46e1Vikram Aggarwal    /**
65634f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal     * If required, starts wait mode for the current account.
65734f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal     */
65834f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal    final void perhapsEnterWaitMode() {
65934f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal        // If the account is not initialized, then show the wait fragment, since nothing can be
66034f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal        // shown.
66134f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal        if (mAccount.isAccountInitializationRequired()) {
66234f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal            showWaitForInitialization();
66334f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal            return;
66434f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal        }
66534f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal
66634f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal        final boolean inWaitingMode = inWaitMode();
66734f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal        final boolean isSyncRequired = mAccount.isAccountSyncRequired();
66834f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal        if (isSyncRequired) {
66934f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal            if (inWaitingMode) {
67034f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal                // Update the WaitFragment's account object
67134f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal                updateWaitMode();
67234f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal            } else {
67334f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal                // Transition to waiting mode
67434f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal                showWaitForInitialization();
67534f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal            }
67634f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal        } else if (inWaitingMode) {
67734f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal            // Dismiss waiting mode
67834f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal            hideWaitForInitialization();
67934f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal        }
68034f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal    }
68134f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal
682eb7d329e448eb42195f7c35b978035766b6f46e1Vikram Aggarwal    @Override
6834cbb0da3cd701ee0c06237ded965648d68c663c8Andrew Sapperstein    public void switchToDefaultInboxOrChangeAccount(Account account) {
6844cbb0da3cd701ee0c06237ded965648d68c663c8Andrew Sapperstein        LogUtils.d(LOG_TAG, "AAC.switchToDefaultAccount(%s)", account);
685e764cfd130c4f92ae823fd1f361ab70439e8ec95Andy Huang        if (mViewMode.isSearchMode()) {
686371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon            // We are in an activity on top of the main navigation activity.
687371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon            // We need to return to it with a result code that indicates it should navigate to
688371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon            // a different folder.
689371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon            final Intent intent = new Intent();
690371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon            intent.putExtra(AbstractActivityController.EXTRA_ACCOUNT, account);
691371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon            mActivity.setResult(Activity.RESULT_OK, intent);
692371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon            mActivity.finish();
693371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon            return;
694371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon        }
69566bc6ed16956963cb18660d15ea7e6adc6c62411Vikram Aggarwal        final boolean firstLoad = mAccount == null;
696252684a1ddaf00119d8f2440e5d4cd93c77e5298Andrew Sapperstein        final boolean switchToDefaultInbox = !firstLoad && account.uri.equals(mAccount.uri);
697d70fe49cabf896e87574bcb2e57e8a32608537beVikram Aggarwal        // If the active account has been clicked in the drawer, go to default inbox
698252684a1ddaf00119d8f2440e5d4cd93c77e5298Andrew Sapperstein        if (switchToDefaultInbox) {
699252684a1ddaf00119d8f2440e5d4cd93c77e5298Andrew Sapperstein            loadAccountInbox();
700252684a1ddaf00119d8f2440e5d4cd93c77e5298Andrew Sapperstein            return;
701252684a1ddaf00119d8f2440e5d4cd93c77e5298Andrew Sapperstein        }
7024cbb0da3cd701ee0c06237ded965648d68c663c8Andrew Sapperstein        changeAccount(account);
7034cbb0da3cd701ee0c06237ded965648d68c663c8Andrew Sapperstein    }
7044cbb0da3cd701ee0c06237ded965648d68c663c8Andrew Sapperstein
7054cbb0da3cd701ee0c06237ded965648d68c663c8Andrew Sapperstein    public void changeAccount(Account account) {
7064cbb0da3cd701ee0c06237ded965648d68c663c8Andrew Sapperstein        LogUtils.d(LOG_TAG, "AAC.changeAccount(%s)", account);
7074cbb0da3cd701ee0c06237ded965648d68c663c8Andrew Sapperstein        // Is the account or account settings different from the existing account?
7084cbb0da3cd701ee0c06237ded965648d68c663c8Andrew Sapperstein        final boolean firstLoad = mAccount == null;
7094cbb0da3cd701ee0c06237ded965648d68c663c8Andrew Sapperstein        final boolean accountChanged = firstLoad || !account.uri.equals(mAccount.uri);
7104cbb0da3cd701ee0c06237ded965648d68c663c8Andrew Sapperstein
711472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        // If nothing has changed, return early without wasting any more time.
712472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        if (!accountChanged && !account.settingsDiffer(mAccount)) {
713472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal            return;
714472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        }
715472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        // We also don't want to do anything if the new account is null
716472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        if (account == null) {
7175fd8afd5c1b40580c069a5a97039ca0601208216Vikram Aggarwal            LogUtils.e(LOG_TAG, "AAC.changeAccount(null) called.");
718472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal            return;
719472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        }
72079b1156803585952a4851221dc9fa9974e606433Tony Mantler        final String emailAddress = account.getEmailAddress();
721472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        mHandler.post(new Runnable() {
722472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal            @Override
723472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal            public void run() {
72479b1156803585952a4851221dc9fa9974e606433Tony Mantler                MailActivity.setNfcMessage(emailAddress);
725c6adce3cf6887c4c6dd5005724565702751843d0mindyp            }
726472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        });
727472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        if (accountChanged) {
728472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal            commitDestructiveActions(false);
729472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        }
7302168cbc9c58d2133bdf77ecd342c3d5ee755d607Régis Décamps
731472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        // Change the account here
732472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        setAccount(account);
733472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        // And carry out associated actions.
734472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        cancelRefreshTask();
735472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        if (accountChanged) {
736472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal            loadAccountInbox();
737472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        }
738472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        // Check if we need to force setting up an account before proceeding.
739472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal        if (mAccount != null && !Uri.EMPTY.equals(mAccount.settings.setupIntentUri)) {
740472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal            // Launch the intent!
741472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal            final Intent intent = new Intent(Intent.ACTION_EDIT);
742ec6e2516037e7172202e208b2015d7684a78eddbTony Mantler
743ec6e2516037e7172202e208b2015d7684a78eddbTony Mantler            intent.setPackage(mContext.getPackageName());
744472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal            intent.setData(mAccount.settings.setupIntentUri);
745ec6e2516037e7172202e208b2015d7684a78eddbTony Mantler
746472e53693c6f6c1af335a2286f30e8dfbc2499e1Vikram Aggarwal            mActivity.startActivity(intent);
74728d5f72e0ad0db75b6243ee8125bfe3aadbdcb85Mindy Pereira        }
748a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    }
749a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal
750e6340bc7f2bb9868e5014a101ea69b777d7b6750Vikram Aggarwal    /**
7517c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal     * Adds a listener interested in change in the current account. If a class is storing a
7527c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal     * reference to the current account, it should listen on changes, so it can receive updates to
7537c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal     * settings. Must happen in the UI thread.
7547d81600cbce3cfd366cbff9ecd1b7317ff957221Vikram Aggarwal     */
7557c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal    @Override
7567c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal    public void registerAccountObserver(DataSetObserver obs) {
7577c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal        mAccountObservers.registerObserver(obs);
7587d81600cbce3cfd366cbff9ecd1b7317ff957221Vikram Aggarwal    }
7597d81600cbce3cfd366cbff9ecd1b7317ff957221Vikram Aggarwal
7607d81600cbce3cfd366cbff9ecd1b7317ff957221Vikram Aggarwal    /**
7617c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal     * Removes a listener from receiving current account changes.
7627d81600cbce3cfd366cbff9ecd1b7317ff957221Vikram Aggarwal     * Must happen in the UI thread.
7637d81600cbce3cfd366cbff9ecd1b7317ff957221Vikram Aggarwal     */
7647c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal    @Override
7657c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal    public void unregisterAccountObserver(DataSetObserver obs) {
7667c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal        mAccountObservers.unregisterObserver(obs);
7677d81600cbce3cfd366cbff9ecd1b7317ff957221Vikram Aggarwal    }
7687d81600cbce3cfd366cbff9ecd1b7317ff957221Vikram Aggarwal
7697c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal    @Override
77007dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal    public void registerAllAccountObserver(DataSetObserver observer) {
77107dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal        mAllAccountObservers.registerObserver(observer);
77207dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal    }
77307dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal
77407dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal    @Override
77507dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal    public void unregisterAllAccountObserver(DataSetObserver observer) {
77607dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal        mAllAccountObservers.unregisterObserver(observer);
77707dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal    }
77807dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal
77907dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal    @Override
78007dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal    public Account[] getAllAccounts() {
78107dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal        return mAllAccounts;
78207dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal    }
78307dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal
78407dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal    @Override
7857c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal    public Account getAccount() {
7867c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal        return mAccount;
7877d81600cbce3cfd366cbff9ecd1b7317ff957221Vikram Aggarwal    }
7887d81600cbce3cfd366cbff9ecd1b7317ff957221Vikram Aggarwal
7890f73d905c5e935a5280f8eb647cc924c2854b7deRohan Shah    @Override
79054022ee4c98f02401ca567e643f24ed42aebe203Tony Mantler    public void registerFolderOrAccountChangedObserver(final DataSetObserver observer) {
79154022ee4c98f02401ca567e643f24ed42aebe203Tony Mantler        mFolderOrAccountObservers.registerObserver(observer);
7920f73d905c5e935a5280f8eb647cc924c2854b7deRohan Shah    }
7930f73d905c5e935a5280f8eb647cc924c2854b7deRohan Shah
7940f73d905c5e935a5280f8eb647cc924c2854b7deRohan Shah    @Override
79554022ee4c98f02401ca567e643f24ed42aebe203Tony Mantler    public void unregisterFolderOrAccountChangedObserver(final DataSetObserver observer) {
79654022ee4c98f02401ca567e643f24ed42aebe203Tony Mantler        mFolderOrAccountObservers.unregisterObserver(observer);
7970f73d905c5e935a5280f8eb647cc924c2854b7deRohan Shah    }
7980f73d905c5e935a5280f8eb647cc924c2854b7deRohan Shah
7990f73d905c5e935a5280f8eb647cc924c2854b7deRohan Shah    /**
80012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang     * If the drawer is open, the function locks the drawer to the closed, thereby sliding in
80112b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang     * the drawer to the left edge, disabling events, and refreshing it once it's either closed
80212b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang     * or put in an idle state.
8030f73d905c5e935a5280f8eb647cc924c2854b7deRohan Shah     */
8040f73d905c5e935a5280f8eb647cc924c2854b7deRohan Shah    @Override
805a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal    public void closeDrawer(final boolean hasNewFolderOrAccount, Account nextAccount,
806a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal            Folder nextFolder) {
80712b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        if (!isDrawerEnabled()) {
80854022ee4c98f02401ca567e643f24ed42aebe203Tony Mantler            if (hasNewFolderOrAccount) {
80954022ee4c98f02401ca567e643f24ed42aebe203Tony Mantler                mFolderOrAccountObservers.notifyChanged();
81054022ee4c98f02401ca567e643f24ed42aebe203Tony Mantler            }
81112b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            return;
81212b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
81312b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        // If there are no new folders or accounts to switch to, just close the drawer
81412b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        if (!hasNewFolderOrAccount) {
81512b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            mDrawerContainer.closeDrawers();
81612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            return;
81712b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
8182f9d394867be45302bb4323da7cacf193da38f39Vikram Aggarwal        // Otherwise, start preloading the conversation list for the new folder.
8192f9d394867be45302bb4323da7cacf193da38f39Vikram Aggarwal        if (nextFolder != null) {
8202f9d394867be45302bb4323da7cacf193da38f39Vikram Aggarwal            preloadConvList(nextAccount, nextFolder);
8212f9d394867be45302bb4323da7cacf193da38f39Vikram Aggarwal        }
8222f9d394867be45302bb4323da7cacf193da38f39Vikram Aggarwal        // Remember if the conversation list view is animating
82312b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        final ConversationListFragment conversationList = getConversationListFragment();
82412b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        if (conversationList != null) {
82512b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            mListViewForAnimating = conversationList.getListView();
82612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        } else {
82712b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            // There is no conversation list to animate, so just set it to null
82812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            mListViewForAnimating = null;
82912b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
83012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang
83112b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        if (mDrawerContainer.isDrawerOpen(mDrawerPullout)) {
83212b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            // Lets the drawer listener update the drawer contents and notify the FolderListFragment
83312b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            mHasNewAccountOrFolder = true;
83412b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            mDrawerContainer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
83512b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        } else {
83612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            // Drawer is already closed, notify observers that is the case.
83754022ee4c98f02401ca567e643f24ed42aebe203Tony Mantler            if (hasNewFolderOrAccount) {
83854022ee4c98f02401ca567e643f24ed42aebe203Tony Mantler                mFolderOrAccountObservers.notifyChanged();
83954022ee4c98f02401ca567e643f24ed42aebe203Tony Mantler            }
84012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
8410f73d905c5e935a5280f8eb647cc924c2854b7deRohan Shah    }
8420f73d905c5e935a5280f8eb647cc924c2854b7deRohan Shah
843a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal    /**
844aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * Load the conversation list early for the given folder. This happens when some UI element
845aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * (usually the drawer) instructs the controller that an account change or folder change is
846aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * imminent. While the UI element is animating, the controller can preload the conversation
847aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * list for the default inbox of the account provided here or to the folder provided here.
848aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     *
849aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * @param nextAccount The account which the app will switch to shortly, possibly null.
850aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * @param nextFolder The folder which the app will switch to shortly, possibly null.
851a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal     */
852a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal    protected void preloadConvList(Account nextAccount, Folder nextFolder) {
853a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal        // Fire off the conversation list loader for this account already with a fake
854a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal        // listener.
855e2be97a58fbae98b95e51735c6468d121e465355Paul Westbrook        final Bundle args = new Bundle(2);
856a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal        if (nextAccount != null) {
857a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal            args.putParcelable(BUNDLE_ACCOUNT_KEY, nextAccount);
858a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal        } else {
859a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal            args.putParcelable(BUNDLE_ACCOUNT_KEY, mAccount);
860a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal        }
861a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal        if (nextFolder != null) {
862a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal            args.putParcelable(BUNDLE_FOLDER_KEY, nextFolder);
8632dc850446320d6b56b89dda7fefc5113c6b7d8d1Vikram Aggarwal        } else {
8642dc850446320d6b56b89dda7fefc5113c6b7d8d1Vikram Aggarwal            LogUtils.e(LOG_TAG, new Error(), "AAC.preloadConvList(): Got an empty folder");
865a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal        }
866a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal        mFolder = null;
867a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal        final LoaderManager lm = mActivity.getLoaderManager();
868a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal        lm.destroyLoader(LOADER_CONVERSATION_LIST);
869a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal        lm.initLoader(LOADER_CONVERSATION_LIST, args, mListCursorCallbacks);
870a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal    }
871a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal
872aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal    /**
873aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * Initiates the async request to create a fake search folder, which returns conversations that
874aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * match the query term provided by the user. Returns immediately.
875aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     * @param intent Intent that the app was started with. This intent contains the search query.
876aa941d7e107f7ce09838bd4daf295259d126dfa2Vikram Aggarwal     */
877e0828393e175c9293c86a7490225f324cbec5eefMindy Pereira    private void fetchSearchFolder(Intent intent) {
878e2be97a58fbae98b95e51735c6468d121e465355Paul Westbrook        final Bundle args = new Bundle(1);
879ab4863650794e81f35a0fd9abc16a5564d7b102bMindy Pereira        args.putString(ConversationListContext.EXTRA_SEARCH_QUERY, intent
880e0828393e175c9293c86a7490225f324cbec5eefMindy Pereira                .getStringExtra(ConversationListContext.EXTRA_SEARCH_QUERY));
881177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        mActivity.getLoaderManager().restartLoader(LOADER_SEARCH, args, mFolderCallbacks);
882e0828393e175c9293c86a7490225f324cbec5eefMindy Pereira    }
883e0828393e175c9293c86a7490225f324cbec5eefMindy Pereira
884405a344937675f57fc9c6988b2b124410a270f13Jin Cao    protected void onFolderChanged(Folder folder, final boolean force) {
885f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang        if (isDrawerEnabled()) {
886f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang            /** If the folder doesn't exist, or its parent URI is empty,
887f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang             * this is not a child folder */
888f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang            final boolean isTopLevel = Folder.isRoot(folder);
889f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang            final int mode = mViewMode.getMode();
890207e662df01c550a4894bec03219820528365508Rohan Shah            updateDrawerIndicator(mode, isTopLevel);
891f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang            mDrawerContainer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
892dfbc6985e44c05e37fd8addce65a063ff57a3dc1Vikram Aggarwal
893f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang            mDrawerContainer.closeDrawers();
894f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang        }
8957dd0e1cec391fc2562795d6e6d9fc91ab36ca647Alice Yang
8967dd0e1cec391fc2562795d6e6d9fc91ab36ca647Alice Yang        if (mFolder == null || !mFolder.equals(folder)) {
8977dd0e1cec391fc2562795d6e6d9fc91ab36ca647Alice Yang            // We are actually changing the folder, so exit cab mode
8987dd0e1cec391fc2562795d6e6d9fc91ab36ca647Alice Yang            exitCabMode();
8997dd0e1cec391fc2562795d6e6d9fc91ab36ca647Alice Yang        }
9007dd0e1cec391fc2562795d6e6d9fc91ab36ca647Alice Yang
901a158ac8a41239300a2f50b38ed17167aeef4ff4bScott Kennedy        final String query;
902a158ac8a41239300a2f50b38ed17167aeef4ff4bScott Kennedy        if (folder != null && folder.isType(FolderType.SEARCH)) {
903a158ac8a41239300a2f50b38ed17167aeef4ff4bScott Kennedy            query = mConvListContext.searchQuery;
904a158ac8a41239300a2f50b38ed17167aeef4ff4bScott Kennedy        } else {
905a158ac8a41239300a2f50b38ed17167aeef4ff4bScott Kennedy            query = null;
906a158ac8a41239300a2f50b38ed17167aeef4ff4bScott Kennedy        }
907a158ac8a41239300a2f50b38ed17167aeef4ff4bScott Kennedy
908a158ac8a41239300a2f50b38ed17167aeef4ff4bScott Kennedy        changeFolder(folder, query, force);
909f3341405c97ff6d27a0d5fa37f6031e1c395d161Vikram Aggarwal    }
910f3341405c97ff6d27a0d5fa37f6031e1c395d161Vikram Aggarwal
911f3341405c97ff6d27a0d5fa37f6031e1c395d161Vikram Aggarwal    /**
912203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal     * Sets the folder state without changing view mode and without creating a list fragment, if
913203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal     * possible.
914715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @param folder the folder whose list of conversations are to be shown
915715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @param query the query string for a list of conversations matching a search
916203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal     */
917203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal    private void setListContext(Folder folder, String query) {
918203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal        updateFolder(folder);
919203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal        if (query != null) {
920203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal            mConvListContext = ConversationListContext.forSearchQuery(mAccount, mFolder, query);
921203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal        } else {
922203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal            mConvListContext = ConversationListContext.forFolder(mAccount, mFolder);
923203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal        }
924203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal        cancelRefreshTask();
925203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal    }
926203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal
927203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal    /**
928f3341405c97ff6d27a0d5fa37f6031e1c395d161Vikram Aggarwal     * Changes the folder to the value provided here. This causes the view mode to change.
929f3341405c97ff6d27a0d5fa37f6031e1c395d161Vikram Aggarwal     * @param folder the folder to change to
930f3341405c97ff6d27a0d5fa37f6031e1c395d161Vikram Aggarwal     * @param query if non-null, this represents the search string that the folder represents.
931ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang     * @param force <code>true</code> to force a folder change, <code>false</code> to disallow
932ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang     *          changing to the current folder
933f3341405c97ff6d27a0d5fa37f6031e1c395d161Vikram Aggarwal     */
934ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang    private void changeFolder(Folder folder, String query, final boolean force) {
935203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal        if (!Objects.equal(mFolder, folder)) {
936203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal            commitDestructiveActions(false);
937203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal        }
938ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang        if (folder != null && (!folder.equals(mFolder) || force)
939a1f99981919755e50f31fe300517d2fed752997eMindy Pereira                || (mViewMode.getMode() != ViewMode.CONVERSATION_LIST)) {
940203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal            setListContext(folder, query);
94128e0c34ad35925fc74633bec66344911f2de6a22Mindy Pereira            showConversationList(mConvListContext);
94258ccd6952c0a5ed979e9cee1a3f6992b4040c732Vikram Aggarwal            // Touch the current folder: it is different, and it has been accessed.
94346b5b33c4de296b5c3b61b031db9d36bf429be6aTony Mantler            if (mFolder != null) {
94446b5b33c4de296b5c3b61b031db9d36bf429be6aTony Mantler                mRecentFolderList.touchFolder(mFolder, mAccount);
94546b5b33c4de296b5c3b61b031db9d36bf429be6aTony Mantler            }
94628e0c34ad35925fc74633bec66344911f2de6a22Mindy Pereira        }
94793dc202420c3bf8adf4d9e8ae18c8e30e3cb1959Vikram Aggarwal        resetActionBarIcon();
948a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    }
949a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal
95013c12a628bd4f1e52b9a4fc44a18b85bfdaeb88bMindy Pereira    @Override
951505df5f59c92adbe8eaa734321c1d3f91be84a4aMindy Pereira    public void onFolderSelected(Folder folder) {
952ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang        onFolderChanged(folder, false /* force */);
95313c12a628bd4f1e52b9a4fc44a18b85bfdaeb88bMindy Pereira    }
95413c12a628bd4f1e52b9a4fc44a18b85bfdaeb88bMindy Pereira
955792ccbac8b579fc1077712d82144a31fd92241d0Vikram Aggarwal    /**
95658cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal     * Adds a listener interested in change in the recent folders. If a class is storing a
95758cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal     * reference to the recent folders, it should listen on changes, so it can receive updates.
95858cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal     * Must happen in the UI thread.
95958cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal     */
96058cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal    @Override
96158cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal    public void registerRecentFolderObserver(DataSetObserver obs) {
96258cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal        mRecentFolderObservers.registerObserver(obs);
96358cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal    }
96458cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal
96558cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal    /**
96658cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal     * Removes a listener from receiving recent folder changes.
96758cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal     * Must happen in the UI thread.
96858cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal     */
96958cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal    @Override
97058cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal    public void unregisterRecentFolderObserver(DataSetObserver obs) {
97158cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal        mRecentFolderObservers.unregisterObserver(obs);
97258cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal    }
97358cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal
97458cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal    @Override
97558cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal    public RecentFolderList getRecentFolders() {
97658cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal        return mRecentFolderList;
97758cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal    }
97858cad2eea744d41a11c0124e91308e38108d242eVikram Aggarwal
979405a344937675f57fc9c6988b2b124410a270f13Jin Cao    /**
980405a344937675f57fc9c6988b2b124410a270f13Jin Cao     * Load the default inbox associated with the current account.
981405a344937675f57fc9c6988b2b124410a270f13Jin Cao     */
982405a344937675f57fc9c6988b2b124410a270f13Jin Cao    protected void loadAccountInbox() {
98377ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal        boolean handled = false;
98477ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal        if (mFolderWatcher != null) {
98577ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal            final Folder inbox = mFolderWatcher.getDefaultInbox(mAccount);
98677ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal            if (inbox != null) {
987ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                onFolderChanged(inbox, false /* force */);
98877ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal                handled = true;
98977ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal            }
99077ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal        }
99177ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal        if (!handled) {
9921bf20e01767d6864ef8ed196195cf7771dc2c45cPaul Westbrook            LogUtils.d(LOG_TAG, "Starting a LOADER_ACCOUNT_INBOX for %s", mAccount);
99377ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal            restartOptionalLoader(LOADER_ACCOUNT_INBOX, mFolderCallbacks, Bundle.EMPTY);
99477ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal        }
9958cbf281d1676c21ced08c42da2aa9828cdadd87dVikram Aggarwal        final int mode = mViewMode.getMode();
9968cbf281d1676c21ced08c42da2aa9828cdadd87dVikram Aggarwal        if (mode == ViewMode.UNKNOWN || mode == ViewMode.WAITING_FOR_ACCOUNT_INITIALIZATION) {
997e6459424b3e46d768865c908af1caa932f783095Andy Huang            mViewMode.enterConversationListMode();
998e6459424b3e46d768865c908af1caa932f783095Andy Huang        }
999f6acdade010b2d0f49fd425b33e7f7b376286490Mindy Pereira    }
1000f6acdade010b2d0f49fd425b33e7f7b376286490Mindy Pereira
100177ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal    @Override
100277ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal    public void setFolderWatcher(FolderWatcher watcher) {
100377ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal        mFolderWatcher = watcher;
100477ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal    }
100577ee0ceaefaa9be699a20bfbe162f4c0ef83c6e1Vikram Aggarwal
100669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal    /**
1007d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal     * Marks the {@link #mFolderChanged} value if the newFolder is different from the existing
1008d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal     * {@link #mFolder}. This should be called immediately <b>before</b> assigning newFolder to
1009d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal     * mFolder.
1010715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @param newFolder the new folder we are switching to.
1011d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal     */
1012715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal    private void setHasFolderChanged(final Folder newFolder) {
1013d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal        // We should never try to assign a null folder. But in the rare event that we do, we should
1014d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal        // only set the bit when we have a valid folder, and null is not valid.
1015d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal        if (newFolder == null) {
1016d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal            return;
1017d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal        }
1018d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal        // If the previous folder was null, or if the two folders represent different data, then we
1019d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal        // consider that the folder has changed.
1020259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy        if (mFolder == null || !newFolder.equals(mFolder)) {
1021d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal            mFolderChanged = true;
1022d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal        }
1023d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal    }
1024d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal
1025d00229d30e3e24e0a40fb75e7500c15f5ff6138bVikram Aggarwal    /**
102669b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * Sets the current folder if it is different from the object provided here. This method does
102769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * NOT notify the folder observers that a change has happened. Observers are notified when we
102869b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * get an updated folder from the loaders, which will happen as a consequence of this method
102969b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * (since this method starts/restarts the loaders).
103069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * @param folder The folder to assign
103169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     */
103211e3596e8c5978d07195ae2d7c8a96bb51aa75b3Mindy Pereira    private void updateFolder(Folder folder) {
10331672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        if (folder == null || !folder.isInitialized()) {
10341672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal            LogUtils.e(LOG_TAG, new Error(), "AAC.setFolder(%s): Bad input", folder);
10351672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal            return;
10361672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        }
10371672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        if (folder.equals(mFolder)) {
10381672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal            LogUtils.d(LOG_TAG, "AAC.setFolder(%s): Input matches mFolder", folder);
10391672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal            return;
10401672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        }
10411672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        final boolean wasNull = mFolder == null;
10421672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        LogUtils.d(LOG_TAG, "AbstractActivityController.setFolder(%s)", folder.name);
10431672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        final LoaderManager lm = mActivity.getLoaderManager();
10441672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        // updateFolder is called from AAC.onLoadFinished() on folder changes.  We need to
10451672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        // ensure that the folder is different from the previous folder before marking the
10461672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        // folder changed.
10471672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        setHasFolderChanged(folder);
10481672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        mFolder = folder;
10491672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal
10501672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        // We do not need to notify folder observers yet. Instead we start the loaders and
10511672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        // when the load finishes, we will get an updated folder. Then, we notify the
10521672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        // folderObservers in onLoadFinished.
10532d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein        mActionBarController.setFolder(mFolder);
10541672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal
10551672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        // Only when we switch from one folder to another do we want to restart the
10561672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        // folder and conversation list loaders (to trigger onCreateLoader).
10571672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        // The first time this runs when the activity is [re-]initialized, we want to re-use the
10581672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        // previous loader's instance and data upon configuration change (e.g. rotation).
10591672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        // If there was not already an instance of the loader, init it.
10601672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        if (lm.getLoader(LOADER_FOLDER_CURSOR) == null) {
1061177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            lm.initLoader(LOADER_FOLDER_CURSOR, Bundle.EMPTY, mFolderCallbacks);
10621672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        } else {
1063177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            lm.restartLoader(LOADER_FOLDER_CURSOR, Bundle.EMPTY, mFolderCallbacks);
10641672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal        }
1065a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal        if (!wasNull && lm.getLoader(LOADER_CONVERSATION_LIST) != null) {
1066a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal            // If there was an existing folder AND we have changed
10671672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal            // folders, we want to restart the loader to get the information
10681672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal            // for the newly selected folder
10691672ff8ed6741df5c245e55f7a4e847f6ccb8c52Vikram Aggarwal            lm.destroyLoader(LOADER_CONVERSATION_LIST);
10705cb0c3e684150d6d8ed4d9df4fb73a7606086aaaMindy Pereira        }
1071e2be97a58fbae98b95e51735c6468d121e465355Paul Westbrook        final Bundle args = new Bundle(2);
10722dc850446320d6b56b89dda7fefc5113c6b7d8d1Vikram Aggarwal        args.putParcelable(BUNDLE_ACCOUNT_KEY, mAccount);
1073a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal        args.putParcelable(BUNDLE_FOLDER_KEY, mFolder);
10745bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein        args.putBoolean(BUNDLE_IGNORE_INITIAL_CONVERSATION_LIMIT_KEY,
10755bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein                mIgnoreInitialConversationLimit);
10765bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein        mIgnoreInitialConversationLimit = false;
1077a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal        lm.initLoader(LOADER_CONVERSATION_LIST, args, mListCursorCallbacks);
10785cb0c3e684150d6d8ed4d9df4fb73a7606086aaaMindy Pereira    }
10795cb0c3e684150d6d8ed4d9df4fb73a7606086aaaMindy Pereira
1080a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    @Override
1081090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    public Folder getFolder() {
1082090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang        return mFolder;
1083090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    }
1084090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang
1085090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    @Override
1086c975e8416b64a1ebdec6adee7e65dd1d0928e8d4Mindy Pereira    public Folder getHierarchyFolder() {
1087c975e8416b64a1ebdec6adee7e65dd1d0928e8d4Mindy Pereira        return mFolderListFolder;
1088c975e8416b64a1ebdec6adee7e65dd1d0928e8d4Mindy Pereira    }
1089c975e8416b64a1ebdec6adee7e65dd1d0928e8d4Mindy Pereira
1090405a344937675f57fc9c6988b2b124410a270f13Jin Cao    /**
1091405a344937675f57fc9c6988b2b124410a270f13Jin Cao     * Set the folder currently selected in the folder selection hierarchy fragments.
1092405a344937675f57fc9c6988b2b124410a270f13Jin Cao     */
1093405a344937675f57fc9c6988b2b124410a270f13Jin Cao    protected void setHierarchyFolder(Folder folder) {
1094c975e8416b64a1ebdec6adee7e65dd1d0928e8d4Mindy Pereira        mFolderListFolder = folder;
109523aadfd1c83e08b6723d4cb9357b2908cc464a86Mindy Pereira    }
109623aadfd1c83e08b6723d4cb9357b2908cc464a86Mindy Pereira
10976aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal    /**
10986aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     * The mail activity calls other activities for two specific reasons:
10996aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     * <ul>
11006aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     <li>To add an account. And receives the result {@link #ADD_ACCOUNT_REQUEST_CODE}</li>
11016aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     <li>To update the password on a current account. The result {@link
11026aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     #REAUTHENTICATE_REQUEST_CODE} is received.</li>
11036aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     * </ul>
11046aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     * @param requestCode
11056aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     * @param resultCode
11066aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     * @param data
11076aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     */
110823aadfd1c83e08b6723d4cb9357b2908cc464a86Mindy Pereira    @Override
11091ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    public void onActivityResult(int requestCode, int resultCode, Intent data) {
1110122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook        switch (requestCode) {
1111122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook            case ADD_ACCOUNT_REQUEST_CODE:
1112122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                // We were waiting for the user to create an account
1113122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                if (resultCode == Activity.RESULT_OK) {
1114122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                    // restart the loader to get the updated list of accounts
1115177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    mActivity.getLoaderManager().initLoader(LOADER_ACCOUNT_CURSOR, Bundle.EMPTY,
1116177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            mAccountCallbacks);
1117122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                } else {
1118122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                    // The user failed to create an account, just exit the app
1119122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                    mActivity.finish();
1120122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                }
1121122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                break;
1122122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook            case REAUTHENTICATE_REQUEST_CODE:
1123122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                if (resultCode == Activity.RESULT_OK) {
1124122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                    // The user successfully authenticated, attempt to refresh the list
1125122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                    final Uri refreshUri = mFolder != null ? mFolder.refreshUri : null;
1126122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                    if (refreshUri != null) {
1127122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                        startAsyncRefreshTask(refreshUri);
1128122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                    }
1129122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                }
1130122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                break;
1131371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon            case CHANGE_NAVIGATION_REQUEST_CODE:
1132c6801eb828627c37b8992584767c095dfe11df62Jin Cao                if (ViewMode.isSearchMode(mViewMode.getMode())) {
1133c6801eb828627c37b8992584767c095dfe11df62Jin Cao                    mActivity.setResult(resultCode, data);
1134c6801eb828627c37b8992584767c095dfe11df62Jin Cao                    mActivity.finish();
1135c6801eb828627c37b8992584767c095dfe11df62Jin Cao                } else if (resultCode == Activity.RESULT_OK && data != null) {
1136371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                    // We have have received a result that indicates we need to navigate to a
1137371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                    // different folder or account. This happens if someone navigates using the
1138371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                    // drawer on the search results activity.
1139371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                    final Folder folder = data.getParcelableExtra(EXTRA_FOLDER);
1140371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                    final Account account = data.getParcelableExtra(EXTRA_ACCOUNT);
1141371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                    if (folder != null) {
1142371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                        onFolderSelected(folder);
1143371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                        mViewMode.enterConversationListMode();
1144371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                    } else if (account != null) {
1145371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                        switchToDefaultInboxOrChangeAccount(account);
1146371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                        mViewMode.enterConversationListMode();
1147371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                    }
1148371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                }
1149371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                break;
1150406ae08b92f78f26fe83830897570c9db1ab23aaGreg Bullock            case VOICE_SEARCH_REQUEST_CODE:
1151c6801eb828627c37b8992584767c095dfe11df62Jin Cao                if (resultCode == Activity.RESULT_OK) {
1152c6801eb828627c37b8992584767c095dfe11df62Jin Cao                    final ArrayList<String> matches = data.getStringArrayListExtra(
1153c6801eb828627c37b8992584767c095dfe11df62Jin Cao                            RecognizerIntent.EXTRA_RESULTS);
1154c6801eb828627c37b8992584767c095dfe11df62Jin Cao                    if (!matches.isEmpty()) {
1155c6801eb828627c37b8992584767c095dfe11df62Jin Cao                        // not sure how dependable the API is, but it's all we have.
1156c6801eb828627c37b8992584767c095dfe11df62Jin Cao                        // take the top choice.
1157c6801eb828627c37b8992584767c095dfe11df62Jin Cao                        mSearchViewController.onSearchPerformed(matches.get(0));
1158c6801eb828627c37b8992584767c095dfe11df62Jin Cao                    }
1159c6801eb828627c37b8992584767c095dfe11df62Jin Cao                }
1160c6801eb828627c37b8992584767c095dfe11df62Jin Cao                break;
11612388c5d208acd4e4e658aaacbbc5f080ee8e9f7cPaul Westbrook        }
1162a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    }
1163a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal
116469b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal    /**
116569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     * Inform the conversation cursor that there has been a visibility change.
1166715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @param visible true if the conversation list is visible, false otherwise.
116769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal     */
116869b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal    protected synchronized void informCursorVisiblity(boolean visible) {
11699f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        if (mConversationListCursor != null) {
117069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            Utils.setConversationCursorVisibility(mConversationListCursor, visible, mFolderChanged);
117169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            // We have informed the cursor. Subsequent visibility changes should not tell it that
117269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            // the folder has changed.
117369b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            mFolderChanged = false;
11749f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        }
1175a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    }
1176a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal
117769b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal    @Override
117869b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal    public void onConversationListVisibilityChanged(boolean visible) {
11794a0456bf96896de1dcd1e655dbdc0cd96cbe3d33Jin Cao        mFloatingComposeButton.setVisibility(
11804a0456bf96896de1dcd1e655dbdc0cd96cbe3d33Jin Cao                !ViewMode.isSearchMode(mViewMode.getMode()) && visible ? View.VISIBLE : View.GONE);
118192ae766a1eb3182efe5225af224c6ddd7be40931Andy Huang
118269b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        informCursorVisiblity(visible);
1183c94d07f4460d00db898237603de311d226707a96Andy Huang        commitAutoAdvanceOperation();
118432ddb84f2d4f53bfc4b53656461068c0c69d6325Scott Kennedy
118532ddb84f2d4f53bfc4b53656461068c0c69d6325Scott Kennedy        // Notify special views
118632ddb84f2d4f53bfc4b53656461068c0c69d6325Scott Kennedy        final ConversationListFragment convListFragment = getConversationListFragment();
118732ddb84f2d4f53bfc4b53656461068c0c69d6325Scott Kennedy        if (convListFragment != null && convListFragment.getAnimatedAdapter() != null) {
118832ddb84f2d4f53bfc4b53656461068c0c69d6325Scott Kennedy            convListFragment.getAnimatedAdapter().onConversationListVisibilityChanged(visible);
118932ddb84f2d4f53bfc4b53656461068c0c69d6325Scott Kennedy        }
119069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal    }
119169b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal
11921ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    /**
11937dd054e39986de84a213c56a3c11ac94731402e6Vikram Aggarwal     * Called when a conversation is visible. Child classes must call the super class implementation
11947dd054e39986de84a213c56a3c11ac94731402e6Vikram Aggarwal     * before performing local computation.
11951ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal     */
11961ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    @Override
11971ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    public void onConversationVisibilityChanged(boolean visible) {
1198c94d07f4460d00db898237603de311d226707a96Andy Huang        commitAutoAdvanceOperation();
1199c94d07f4460d00db898237603de311d226707a96Andy Huang    }
1200c94d07f4460d00db898237603de311d226707a96Andy Huang
1201c94d07f4460d00db898237603de311d226707a96Andy Huang    /**
1202c94d07f4460d00db898237603de311d226707a96Andy Huang     * Commits any pending destructive action that was earlier deferred by an auto-advance
1203c94d07f4460d00db898237603de311d226707a96Andy Huang     * mode-change transition.
1204c94d07f4460d00db898237603de311d226707a96Andy Huang     */
1205c94d07f4460d00db898237603de311d226707a96Andy Huang    private void commitAutoAdvanceOperation() {
1206c94d07f4460d00db898237603de311d226707a96Andy Huang        if (mAutoAdvanceOp != null) {
1207c94d07f4460d00db898237603de311d226707a96Andy Huang            mAutoAdvanceOp.run();
1208c94d07f4460d00db898237603de311d226707a96Andy Huang            mAutoAdvanceOp = null;
1209c94d07f4460d00db898237603de311d226707a96Andy Huang        }
12101ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    }
12111ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal
121259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal    /**
121359f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal     * Initialize development time logging. This can potentially log a lot of PII, and we don't want
121459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal     * to turn it on for shipped versions.
121559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal     */
121659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal    private void initializeDevLoggingService() {
121759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal        if (!MailLogService.DEBUG_ENABLED) {
121859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal            return;
121959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal        }
122059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal        // Check every 5 minutes.
122159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal        final int WAIT_TIME = 5 * 60 * 1000;
122259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal        // Start a runnable that periodically checks the log level and starts/stops the service.
122359f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal        mLogServiceChecker = new Runnable() {
122459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal            /** True if currently logging. */
122559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal            private boolean mCurrentlyLogging = false;
122659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal
122759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal            /**
122859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal             * If the logging level has been changed since the previous run, start or stop the
122959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal             * service.
123059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal             */
123159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal            private void startOrStopService() {
123259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                // If the log level is already high, start the service.
123359f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                final Intent i = new Intent(mContext, MailLogService.class);
123459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                final boolean loggingEnabled = MailLogService.isLoggingLevelHighEnough();
123559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                if (mCurrentlyLogging == loggingEnabled) {
123659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                    // No change since previous run, just return;
123759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                    return;
123859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                }
123959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                if (loggingEnabled) {
124059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                    LogUtils.e(LOG_TAG, "Starting MailLogService");
124159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                    mContext.startService(i);
124259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                } else {
124359f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                    LogUtils.e(LOG_TAG, "Stopping MailLogService");
124459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                    mContext.stopService(i);
124559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                }
124659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                mCurrentlyLogging = loggingEnabled;
124759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal            }
124859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal
124959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal            @Override
125059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal            public void run() {
125159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                startOrStopService();
125259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal                mHandler.postDelayed(this, WAIT_TIME);
125359f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal            }
125459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal        };
125559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal        // Start the runnable right away.
125659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal        mHandler.post(mLogServiceChecker);
125759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal    }
125859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal
12596aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal    /**
12606aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     * The application can be started from the following entry points:
12616aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     * <ul>
12626aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     <li>Launcher: you tap on the Gmail icon in the launcher. This is what most users think of
12636aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *         as “Starting the app”.</li>
12646aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     <li>Shortcut: Users can make a shortcut to take them directly to a label.</li>
12656aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     <li>Widget: Shows the contents of a synced label, and allows:
12666aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     <ul>
12676aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *         <li>Viewing the list (tapping on the title)</li>
12686aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *         <li>Composing a new message (tapping on the new message icon in the title. This
12696aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *         launches the {@link ComposeActivity}.
12706aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *         </li>
12716aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *         <li>Viewing a single message (tapping on a list element)</li>
12726aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     </ul>
12736aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *
12746aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     </li>
12756aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     <li>Tapping on a notification:
12766aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     <ul>
12776aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *         <li>Shows message list if more than one message</li>
12786aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *         <li>Shows the conversation if the notification is for a single message</li>
12796aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     </ul>
12806aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     </li>
12816aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     <li>...and most importantly, the activity life cycle can tear down the application and
12826aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     restart it:
12836aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     <ul>
12846aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *         <li>Rotate the application: it is destroyed and recreated.</li>
12856aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *         <li>Navigate away, and return from recent applications.</li>
12866aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     </ul>
12876aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     </li>
12886aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     <li>Add a new account: fires off an intent to add an account,
12896aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     and returns in {@link #onActivityResult(int, int, android.content.Intent)} .</li>
12906aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     <li>Re-authenticate your account: again returns in onActivityResult().</li>
12916aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     <li>Composing can happen from many entry points: third party applications fire off an
12926aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     intent to compose email, and launch directly into the {@link ComposeActivity}
12936aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *     .</li>
12946aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     * </ul>
12956aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     * {@inheritDoc}
12966aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     */
12971ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    @Override
129887a8982af83168453b6dc219fe984bff68047bebAndy Huang    public void onCreate(Bundle savedState) {
1299abd24d82e41fd4747945d3f124b4e3a57e5dc6e6Vikram Aggarwal        initializeActionBar();
130059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal        initializeDevLoggingService();
13011ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal        // Allow shortcut keys to function for the ActionBar and menus.
13021ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal        mActivity.setDefaultKeyMode(Activity.DEFAULT_KEYS_SHORTCUT);
130380aeac5356b5a14a5ef2aff8f0f72a464c9a3cb4Vikram Aggarwal        mResolver = mActivity.getContentResolver();
13046ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook        mNewEmailReceiver = new SuppressNotificationReceiver();
13057c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal        mRecentFolderList.initialize(mActivity);
130669a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal        mVeiledMatcher.initialize(this);
13076ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook
13081a1e9754cce66296ae730f2c483f55fcf1a24359James Lemieux        mFloatingComposeButton = mActivity.findViewById(R.id.compose_button);
13091a1e9754cce66296ae730f2c483f55fcf1a24359James Lemieux        mFloatingComposeButton.setOnClickListener(this);
13105d79a913c8289de4ffc1b662b9bea967a7e65b0dJames Lemieux
1311f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang        if (isDrawerEnabled()) {
1312355dd90d120bc105f5ad37aa5a5c7449e665a566Andrew Sapperstein            mDrawerToggle = new ActionBarDrawerToggle(mActivity, mDrawerContainer,
1313355dd90d120bc105f5ad37aa5a5c7449e665a566Andrew Sapperstein                    R.string.drawer_open, R.string.drawer_close);
1314f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang            mDrawerContainer.setDrawerListener(mDrawerListener);
1315f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang            mDrawerContainer.setDrawerShadow(
1316f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang                    mContext.getResources().getDrawable(R.drawable.drawer_shadow), Gravity.START);
131712b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang
1318207e662df01c550a4894bec03219820528365508Rohan Shah            // Disable default drawer indicator as we are setting the drawer indicator icons.
1319207e662df01c550a4894bec03219820528365508Rohan Shah            // TODO(shahrk): Once we can disable/enable drawer animation, go back to using
1320207e662df01c550a4894bec03219820528365508Rohan Shah            // drawer indicators.
1321207e662df01c550a4894bec03219820528365508Rohan Shah            mDrawerToggle.setDrawerIndicatorEnabled(false);
1322375faf0f8cf1d1de1d4aca7b9a7598e168dc9bf3Rohan Shah            mDrawerToggle.setHomeAsUpIndicator(R.drawable.ic_menu_wht_24dp);
1323f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang        } else {
132452882ff08fe9420fe2dbb3f681545a330222e9c8Andrew Sapperstein            final ActionBar ab = mActivity.getSupportActionBar();
1325375faf0f8cf1d1de1d4aca7b9a7598e168dc9bf3Rohan Shah            ab.setHomeAsUpIndicator(R.drawable.ic_menu_wht_24dp);
1326f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang            ab.setHomeActionContentDescription(R.string.drawer_open);
1327f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang            ab.setDisplayHomeAsUpEnabled(true);
1328f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang        }
132912b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang
1330161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira        // All the individual UI components listen for ViewMode changes. This
1331b849dfbda3c917f078a5c09f03906886c27ecbecMindy Pereira        // simplifies the amount of logic in the AbstractActivityController, but increases the
1332b849dfbda3c917f078a5c09f03906886c27ecbecMindy Pereira        // possibility of timing-related bugs.
13331ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal        mViewMode.addListener(this);
1334632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        mPagerController = new ConversationPagerController(mActivity, this);
13359a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux        mToastBar = findActionableToastBar(mActivity);
1336ada517849ada8a3c3a62d81c5163f392bc674eb7Vikram Aggarwal        attachActionBar();
13371ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal
1338144bfe739b93afdee0a1700a34806b0b287e5887Andy Huang        mDrawIdler.setRootView(mActivity.getWindow().getDecorView());
1339144bfe739b93afdee0a1700a34806b0b287e5887Andy Huang
1340632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        final Intent intent = mActivity.getIntent();
13414fe0af81874976a1995191321e35c844b2229811Andy Huang
1342c6801eb828627c37b8992584767c095dfe11df62Jin Cao        mSearchViewController = new MaterialSearchViewController(mActivity, this, intent,
1343c6801eb828627c37b8992584767c095dfe11df62Jin Cao                savedState);
1344524ded5f9d27dda75c53d256922a39c867857575Jin Cao        addConversationListLayoutListener(mSearchViewController);
1345c6801eb828627c37b8992584767c095dfe11df62Jin Cao
1346abd24d82e41fd4747945d3f124b4e3a57e5dc6e6Vikram Aggarwal        // Immediately handle a clean launch with intent, and any state restoration
1347632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        // that does not rely on restored fragments or loader data
1348632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        // any state restoration that relies on those can be done later in
1349632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        // onRestoreInstanceState, once fragments are up and loader data is re-delivered
1350632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        if (savedState != null) {
1351632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            if (savedState.containsKey(SAVED_ACCOUNT)) {
1352632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                setAccount((Account) savedState.getParcelable(SAVED_ACCOUNT));
1353632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            }
1354632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            if (savedState.containsKey(SAVED_FOLDER)) {
13552bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang                final Folder folder = savedState.getParcelable(SAVED_FOLDER);
1356203dc00aff8e890000ee0c9587081e0968fdae8dVikram Aggarwal                final String query = savedState.getString(SAVED_QUERY, null);
1357649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal                setListContext(folder, query);
1358632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            }
13596cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal            if (savedState.containsKey(SAVED_ACTION)) {
13606cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal                mDialogAction = savedState.getInt(SAVED_ACTION);
13616cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal            }
1362a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal            mDialogFromSelectedSet = savedState.getBoolean(SAVED_ACTION_FROM_SELECTED, false);
1363649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal            mViewMode.handleRestore(savedState);
1364632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        } else if (intent != null) {
1365632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            handleIntent(intent);
1366632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
1367632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        // Create the accounts loader; this loads the account switch spinner.
1368177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        mActivity.getLoaderManager().initLoader(LOADER_ACCOUNT_CURSOR, Bundle.EMPTY,
1369177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                mAccountCallbacks);
1370b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang    }
1371b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang
13729a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux    /**
13739a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux     * @param activity the activity that has been inflated
13749a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux     * @return the Actionable Toast Bar defined within the activity
13759a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux     */
13769a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux    protected ActionableToastBar findActionableToastBar(MailActivity activity) {
13779a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux        return (ActionableToastBar) activity.findViewById(R.id.toast_bar);
13789a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux    }
13799a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux
1380b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang    @Override
138157246a4bf94e8b440c0597fa4a8e4f4e8a46532aPaul Westbrook    public void onPostCreate(Bundle savedState) {
1382f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang        if (!isDrawerEnabled()) {
1383f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang            return;
1384f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang        }
138512b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        // Sync the toggle state after onRestoreInstanceState has occurred.
138612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        mDrawerToggle.syncState();
13875747e152fa606c20261b18431f4a9f0ff3be7b5aAndrew Sapperstein
1388d70fe49cabf896e87574bcb2e57e8a32608537beVikram Aggarwal        mHideMenuItems = isDrawerEnabled() && mDrawerContainer.isDrawerOpen(mDrawerPullout);
138957246a4bf94e8b440c0597fa4a8e4f4e8a46532aPaul Westbrook    }
139057246a4bf94e8b440c0597fa4a8e4f4e8a46532aPaul Westbrook
139157246a4bf94e8b440c0597fa4a8e4f4e8a46532aPaul Westbrook    @Override
139257246a4bf94e8b440c0597fa4a8e4f4e8a46532aPaul Westbrook    public void onConfigurationChanged(Configuration newConfig) {
1393f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang        if (isDrawerEnabled()) {
1394f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang            mDrawerToggle.onConfigurationChanged(newConfig);
1395f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang        }
139612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang    }
139712b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang
139812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang    /**
13995d79a913c8289de4ffc1b662b9bea967a7e65b0dJames Lemieux     * This controller listens for clicks on items in the floating action bar.
14005d79a913c8289de4ffc1b662b9bea967a7e65b0dJames Lemieux     *
14015d79a913c8289de4ffc1b662b9bea967a7e65b0dJames Lemieux     * @param view the item that was clicked in the floating action bar
14025d79a913c8289de4ffc1b662b9bea967a7e65b0dJames Lemieux     */
14035d79a913c8289de4ffc1b662b9bea967a7e65b0dJames Lemieux    @Override
14045d79a913c8289de4ffc1b662b9bea967a7e65b0dJames Lemieux    public void onClick(View view) {
140553de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein        final int viewId = view.getId();
140653de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein        if (viewId == R.id.compose_button) {
140753de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein            ComposeActivity.compose(mActivity.getActivityContext(), getAccount());
140853de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein        } else if (viewId == android.R.id.home) {
140953de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein            // TODO: b/16627877
1410405a344937675f57fc9c6988b2b124410a270f13Jin Cao            handleUpPress();
141153de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein        }
14125d79a913c8289de4ffc1b662b9bea967a7e65b0dJames Lemieux    }
14135d79a913c8289de4ffc1b662b9bea967a7e65b0dJames Lemieux
14145d79a913c8289de4ffc1b662b9bea967a7e65b0dJames Lemieux    /**
141512b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang     * If drawer is open/visible (even partially), close it.
141612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang     */
141712b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang    protected void closeDrawerIfOpen() {
141812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        if (!isDrawerEnabled()) {
141912b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            return;
142012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
142112b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        if(mDrawerContainer.isDrawerOpen(mDrawerPullout)) {
142212b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            mDrawerContainer.closeDrawers();
142312b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
142457246a4bf94e8b440c0597fa4a8e4f4e8a46532aPaul Westbrook    }
142557246a4bf94e8b440c0597fa4a8e4f4e8a46532aPaul Westbrook
142657246a4bf94e8b440c0597fa4a8e4f4e8a46532aPaul Westbrook    @Override
14271ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    public void onStart() {
14281ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        mSafeToModifyFragments = true;
1429cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedy
1430cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedy        NotificationActionUtils.registerUndoNotificationObserver(mUndoNotificationObserver);
1431761522c1ac7a8fa05acde714f0e26fde47f3415fAndy Huang
1432761522c1ac7a8fa05acde714f0e26fde47f3415fAndy Huang        if (mViewMode.getMode() != ViewMode.UNKNOWN) {
1433761522c1ac7a8fa05acde714f0e26fde47f3415fAndy Huang            Analytics.getInstance().sendView("MainActivity" + mViewMode.toString());
1434761522c1ac7a8fa05acde714f0e26fde47f3415fAndy Huang        }
14351ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    }
14361ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang
14371ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    @Override
143800179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein    public void onRestart() {
1439177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        final DialogFragment fragment = (DialogFragment)
144000179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                mFragmentManager.findFragmentByTag(SYNC_ERROR_DIALOG_FRAGMENT_TAG);
144100179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein        if (fragment != null) {
144200179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein            fragment.dismiss();
144300179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein        }
1444ea04f938bef8e2ec7c6f64067511f006681318fbmindyp        // When the user places the app in the background by pressing "home",
1445ea04f938bef8e2ec7c6f64067511f006681318fbmindyp        // dismiss the toast bar. However, since there is no way to determine if
1446ea04f938bef8e2ec7c6f64067511f006681318fbmindyp        // home was pressed, just dismiss any existing toast bar when restarting
1447ea04f938bef8e2ec7c6f64067511f006681318fbmindyp        // the app.
1448ea04f938bef8e2ec7c6f64067511f006681318fbmindyp        if (mToastBar != null) {
1449f8ccdcfc26f362d9c20771e2295a0c0e8d1195f3Andrew Sapperstein            mToastBar.hide(false, false /* actionClicked */);
1450ea04f938bef8e2ec7c6f64067511f006681318fbmindyp        }
145100179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein    }
145200179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein
145300179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein    @Override
14541ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    public Dialog onCreateDialog(int id, Bundle bundle) {
14551ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal        return null;
14561ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    }
14571ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal
1458a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    @Override
14594eb5271e0f0a48ea624e048eb69fbe86d6c62dc3Vikram Aggarwal    public final boolean onCreateOptionsMenu(Menu menu) {
14600fa9efd6df21a92866908ab679a78b7a128bc28fAndrew Sapperstein        if (mViewMode.isAdMode()) {
14610fa9efd6df21a92866908ab679a78b7a128bc28fAndrew Sapperstein            return false;
14620fa9efd6df21a92866908ab679a78b7a128bc28fAndrew Sapperstein        }
1463e5e917c10cbc4afd31358c0425954e2c27e0fb8eVikram Aggarwal        final MenuInflater inflater = mActivity.getMenuInflater();
14642d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein        inflater.inflate(mActionBarController.getOptionsMenuId(), menu);
14652d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein        mActionBarController.onCreateOptionsMenu(menu);
146628d5f72e0ad0db75b6243ee8125bfe3aadbdcb85Mindy Pereira        return true;
1467a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    }
1468a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal
1469a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    @Override
14704eb5271e0f0a48ea624e048eb69fbe86d6c62dc3Vikram Aggarwal    public final boolean onKeyDown(int keyCode, KeyEvent event) {
1471b9e1a353c6a173a2885642dbcc1939f3281f29f7Vikram Aggarwal        return false;
14721ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    }
14731ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal
147417a8e78d61bff414610e23f24889bab29955f54emindyp    public abstract boolean doesActionChangeConversationListVisibility(int action);
147517a8e78d61bff414610e23f24889bab29955f54emindyp
147630c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao    /**
147730c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao     * Helper function that determines if we should associate an undo callback with
147830c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao     * the current menu action item
147930c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao     * @param actionId the id of the action
148030c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao     * @return the appropriate callback handler, or null if not applicable
148130c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao     */
148230c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao    private UndoCallback getUndoCallbackForDestructiveActionsWithAutoAdvance(
148330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            int actionId, final Conversation conv) {
148430c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        // We associated the undoCallback if the user is going to perform an action on the current
148530c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        // conversation, causing the current conversation to be removed from view and replacing it
148630c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        // with another (via Auto Advance). The undoCallback will bring the removed conversation
148730c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        // back into the view if the action is undone.
148830c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        final Collection<Conversation> convCol = Conversation.listOf(conv);
148930c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        final boolean isApplicableForReshow = mAccount != null &&
149030c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                mAccount.settings != null &&
149130c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                mTracker != null &&
149230c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                // ensure that we will show another conversation due to Auto Advance
149330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                mTracker.getNextConversation(
149430c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                        mAccount.settings.getAutoAdvanceSetting(), convCol) != null &&
149530c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                // ensure that we are performing the action from conversation view
149630c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                isCurrentConversationInView(convCol) &&
149730c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                // check for the appropriate destructive actions
149830c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                doesActionRemoveCurrentConversationFromView(actionId);
149930c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        return (isApplicableForReshow) ?
150030c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            new UndoCallback() {
150130c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                @Override
150230c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                public void performUndoCallback() {
150330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                    showConversation(conv);
150430c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                }
150530c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            } : null;
150630c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao    }
150730c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao
150830c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao    /**
150930c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao     * Check if the provided action will remove the active conversation from view
151030c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao     * @param actionId the applied action
151130c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao     * @return true if it will remove the conversation from view, false otherwise
151230c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao     */
151330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao    private boolean doesActionRemoveCurrentConversationFromView(int actionId) {
151430c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        return actionId == R.id.archive ||
151530c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                actionId == R.id.delete ||
1516512821c11d89d49908f3cfdee0b582601f500f3dJin Cao                actionId == R.id.discard_outbox ||
151730c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                actionId == R.id.remove_folder ||
151830c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                actionId == R.id.report_spam ||
151930c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                actionId == R.id.report_phishing ||
152030c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                actionId == R.id.move_to;
152130c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao    }
152230c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao
15231ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    @Override
152457246a4bf94e8b440c0597fa4a8e4f4e8a46532aPaul Westbrook    public boolean onOptionsItemSelected(MenuItem item) {
1525761522c1ac7a8fa05acde714f0e26fde47f3415fAndy Huang
152612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        /*
152712b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         * The action bar home/up action should open or close the drawer.
152812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         * mDrawerToggle will take care of this.
152912b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         */
1530f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang        if (isDrawerEnabled() && mDrawerToggle.onOptionsItemSelected(item)) {
1531042a530b2296487fa5899a3e871214ac4a47e3d8Andy Huang            Analytics.getInstance().sendEvent(Analytics.EVENT_CATEGORY_MENU_ITEM, "drawer_toggle",
1532042a530b2296487fa5899a3e871214ac4a47e3d8Andy Huang                    null, 0);
153312b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            return true;
153412b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
153512b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang
15362b55549d4fbe25e91f673a0727fc89ff755d9327Andy Huang        Analytics.getInstance().sendMenuItemEvent(Analytics.EVENT_CATEGORY_MENU_ITEM,
1537f8c59b0198bd3230ef669c5e258b86443e7ed682Andy Huang                item.getItemId(), "action_bar/" + mViewMode.getModeString(), 0);
1538042a530b2296487fa5899a3e871214ac4a47e3d8Andy Huang
153954452ae7a9cdb607ed1db5e74077b5ebd95c46a2Vikram Aggarwal        final int id = item.getItemId();
15407dd054e39986de84a213c56a3c11ac94731402e6Vikram Aggarwal        LogUtils.d(LOG_TAG, "AbstractController.onOptionsItemSelected(%d) called.", id);
154184fe99431df3b989dff9b363562f09fd2757b63cVikram Aggarwal        /** This is NOT a batch action. */
154284fe99431df3b989dff9b363562f09fd2757b63cVikram Aggarwal        final boolean isBatch = false;
1543ba68fdaf4f251eccba0832878a74bdaf6aa7469eMindy Pereira        final Collection<Conversation> target = Conversation.listOf(mCurrentConversation);
1544112cd16335e1ca56c96d005aa3b5bb11508396dcVikram Aggarwal        final Settings settings = (mAccount == null) ? null : mAccount.settings;
154584f7d32bdc79263004ed5241480988e02f8e618cmindyp        // The user is choosing a new action; commit whatever they had been
154617a8e78d61bff414610e23f24889bab29955f54emindyp        // doing before. Don't animate if we are launching a new screen.
154717a8e78d61bff414610e23f24889bab29955f54emindyp        commitDestructiveActions(!doesActionChangeConversationListVisibility(id));
154830c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        final UndoCallback undoCallback = getUndoCallbackForDestructiveActionsWithAutoAdvance(
154930c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                id, mCurrentConversation);
155030c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao
15512d7d1eeb7ac15f09b7129860179bf5de62a85009Jin Cao        // Menu items that are targetted, only perform if there actually is a target and the
15522d7d1eeb7ac15f09b7129860179bf5de62a85009Jin Cao        // cursor is showing the target in the list.
1553a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao        boolean handled = false;
15542d7d1eeb7ac15f09b7129860179bf5de62a85009Jin Cao        if (target.size() > 0 &&
15552d7d1eeb7ac15f09b7129860179bf5de62a85009Jin Cao                ConversationCursor.isCursorReadyToShow(getConversationListCursor())) {
1556a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao            handled = true;
1557a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao            if (id == R.id.archive) {
1558a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                final boolean showDialog = (settings != null && settings.confirmArchive);
1559a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                confirmAndDelete(id, target, showDialog, R.plurals.confirm_archive_conversation,
1560a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                        undoCallback);
1561a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao            } else if (id == R.id.remove_folder) {
1562a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                delete(R.id.remove_folder, target,
1563a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                        getDeferredRemoveFolder(target, mFolder, true, isBatch, true, undoCallback),
156430c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                        isBatch);
1565a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao            } else if (id == R.id.delete) {
1566a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                final boolean showDialog = (settings != null && settings.confirmDelete);
1567a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                confirmAndDelete(id, target, showDialog, R.plurals.confirm_delete_conversation,
1568a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                        undoCallback);
1569a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao            } else if (id == R.id.discard_drafts) {
1570a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                // drafts are lost forever, so always confirm
1571a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                confirmAndDelete(id, target, true /* showDialog */,
1572a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                        R.plurals.confirm_discard_drafts_conversation, undoCallback);
1573a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao            } else if (id == R.id.discard_outbox) {
1574a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                // discard in outbox means we discard the failed message and save them in drafts
1575a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                delete(id, target, getDeferredAction(id, target, isBatch, undoCallback), isBatch);
1576a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao            } else if (id == R.id.mark_important) {
15772b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                updateConversation(Conversation.listOf(mCurrentConversation),
1578a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                        ConversationColumns.PRIORITY, UIProvider.ConversationPriority.HIGH);
1579a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao            } else if (id == R.id.mark_not_important) {
1580a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                if (mFolder != null && mFolder.isImportantOnly()) {
1581a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                    delete(R.id.mark_not_important, target,
1582a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                            getDeferredAction(R.id.mark_not_important, target, isBatch, undoCallback),
1583a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                            isBatch);
1584a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                } else {
1585a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                    updateConversation(target, ConversationColumns.PRIORITY,
1586a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                            UIProvider.ConversationPriority.LOW);
1587a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                }
1588a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao            } else if (id == R.id.mute) {
1589a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                delete(R.id.mute, target, getDeferredAction(R.id.mute, target, isBatch, undoCallback),
1590a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                        isBatch);
1591a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao            } else if (id == R.id.report_spam) {
1592a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                delete(R.id.report_spam, target,
1593a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                        getDeferredAction(R.id.report_spam, target, isBatch, undoCallback),
1594a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                        isBatch);
1595a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao            } else if (id == R.id.mark_not_spam) {
1596a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                // Currently, since spam messages are only shown in list with
1597a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                // other spam messages,
1598a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                // marking a message not as spam is a destructive action
1599a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                delete(R.id.mark_not_spam, target,
1600a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                        getDeferredAction(R.id.mark_not_spam, target, isBatch, undoCallback),
1601a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                        isBatch);
1602a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao            } else if (id == R.id.report_phishing) {
1603a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                delete(R.id.report_phishing, target,
1604a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                        getDeferredAction(R.id.report_phishing, target, isBatch, undoCallback),
1605a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                        isBatch);
1606a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao            } else if (id == R.id.move_to || id == R.id.change_folders) {
1607a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                final FolderSelectionDialog dialog = FolderSelectionDialog.getInstance(mAccount,
1608a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                        target, isBatch, mFolder, id == R.id.move_to);
1609a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                if (dialog != null) {
1610a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                    dialog.show(mActivity.getFragmentManager(), null);
1611a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                }
1612a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao            } else if (id == R.id.move_to_inbox) {
1613a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                new AsyncTask<Void, Void, Folder>() {
1614a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                    @Override
1615a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                    protected Folder doInBackground(final Void... params) {
1616a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                        // Get the "move to" inbox
1617a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                        return Utils.getFolder(mContext, mAccount.settings.moveToInbox,
1618a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                                true /* allowHidden */);
1619a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                    }
1620a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao
1621a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                    @Override
1622a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                    protected void onPostExecute(final Folder moveToInbox) {
1623a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                        final List<FolderOperation> ops = Lists.newArrayListWithCapacity(1);
1624a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                        // Add inbox
1625a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                        ops.add(new FolderOperation(moveToInbox, true));
1626a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                        assignFolder(ops, target, true, true /* showUndo */, false /* isMoveTo */);
1627a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                    }
1628a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                }.execute((Void[]) null);
1629a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao            } else {
1630a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                handled = false;
1631ba68fdaf4f251eccba0832878a74bdaf6aa7469eMindy Pereira            }
1632a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao        }
1633a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao
1634a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao        // Not handled by the targetted menu items, check the general ones.
1635a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao        if (!handled) {
1636a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao            handled = true;
1637a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao            if (id == android.R.id.home) {
1638a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                handleUpPress();
1639a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao            } else if (id == R.id.compose) {
1640a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                ComposeActivity.compose(mActivity.getActivityContext(), mAccount);
1641a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao            } else if (id == R.id.refresh) {
1642a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                requestFolderRefresh();
1643a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao            } else if (id == R.id.toggle_drawer) {
1644a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                toggleDrawerState();
1645a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao            } else if (id == R.id.settings) {
1646a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                Utils.showSettings(mActivity.getActivityContext(), mAccount);
1647a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao            } else if (id == R.id.help_info_menu_item) {
1648a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                mActivity.showHelp(mAccount, mViewMode.getMode());
1649a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao            } else if (id == R.id.empty_trash) {
1650a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                showEmptyDialog();
1651a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao            } else if (id == R.id.empty_spam) {
1652a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                showEmptyDialog();
1653a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao            } else if (id == R.id.search) {
1654a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                mSearchViewController.showSearchActionBar(
1655a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                        MaterialSearchViewController.SEARCH_VIEW_STATE_VISIBLE);
1656a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao            } else {
1657a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                handled = false;
1658a8e4318bb9921e2ec6045c5f7187a4b78c55fe10Vikram Aggarwal            }
1659a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao        }
1660dd2ec688cf18e0ec132d936168ccaa3c43cdefb1Scott Kennedy
1661a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao        // If the controller didn't handle this event, check the CAB menu if it's active.
1662a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao        // This is necessary because keyboard shortcuts don't seem to check CAB menus.
1663a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao        if (!handled && mCabActionMenu != null && mCabActionMenu.isActivated() &&
1664a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao                    mCabActionMenu.onActionItemClicked(item)) {
1665a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao            handled = true;
166628d5f72e0ad0db75b6243ee8125bfe3aadbdcb85Mindy Pereira        }
1667a6b45218b8d90d7fc3a5ca0901293c71df404553Jin Cao
16689b87568c9e9f1c32a9672b315229866a58a1e757Mindy Pereira        return handled;
1669a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    }
1670a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal
1671ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein    /**
1672ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein     * Opens an {@link EmptyFolderDialogFragment} for the current folder.
1673ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein     */
1674ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein    private void showEmptyDialog() {
1675ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein        if (mFolder != null) {
1676ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein            final EmptyFolderDialogFragment fragment =
1677ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein                    EmptyFolderDialogFragment.newInstance(mFolder.totalCount, mFolder.type);
1678ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein            fragment.setListener(this);
1679ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein            fragment.show(mActivity.getFragmentManager(), EmptyFolderDialogFragment.FRAGMENT_TAG);
1680ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein        }
1681ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein    }
1682ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein
1683ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein    @Override
1684ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein    public void onFolderEmptied() {
1685ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein        emptyFolder();
1686ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein    }
1687ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein
1688ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein    /**
1689ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein     * Performs the work of emptying the currently visible folder.
1690ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein     */
16917ee089ea2ef96e31504e88637b5f3b0969b3c7c1Scott Kennedy    private void emptyFolder() {
16921d0f1f839023e405b24ffc6a670b5c26b36f24e0Yorke Lee        if (mConversationListCursor != null) {
16931d0f1f839023e405b24ffc6a670b5c26b36f24e0Yorke Lee            mConversationListCursor.emptyFolder();
16941d0f1f839023e405b24ffc6a670b5c26b36f24e0Yorke Lee        }
16957ee089ea2ef96e31504e88637b5f3b0969b3c7c1Scott Kennedy    }
16967ee089ea2ef96e31504e88637b5f3b0969b3c7c1Scott Kennedy
1697ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein    private void attachEmptyFolderDialogFragmentListener() {
1698ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein        final EmptyFolderDialogFragment fragment =
1699ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein                (EmptyFolderDialogFragment) mActivity.getFragmentManager()
1700ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein                        .findFragmentByTag(EmptyFolderDialogFragment.FRAGMENT_TAG);
1701ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein
1702ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein        if (fragment != null) {
1703ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein            fragment.setListener(this);
1704ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein        }
1705ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein    }
1706ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein
17078e65c6d2527c7539b689e0e8548c84f690badca5Rohan Shah    /**
170812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang     * Toggles the drawer pullout. If it was open (Fully extended), the
170912b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang     * drawer will be closed. Otherwise, the drawer will be opened. This should
171012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang     * only be called when used with a toggle item. Other cases should be handled
171112b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang     * explicitly with just closeDrawers() or openDrawer(View drawerView);
17128e65c6d2527c7539b689e0e8548c84f690badca5Rohan Shah     */
1713d9eaca8676ad4c6c58122fd41be799872bfd9cdaTony Mantler    protected void toggleDrawerState() {
171412b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        if (!isDrawerEnabled()) {
171512b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            return;
171612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
171712b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        if(mDrawerContainer.isDrawerOpen(mDrawerPullout)) {
171812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            mDrawerContainer.closeDrawers();
171912b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        } else {
172012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            mDrawerContainer.openDrawer(mDrawerPullout);
172112b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
17228e65c6d2527c7539b689e0e8548c84f690badca5Rohan Shah    }
17238e65c6d2527c7539b689e0e8548c84f690badca5Rohan Shah
1724531488ed848766708252a5fdc01c4ecf0fd167d1Vikram Aggarwal    @Override
1725c1fb9a9c2730178105977fca629e80951bfc3cdcAndy Huang    public final boolean onBackPressed() {
172612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        if (isDrawerEnabled() && mDrawerContainer.isDrawerVisible(mDrawerPullout)) {
172712b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            mDrawerContainer.closeDrawers();
172812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            return true;
1729c6801eb828627c37b8992584767c095dfe11df62Jin Cao        } else if (mSearchViewController.handleBackPress()) {
1730c6801eb828627c37b8992584767c095dfe11df62Jin Cao            return true;
17314c92874d9acedc81184339645b24eaeebacbb782Andrew Sapperstein        // If we're in CAB mode, let the activity handle onBackPressed.
17324c92874d9acedc81184339645b24eaeebacbb782Andrew Sapperstein        // It will handle closing CAB mode for us.
17334c92874d9acedc81184339645b24eaeebacbb782Andrew Sapperstein        } else if (mCabActionMenu != null && mCabActionMenu.isActivated()) {
17344c92874d9acedc81184339645b24eaeebacbb782Andrew Sapperstein            return false;
173512b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
173612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang
1737c1fb9a9c2730178105977fca629e80951bfc3cdcAndy Huang        return handleBackPress();
1738c1fb9a9c2730178105977fca629e80951bfc3cdcAndy Huang    }
1739c1fb9a9c2730178105977fca629e80951bfc3cdcAndy Huang
1740c1fb9a9c2730178105977fca629e80951bfc3cdcAndy Huang    protected abstract boolean handleBackPress();
1741ede2e52e725abc625ae963f979639ff8ecc40cf5Greg Bullock
1742c1fb9a9c2730178105977fca629e80951bfc3cdcAndy Huang    protected abstract boolean handleUpPress();
1743c1fb9a9c2730178105977fca629e80951bfc3cdcAndy Huang
1744c1fb9a9c2730178105977fca629e80951bfc3cdcAndy Huang    @Override
17456c2663dc9cade8aff34c462d244e3d72c475c664Mindy Pereira    public void updateConversation(Collection<Conversation> target, ContentValues values) {
17469e2d407fdafeb874e640eb84017feaf784309075Scott Kennedy        mConversationListCursor.updateValues(target, values);
17476c2663dc9cade8aff34c462d244e3d72c475c664Mindy Pereira        refreshConversationList();
17486c2663dc9cade8aff34c462d244e3d72c475c664Mindy Pereira    }
17496c2663dc9cade8aff34c462d244e3d72c475c664Mindy Pereira
17506c2663dc9cade8aff34c462d244e3d72c475c664Mindy Pereira    @Override
1751531488ed848766708252a5fdc01c4ecf0fd167d1Vikram Aggarwal    public void updateConversation(Collection <Conversation> target, String columnName,
1752531488ed848766708252a5fdc01c4ecf0fd167d1Vikram Aggarwal            boolean value) {
17539e2d407fdafeb874e640eb84017feaf784309075Scott Kennedy        mConversationListCursor.updateBoolean(target, columnName, value);
175475daee52f08c9bf66652b9bddf42cfbd1ca71987Vikram Aggarwal        refreshConversationList();
175554452ae7a9cdb607ed1db5e74077b5ebd95c46a2Vikram Aggarwal    }
175654452ae7a9cdb607ed1db5e74077b5ebd95c46a2Vikram Aggarwal
1757531488ed848766708252a5fdc01c4ecf0fd167d1Vikram Aggarwal    @Override
17586c2663dc9cade8aff34c462d244e3d72c475c664Mindy Pereira    public void updateConversation(Collection <Conversation> target, String columnName,
17596c2663dc9cade8aff34c462d244e3d72c475c664Mindy Pereira            int value) {
17609e2d407fdafeb874e640eb84017feaf784309075Scott Kennedy        mConversationListCursor.updateInt(target, columnName, value);
176175daee52f08c9bf66652b9bddf42cfbd1ca71987Vikram Aggarwal        refreshConversationList();
1762c9d59184da271d5a6974edb709e3b39a5a970fa7Mindy Pereira    }
1763c9d59184da271d5a6974edb709e3b39a5a970fa7Mindy Pereira
1764531488ed848766708252a5fdc01c4ecf0fd167d1Vikram Aggarwal    @Override
1765531488ed848766708252a5fdc01c4ecf0fd167d1Vikram Aggarwal    public void updateConversation(Collection <Conversation> target, String columnName,
1766531488ed848766708252a5fdc01c4ecf0fd167d1Vikram Aggarwal            String value) {
17679e2d407fdafeb874e640eb84017feaf784309075Scott Kennedy        mConversationListCursor.updateString(target, columnName, value);
176875daee52f08c9bf66652b9bddf42cfbd1ca71987Vikram Aggarwal        refreshConversationList();
176954452ae7a9cdb607ed1db5e74077b5ebd95c46a2Vikram Aggarwal    }
177054452ae7a9cdb607ed1db5e74077b5ebd95c46a2Vikram Aggarwal
1771839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang    @Override
17727c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu    public void markConversationMessagesUnread(final Conversation conv,
17737c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu            final Set<Uri> unreadMessageUris, final byte[] originalConversationInfo) {
177487a8982af83168453b6dc219fe984bff68047bebAndy Huang        onPreMarkUnread();
17758f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang
1776839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        // locally mark conversation unread (the provider is supposed to propagate message unread
1777839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        // to conversation unread)
1778839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        conv.read = false;
17791faf93d79a00acb0fe607495c39f87d53a05889bPaul Westbrook        if (mConversationListCursor == null) {
178081230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal            LogUtils.d(LOG_TAG, "markConversationMessagesUnread(id=%d), deferring", conv.id);
17817c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu
17827c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu            mConversationListLoadFinishedCallbacks.add(new LoadFinishedCallback() {
17837c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu                @Override
17847c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu                public void onLoadFinished() {
17857c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu                    doMarkConversationMessagesUnread(conv, unreadMessageUris,
17867c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu                            originalConversationInfo);
17877c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu                }
17887c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu            });
17897c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu        } else {
179081230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal            LogUtils.d(LOG_TAG, "markConversationMessagesUnread(id=%d), performing", conv.id);
17917c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu            doMarkConversationMessagesUnread(conv, unreadMessageUris, originalConversationInfo);
17921faf93d79a00acb0fe607495c39f87d53a05889bPaul Westbrook        }
17937c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu    }
17941faf93d79a00acb0fe607495c39f87d53a05889bPaul Westbrook
179587a8982af83168453b6dc219fe984bff68047bebAndy Huang    /**
179687a8982af83168453b6dc219fe984bff68047bebAndy Huang     * Hook to do stuff before actually marking a conversation unread (only called from within
179787a8982af83168453b6dc219fe984bff68047bebAndy Huang     * conversation view). Most configurations do the default behavior of popping out of
179887a8982af83168453b6dc219fe984bff68047bebAndy Huang     * CV to go back to TL.
179987a8982af83168453b6dc219fe984bff68047bebAndy Huang     *
180087a8982af83168453b6dc219fe984bff68047bebAndy Huang     */
180187a8982af83168453b6dc219fe984bff68047bebAndy Huang    protected void onPreMarkUnread() {
180287a8982af83168453b6dc219fe984bff68047bebAndy Huang        // The only caller of this method is the conversation view, from where marking unread should
180387a8982af83168453b6dc219fe984bff68047bebAndy Huang        // take you back to list mode in most cases. Two-pane view is the exception.
180487a8982af83168453b6dc219fe984bff68047bebAndy Huang        showConversation(null);
180587a8982af83168453b6dc219fe984bff68047bebAndy Huang    }
180687a8982af83168453b6dc219fe984bff68047bebAndy Huang
18077c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu    private void doMarkConversationMessagesUnread(Conversation conv, Set<Uri> unreadMessageUris,
18087c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu            byte[] originalConversationInfo) {
180981230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal        // Only do a granular 'mark unread' if a subset of messages are unread
181028e31e2417d775b343919cf6018f85871e40a294Andy Huang        final int unreadCount = (unreadMessageUris == null) ? 0 : unreadMessageUris.size();
18110972e0793cc321670391d063348aecb5031b2677Mindy Pereira        final int numMessages = conv.getNumMessages();
18120972e0793cc321670391d063348aecb5031b2677Mindy Pereira        final boolean subsetIsUnread = (numMessages > 1 && unreadCount > 0
18130972e0793cc321670391d063348aecb5031b2677Mindy Pereira                && unreadCount < numMessages);
181428e31e2417d775b343919cf6018f85871e40a294Andy Huang
18159e4ca7993213d296043d20fe9cf4e166b5d595e8Andy Huang        LogUtils.d(LOG_TAG, "markConversationMessagesUnread(conv=%s)"
181681230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal                + ", numMessages=%d, unreadCount=%d, subsetIsUnread=%b",
18179e4ca7993213d296043d20fe9cf4e166b5d595e8Andy Huang                conv, numMessages, unreadCount, subsetIsUnread);
181828e31e2417d775b343919cf6018f85871e40a294Andy Huang        if (!subsetIsUnread) {
181966bc2aaf76bb0dfa4fc38f8fcbaaf9e413e321ddVikram Aggarwal            // Conversations are neither marked read, nor viewed, and we don't want to show
182066bc2aaf76bb0dfa4fc38f8fcbaaf9e413e321ddVikram Aggarwal            // the next conversation.
182181230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal            LogUtils.d(LOG_TAG, ". . doing full mark unread");
1822caaeed3837316175111bb16e6b60aeb0d523debfScott Kennedy            markConversationsRead(Collections.singletonList(conv), false, false, false);
1823839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        } else {
182481230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal            if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)) {
182581230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal                final ConversationInfo info = ConversationInfo.fromBlob(originalConversationInfo);
182681230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal                LogUtils.d(LOG_TAG, ". . doing subset mark unread, originalConversationInfo = %s",
182781230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal                        info);
182881230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal            }
1829daa06aba887e8e3748453f45ae52c1e571fe9f2eAndy Huang            mConversationListCursor.setConversationColumn(conv.uri, ConversationColumns.READ, 0);
1830839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang
183181230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal            // Locally update conversation's conversationInfo to revert to original version
183228e31e2417d775b343919cf6018f85871e40a294Andy Huang            if (originalConversationInfo != null) {
183328e31e2417d775b343919cf6018f85871e40a294Andy Huang                mConversationListCursor.setConversationColumn(conv.uri,
183428e31e2417d775b343919cf6018f85871e40a294Andy Huang                        ConversationColumns.CONVERSATION_INFO, originalConversationInfo);
183528e31e2417d775b343919cf6018f85871e40a294Andy Huang            }
1836839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang
1837839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang            // applyBatch with each CPO as an UPDATE op on each affected message uri
1838839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang            final ArrayList<ContentProviderOperation> ops = Lists.newArrayList();
1839839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang            String authority = null;
1840839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang            for (Uri messageUri : unreadMessageUris) {
1841839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang                if (authority == null) {
1842839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang                    authority = messageUri.getAuthority();
1843839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang                }
1844839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang                ops.add(ContentProviderOperation.newUpdate(messageUri)
1845839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang                        .withValue(UIProvider.MessageColumns.READ, 0)
1846839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang                        .build());
184781230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal                LogUtils.d(LOG_TAG, ". . Adding op: read=0, uri=%s", messageUri);
1848839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang            }
184981230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal            LogUtils.d(LOG_TAG, ". . operations = %s", ops);
1850839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang            new ContentProviderTask() {
1851839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang                @Override
1852839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang                protected void onPostExecute(Result result) {
1853d9895b65e074a2cd68ad2aaa87d7b49f05bacdbeVikram Aggarwal                    if (result.exception != null) {
1854d9895b65e074a2cd68ad2aaa87d7b49f05bacdbeVikram Aggarwal                        LogUtils.e(LOG_TAG, result.exception, "ContentProviderTask() ERROR.");
1855d9895b65e074a2cd68ad2aaa87d7b49f05bacdbeVikram Aggarwal                    } else {
18569e4ca7993213d296043d20fe9cf4e166b5d595e8Andy Huang                        LogUtils.d(LOG_TAG, "ContentProviderTask(): success %s",
18579e4ca7993213d296043d20fe9cf4e166b5d595e8Andy Huang                                Arrays.toString(result.results));
1858d9895b65e074a2cd68ad2aaa87d7b49f05bacdbeVikram Aggarwal                    }
1859839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang                }
1860839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang            }.run(mResolver, authority, ops);
1861839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        }
1862839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang    }
1863839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang
186487a8982af83168453b6dc219fe984bff68047bebAndy Huang    /**
186587a8982af83168453b6dc219fe984bff68047bebAndy Huang     * Mark a single conversation 'seen', which is a combination of 'viewed' and 'read'. In some
186687a8982af83168453b6dc219fe984bff68047bebAndy Huang     * configurations (peek mode), this operation may be prevented and the method will return false.
186787a8982af83168453b6dc219fe984bff68047bebAndy Huang     *
186887a8982af83168453b6dc219fe984bff68047bebAndy Huang     * @param conv the conversation to mark seen
186987a8982af83168453b6dc219fe984bff68047bebAndy Huang     * @return true if the operation was a success
187087a8982af83168453b6dc219fe984bff68047bebAndy Huang     */
187187a8982af83168453b6dc219fe984bff68047bebAndy Huang    @Override
187287a8982af83168453b6dc219fe984bff68047bebAndy Huang    public boolean markConversationSeen(Conversation conv) {
187387a8982af83168453b6dc219fe984bff68047bebAndy Huang        if (isCurrentConversationJustPeeking()) {
187487a8982af83168453b6dc219fe984bff68047bebAndy Huang            LogUtils.i(LOG_TAG, "AAC is in peek mode, not marking seen. conv=%s", conv);
187587a8982af83168453b6dc219fe984bff68047bebAndy Huang            return false;
187687a8982af83168453b6dc219fe984bff68047bebAndy Huang        } else {
187787a8982af83168453b6dc219fe984bff68047bebAndy Huang            markConversationsRead(Arrays.asList(conv), true /* read */, true /* viewed */);
187887a8982af83168453b6dc219fe984bff68047bebAndy Huang            return true;
187987a8982af83168453b6dc219fe984bff68047bebAndy Huang        }
188087a8982af83168453b6dc219fe984bff68047bebAndy Huang    }
188187a8982af83168453b6dc219fe984bff68047bebAndy Huang
1882839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang    @Override
18837c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu    public void markConversationsRead(final Collection<Conversation> targets, final boolean read,
18847c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu            final boolean viewed) {
1885919d01a1144dc14f114860949ff4af79a9165ec7Scott Kennedy        LogUtils.d(LOG_TAG, "markConversationsRead(targets=%s)", targets.toArray());
1886919d01a1144dc14f114860949ff4af79a9165ec7Scott Kennedy
18877c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu        if (mConversationListCursor == null) {
188881230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal            if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)) {
188981230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal                LogUtils.d(LOG_TAG, "markConversationsRead(targets=%s), deferring",
189081230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal                        targets.toArray());
189181230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal            }
18927c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu            mConversationListLoadFinishedCallbacks.add(new LoadFinishedCallback() {
18937c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu                @Override
18947c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu                public void onLoadFinished() {
1895caaeed3837316175111bb16e6b60aeb0d523debfScott Kennedy                    markConversationsRead(targets, read, viewed, true);
18967c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu                }
18977c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu            });
18987c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu        } else {
18997c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu            // We want to show the next conversation if we are marking unread.
1900caaeed3837316175111bb16e6b60aeb0d523debfScott Kennedy            markConversationsRead(targets, read, viewed, true);
19017c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu        }
19028f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang    }
19038f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang
19040d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy    private void markConversationsRead(final Collection<Conversation> targets, final boolean read,
1905caaeed3837316175111bb16e6b60aeb0d523debfScott Kennedy            final boolean markViewed, final boolean showNext) {
19067c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu        LogUtils.d(LOG_TAG, "performing markConversationsRead");
1907cc754b102ed65df9d637b4e0a03e645214ea3cccVikram Aggarwal        // Auto-advance if requested and the current conversation is being marked unread
19088f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang        if (showNext && !read) {
19090d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy            final Runnable operation = new Runnable() {
19100d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy                @Override
19110d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy                public void run() {
1912caaeed3837316175111bb16e6b60aeb0d523debfScott Kennedy                    markConversationsRead(targets, read, markViewed, showNext);
19130d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy                }
19140d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy            };
19150d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy
1916caaeed3837316175111bb16e6b60aeb0d523debfScott Kennedy            if (!showNextConversation(targets, operation)) {
19170d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy                // This method will be called again if the user selects an autoadvance option
19180d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy                return;
19190d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy            }
19208f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang        }
19210d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy
1922cc754b102ed65df9d637b4e0a03e645214ea3cccVikram Aggarwal        final int size = targets.size();
1923cc754b102ed65df9d637b4e0a03e645214ea3cccVikram Aggarwal        final List<ConversationOperation> opList = new ArrayList<ConversationOperation>(size);
1924cc754b102ed65df9d637b4e0a03e645214ea3cccVikram Aggarwal        for (final Conversation target : targets) {
1925e2be97a58fbae98b95e51735c6468d121e465355Paul Westbrook            final ContentValues value = new ContentValues(4);
1926cc754b102ed65df9d637b4e0a03e645214ea3cccVikram Aggarwal            value.put(ConversationColumns.READ, read);
19275109c516b5cac279b4f99271953a27dd9a4828d8Paul Westbrook
1928d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy            // We never want to mark unseen here, but we do want to mark it seen
1929d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy            if (read || markViewed) {
1930d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy                value.put(ConversationColumns.SEEN, Boolean.TRUE);
1931d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy            }
1932d5edd2d02649dffb40065fdb6a16acf91552b800Scott Kennedy
19335109c516b5cac279b4f99271953a27dd9a4828d8Paul Westbrook            // The mark read/unread/viewed operations do not show an undo bar
19345109c516b5cac279b4f99271953a27dd9a4828d8Paul Westbrook            value.put(ConversationOperations.Parameters.SUPPRESS_UNDO, true);
193566bc2aaf76bb0dfa4fc38f8fcbaaf9e413e321ddVikram Aggarwal            if (markViewed) {
1936cc754b102ed65df9d637b4e0a03e645214ea3cccVikram Aggarwal                value.put(ConversationColumns.VIEWED, true);
193766bc2aaf76bb0dfa4fc38f8fcbaaf9e413e321ddVikram Aggarwal            }
1938192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal            final ConversationInfo info = target.conversationInfo;
1939edd6c1a2807d2ade930dfd4622707298dc470d64Tony Mantler            final boolean changed = info.markRead(read);
1940edd6c1a2807d2ade930dfd4622707298dc470d64Tony Mantler            if (changed) {
1941edd6c1a2807d2ade930dfd4622707298dc470d64Tony Mantler                value.put(ConversationColumns.CONVERSATION_INFO, info.toBlob());
1942839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang            }
1943cc754b102ed65df9d637b4e0a03e645214ea3cccVikram Aggarwal            opList.add(mConversationListCursor.getOperationForConversation(
1944cc754b102ed65df9d637b4e0a03e645214ea3cccVikram Aggarwal                    target, ConversationOperation.UPDATE, value));
1945cc754b102ed65df9d637b4e0a03e645214ea3cccVikram Aggarwal            // Update the local conversation objects so they immediately change state.
1946cc754b102ed65df9d637b4e0a03e645214ea3cccVikram Aggarwal            target.read = read;
1947cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang            if (markViewed) {
1948cc754b102ed65df9d637b4e0a03e645214ea3cccVikram Aggarwal                target.markViewed();
1949cd5c5eeae167885ffa2959c200233fea2f39c5f7Andy Huang            }
1950839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        }
19519e2d407fdafeb874e640eb84017feaf784309075Scott Kennedy        mConversationListCursor.updateBulkValues(opList);
1952839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang    }
1953839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang
19548f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang    /**
19558f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang     * Auto-advance to a different conversation if the currently visible conversation in
19568f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang     * conversation mode is affected (deleted, marked unread, etc.).
19578f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang     *
19580d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy     * <p>Does nothing if outside of conversation mode.</p>
19598f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang     *
19608f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang     * @param target the set of conversations being deleted/marked unread
19618f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang     */
19629365a826b46b0e274df88e92534f7d871eef2aa2mindyp    @Override
19630d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy    public void showNextConversation(final Collection<Conversation> target) {
1964caaeed3837316175111bb16e6b60aeb0d523debfScott Kennedy        showNextConversation(target, null);
19650d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy    }
19660d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy
19670d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy    /**
196830c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao     * Helper function to determine if the provided set of conversations is in view
196930c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao     * @param target set of conversations that we are interested in
197030c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao     * @return true if they are in view, false otherwise
197130c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao     */
197230c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao    private boolean isCurrentConversationInView(final Collection<Conversation> target) {
197330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        final int viewMode = mViewMode.getMode();
197430c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        return (viewMode == ViewMode.CONVERSATION
197530c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                || viewMode == ViewMode.SEARCH_RESULTS_CONVERSATION)
197630c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                && Conversation.contains(target, mCurrentConversation);
197730c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao    }
197830c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao
197930c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao    /**
19800d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy     * Auto-advance to a different conversation if the currently visible conversation in
19810d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy     * conversation mode is affected (deleted, marked unread, etc.).
19820d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy     *
19830d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy     * <p>Does nothing if outside of conversation mode.</p>
1984c94d07f4460d00db898237603de311d226707a96Andy Huang     * <p>
1985c94d07f4460d00db898237603de311d226707a96Andy Huang     * Clients may pass an operation to execute on the target that this method will run after
1986c94d07f4460d00db898237603de311d226707a96Andy Huang     * auto-advance is complete. The operation, if provided, may run immediately, or it may run
1987c94d07f4460d00db898237603de311d226707a96Andy Huang     * later, or not at all. Reasons it may run later include:
1988c94d07f4460d00db898237603de311d226707a96Andy Huang     * <ul>
1989c94d07f4460d00db898237603de311d226707a96Andy Huang     * <li>the auto-advance setting is uninitialized and we need to wait for the user to set it</li>
1990c94d07f4460d00db898237603de311d226707a96Andy Huang     * <li>auto-advance in this configuration requires a mode change, and we need to wait for the
1991c94d07f4460d00db898237603de311d226707a96Andy Huang     * mode change transition to finish</li>
1992c94d07f4460d00db898237603de311d226707a96Andy Huang     * </ul>
1993c94d07f4460d00db898237603de311d226707a96Andy Huang     * <p>If the current conversation is not in the target collection, this method will do nothing,
1994c94d07f4460d00db898237603de311d226707a96Andy Huang     * and will not execute the operation.
19950d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy     *
19960d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy     * @param target the set of conversations being deleted/marked unread
1997c94d07f4460d00db898237603de311d226707a96Andy Huang     * @param operation (optional) the operation to execute after advancing
1998c94d07f4460d00db898237603de311d226707a96Andy Huang     * @return <code>false</code> if this method handled or will execute the operation,
1999c94d07f4460d00db898237603de311d226707a96Andy Huang     * <code>true</code> otherwise.
20000d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy     */
20010d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy    private boolean showNextConversation(final Collection<Conversation> target,
2002caaeed3837316175111bb16e6b60aeb0d523debfScott Kennedy            final Runnable operation) {
200330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        if (isCurrentConversationInView(target)) {
20040d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy            final int autoAdvanceSetting = mAccount.settings.getAutoAdvanceSetting();
20050d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy
200693e6457cf4391f1aff025808b536135b5bdbfe39Tony Mantler            // If we don't have one set, but we're here, just take the default
200793e6457cf4391f1aff025808b536135b5bdbfe39Tony Mantler            final int autoAdvance = (autoAdvanceSetting == AutoAdvance.UNSET) ?
200893e6457cf4391f1aff025808b536135b5bdbfe39Tony Mantler                    AutoAdvance.DEFAULT : autoAdvanceSetting;
20090d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy
201093e6457cf4391f1aff025808b536135b5bdbfe39Tony Mantler            // Set mAutoAdvanceOp *before* showConversation() to ensure that it runs when the
201193e6457cf4391f1aff025808b536135b5bdbfe39Tony Mantler            // transition doesn't run (i.e. it "completes" immediately).
201293e6457cf4391f1aff025808b536135b5bdbfe39Tony Mantler            mAutoAdvanceOp = operation;
201387a8982af83168453b6dc219fe984bff68047bebAndy Huang            doShowNextConversation(target, autoAdvance);
201493e6457cf4391f1aff025808b536135b5bdbfe39Tony Mantler            return (mAutoAdvanceOp == null);
20150d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy        }
20160d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy
201793e6457cf4391f1aff025808b536135b5bdbfe39Tony Mantler        return true;
20188f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang    }
20198f6b006357bf6c76e62fca899f594b2c674e8152Andy Huang
202087a8982af83168453b6dc219fe984bff68047bebAndy Huang    /**
202187a8982af83168453b6dc219fe984bff68047bebAndy Huang     * Do the actual work of selecting a next conversation to show and showing it. Two-pane
202287a8982af83168453b6dc219fe984bff68047bebAndy Huang     * overrides this in landscape to prefer peeking rather than staring at an empty CV pane when
202387a8982af83168453b6dc219fe984bff68047bebAndy Huang     * auto-advance=LIST.
202487a8982af83168453b6dc219fe984bff68047bebAndy Huang     *
202587a8982af83168453b6dc219fe984bff68047bebAndy Huang     * @param target conversations being destroyed, of which the current convo is one
202687a8982af83168453b6dc219fe984bff68047bebAndy Huang     * @param autoAdvance auto-advance pref value
202787a8982af83168453b6dc219fe984bff68047bebAndy Huang     */
202887a8982af83168453b6dc219fe984bff68047bebAndy Huang    protected void doShowNextConversation(Collection<Conversation> target, int autoAdvance) {
202987a8982af83168453b6dc219fe984bff68047bebAndy Huang        final Conversation next = mTracker.getNextConversation(autoAdvance, target);
203087a8982af83168453b6dc219fe984bff68047bebAndy Huang        LogUtils.d(LOG_TAG, "showNextConversation: showing %s next.", next);
203187a8982af83168453b6dc219fe984bff68047bebAndy Huang        showConversation(next);
203287a8982af83168453b6dc219fe984bff68047bebAndy Huang    }
203387a8982af83168453b6dc219fe984bff68047bebAndy Huang
2034839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang    @Override
2035839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang    public void starMessage(ConversationMessage msg, boolean starred) {
2036839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        if (msg.starred == starred) {
2037839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang            return;
2038839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        }
2039839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang
2040b537167ea6f8acb3874dc6416e07a7101539639cTony Mantler        msg.setStarredInConversation(starred);
2041839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang
2042839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        // locally propagate the change to the owning conversation
2043839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        // (figure the provider will properly propagate the change when it commits it)
2044839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        //
2045839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        // when unstarring, only propagate the change if this was the only message starred
2046839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        final boolean conversationStarred = starred || msg.isConversationStarred();
2047cd12e82dead8a920b060340c037d51b144966a73Andy Huang        final Conversation conv = msg.getConversation();
2048cd12e82dead8a920b060340c037d51b144966a73Andy Huang        if (conversationStarred != conv.starred) {
2049cd12e82dead8a920b060340c037d51b144966a73Andy Huang            conv.starred = conversationStarred;
2050cd12e82dead8a920b060340c037d51b144966a73Andy Huang            mConversationListCursor.setConversationColumn(conv.uri,
2051839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang                    ConversationColumns.STARRED, conversationStarred);
2052839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        }
2053839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang
2054839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        final ContentValues values = new ContentValues(1);
2055839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        values.put(UIProvider.MessageColumns.STARRED, starred ? 1 : 0);
2056839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang
2057839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        new ContentProviderTask.UpdateTask() {
2058839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang            @Override
2059839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang            protected void onPostExecute(Result result) {
2060839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang                // TODO: handle errors?
2061839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang            }
2062839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang        }.run(mResolver, msg.uri, values, null /* selection*/, null /* selectionArgs */);
2063839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang    }
2064839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang
206512b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang    @Override
2066486e63e5bc7a88455e6fcb83d9cb251df203f727Alice Yang    public void requestFolderRefresh() {
206737dda440aaa245cef2d21d17babe951e1d449c4aAlice Yang        if (mFolder == null) {
206837dda440aaa245cef2d21d17babe951e1d449c4aAlice Yang            return;
206937dda440aaa245cef2d21d17babe951e1d449c4aAlice Yang        }
207037dda440aaa245cef2d21d17babe951e1d449c4aAlice Yang        final ConversationListFragment convList = getConversationListFragment();
207137dda440aaa245cef2d21d17babe951e1d449c4aAlice Yang        if (convList == null) {
207237dda440aaa245cef2d21d17babe951e1d449c4aAlice Yang            // This could happen if this account is in initial sync (user
207337dda440aaa245cef2d21d17babe951e1d449c4aAlice Yang            // is seeing the "your mail will appear shortly" message)
207437dda440aaa245cef2d21d17babe951e1d449c4aAlice Yang            return;
207537dda440aaa245cef2d21d17babe951e1d449c4aAlice Yang        }
207637dda440aaa245cef2d21d17babe951e1d449c4aAlice Yang        convList.showSyncStatusBar();
20770d74a662f6e96b5a21e37978eb6e3b46545e3cbfAlice Yang
207837dda440aaa245cef2d21d17babe951e1d449c4aAlice Yang        if (mAsyncRefreshTask != null) {
207937dda440aaa245cef2d21d17babe951e1d449c4aAlice Yang            mAsyncRefreshTask.cancel(true);
208028e0c34ad35925fc74633bec66344911f2de6a22Mindy Pereira        }
208137dda440aaa245cef2d21d17babe951e1d449c4aAlice Yang        mAsyncRefreshTask = new AsyncRefreshTask(mContext, mFolder.refreshUri);
208237dda440aaa245cef2d21d17babe951e1d449c4aAlice Yang        mAsyncRefreshTask.execute();
208328e0c34ad35925fc74633bec66344911f2de6a22Mindy Pereira    }
208428e0c34ad35925fc74633bec66344911f2de6a22Mindy Pereira
2085fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira    /**
2086fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira     * Confirm (based on user's settings) and delete a conversation from the conversation list and
2087fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira     * from the database.
2088b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal     * @param actionId the ID of the menu item that caused the delete: R.id.delete, R.id.archive...
20893d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal     * @param target the conversations to act upon
20903d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal     * @param showDialog true if a confirmation dialog is to be shown, false otherwise.
20913d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal     * @param confirmResource the resource ID of the string that is shown in the confirmation dialog
2092fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira     */
2093b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal    private void confirmAndDelete(int actionId, final Collection<Conversation> target,
209430c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            boolean showDialog, int confirmResource, UndoCallback undoCallback) {
209584fe99431df3b989dff9b363562f09fd2757b63cVikram Aggarwal        final boolean isBatch = false;
2096fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira        if (showDialog) {
209730c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            makeDialogListener(actionId, isBatch, undoCallback);
20983d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal            final CharSequence message = Utils.formatPlural(mContext, confirmResource,
20993d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal                    target.size());
2100b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal            final ConfirmDialogFragment c = ConfirmDialogFragment.newInstance(message);
2101b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal            c.displayDialog(mActivity.getFragmentManager());
2102fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira        } else {
210330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            delete(0, target, getDeferredAction(actionId, target, isBatch, undoCallback), isBatch);
2104fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira        }
2105fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira    }
2106fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira
21074f4782bcc53e13b30f1fd417beb1a6c04a54faedVikram Aggarwal    @Override
21080d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy    public void delete(final int actionId, final Collection<Conversation> target,
2109caaeed3837316175111bb16e6b60aeb0d523debfScott Kennedy                       final DestructiveAction action, final boolean isBatch) {
211084f7d32bdc79263004ed5241480988e02f8e618cmindyp        // Order of events is critical! The Conversation View Fragment must be
211184f7d32bdc79263004ed5241480988e02f8e618cmindyp        // notified of the next conversation with showConversation(next) *before* the
211284f7d32bdc79263004ed5241480988e02f8e618cmindyp        // conversation list
2113192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal        // fragment has a chance to delete the conversation, animating it away.
2114192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal
211584f7d32bdc79263004ed5241480988e02f8e618cmindyp        // Update the conversation fragment if the current conversation is
211684f7d32bdc79263004ed5241480988e02f8e618cmindyp        // deleted.
21170d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy        final Runnable operation = new Runnable() {
21180d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy            @Override
21190d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy            public void run() {
2120caaeed3837316175111bb16e6b60aeb0d523debfScott Kennedy                delete(actionId, target, action, isBatch);
21210d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy            }
21220d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy        };
21230d0f8b061faae060529af4da339eed2d728ef1bdScott Kennedy
212452bab6f5e293c8c16e7229c3e42412f169d47752Rohan Shah        showNextConversation(target, operation);
212552bab6f5e293c8c16e7229c3e42412f169d47752Rohan Shah
2126ab1b5b6d16800ff675116cdab33cf26ab3514732Vikram Aggarwal        // If the conversation is in the selected set, remove it from the set.
212784fe99431df3b989dff9b363562f09fd2757b63cVikram Aggarwal        // Batch selections are cleared in the end of the action, so not done for batch actions.
212884fe99431df3b989dff9b363562f09fd2757b63cVikram Aggarwal        if (!isBatch) {
212984fe99431df3b989dff9b363562f09fd2757b63cVikram Aggarwal            for (final Conversation conv : target) {
2130ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao                if (mCheckedSet.contains(conv)) {
2131ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao                    mCheckedSet.toggle(conv);
213284fe99431df3b989dff9b363562f09fd2757b63cVikram Aggarwal                }
2133ab1b5b6d16800ff675116cdab33cf26ab3514732Vikram Aggarwal            }
2134ab1b5b6d16800ff675116cdab33cf26ab3514732Vikram Aggarwal        }
2135192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal        // The conversation list deletes and performs the action if it exists.
2136192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal        final ConversationListFragment convListFragment = getConversationListFragment();
2137192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal        if (convListFragment != null) {
2138193e05a8c38b87c400ac594b53b54eb211d35602Alice Yang            LogUtils.i(LOG_TAG, "AAC.requestDelete: ListFragment is handling delete.");
2139669947bf67a3d0eed377b18099bb873604ad5009Vikram Aggarwal            convListFragment.requestDelete(actionId, target, action);
2140192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal            return;
2141192fac189e6aed434556a4e37bd3c5c29ef02f29Vikram Aggarwal        }
214284f7d32bdc79263004ed5241480988e02f8e618cmindyp        // No visible UI element handled it on our behalf. Perform the action
214384f7d32bdc79263004ed5241480988e02f8e618cmindyp        // ourself.
2144193e05a8c38b87c400ac594b53b54eb211d35602Alice Yang        LogUtils.i(LOG_TAG, "ACC.requestDelete: performing remove action ourselves");
2145d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal        action.performAction();
2146d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal    }
2147d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal
2148d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal    /**
2149d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal     * Requests that the action be performed and the UI state is updated to reflect the new change.
2150715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @param action the action to be performed, specified as a menu id: R.id.archive, ...
2151d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal     */
2152715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal    private void requestUpdate(final DestructiveAction action) {
2153d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal        action.performAction();
2154d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal        refreshConversationList();
215575daee52f08c9bf66652b9bddf42cfbd1ca71987Vikram Aggarwal    }
21566fbc87affe46f6d49c584a307ec836e68163c182Vikram Aggarwal
21576fbc87affe46f6d49c584a307ec836e68163c182Vikram Aggarwal    @Override
21581ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    public void onPrepareDialog(int id, Dialog dialog, Bundle bundle) {
21591ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal        // TODO(viki): Auto-generated method stub
21601ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    }
21611ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal
21621ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    @Override
216387a8982af83168453b6dc219fe984bff68047bebAndy Huang    public void onPrepareOptionsMenu(Menu menu) {
216487a8982af83168453b6dc219fe984bff68047bebAndy Huang        mActionBarController.onPrepareOptionsMenu(menu);
2165a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    }
2166a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal
216768f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira    @Override
216868f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira    public void onPause() {
2169c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal        mHaveAccountList = false;
21706ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook        enableNotifications();
217194e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook    }
217294e440d7fb9bb54ba4b22e348a2e22f3cf8e0167Paul Westbrook
2173a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    @Override
21741ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    public void onResume() {
21756ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook        // Register the receiver that will prevent the status receiver from
21766ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook        // displaying its notification icon as long as we're running.
21776ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook        // The SupressNotificationReceiver will block the broadcast if we're looking at the folder
21786ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook        // that the notification was received for.
21796ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook        disableNotifications();
21801ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang
21811ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        mSafeToModifyFragments = true;
2182ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein
2183ed5b52da9d2dd676c72a7137f6b85d9841fe6eecAndrew Sapperstein        attachEmptyFolderDialogFragmentListener();
218423e3313977d35d5caf152baf6cb27c9cc2d8ab91Andrew Sapperstein
218523e3313977d35d5caf152baf6cb27c9cc2d8ab91Andrew Sapperstein        // Invalidating the options menu so that when we make changes in settings,
218623e3313977d35d5caf152baf6cb27c9cc2d8ab91Andrew Sapperstein        // the changes will always be updated in the action bar/options menu/
218723e3313977d35d5caf152baf6cb27c9cc2d8ab91Andrew Sapperstein        mActivity.invalidateOptionsMenu();
21881ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    }
21891ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal
21901ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    @Override
2191a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    public void onSaveInstanceState(Bundle outState) {
2192649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal        mViewMode.handleSaveInstanceState(outState);
21936c51158ad3269f157424e6c7bd488425c98da08fVikram Aggarwal        if (mAccount != null) {
21946c51158ad3269f157424e6c7bd488425c98da08fVikram Aggarwal            outState.putParcelable(SAVED_ACCOUNT, mAccount);
21956c51158ad3269f157424e6c7bd488425c98da08fVikram Aggarwal        }
21965e478d2c4087b50764756b0edc95941cb6094f68Mindy Pereira        if (mFolder != null) {
21975e478d2c4087b50764756b0edc95941cb6094f68Mindy Pereira            outState.putParcelable(SAVED_FOLDER, mFolder);
21988b15263097bd9ce1a28db981b6a85261e6cb6886Vikram Aggarwal        }
2199f3341405c97ff6d27a0d5fa37f6031e1c395d161Vikram Aggarwal        // If this is a search activity, let's store the search query term as well.
2200f3341405c97ff6d27a0d5fa37f6031e1c395d161Vikram Aggarwal        if (ConversationListContext.isSearchResult(mConvListContext)) {
2201f3341405c97ff6d27a0d5fa37f6031e1c395d161Vikram Aggarwal            outState.putString(SAVED_QUERY, mConvListContext.searchQuery);
2202f3341405c97ff6d27a0d5fa37f6031e1c395d161Vikram Aggarwal        }
220349e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal        if (mCurrentConversation != null && mViewMode.isConversationMode()) {
220426f23fc8cc0d49bc0bea1eeec4c0c4742928f693Mindy Pereira            outState.putParcelable(SAVED_CONVERSATION, mCurrentConversation);
220526f23fc8cc0d49bc0bea1eeec4c0c4742928f693Mindy Pereira        }
2206ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao        if (!mCheckedSet.isEmpty()) {
2207ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao            outState.putParcelable(SAVED_SELECTED_SET, mCheckedSet);
22084556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang        }
2209d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira        if (mToastBar.getVisibility() == View.VISIBLE) {
2210d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira            outState.putParcelable(SAVED_TOAST_BAR_OP, mToastBar.getOperation());
2211d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira        }
2212649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal        final ConversationListFragment convListFragment = getConversationListFragment();
2213d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira        if (convListFragment != null) {
2214649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal            convListFragment.getAnimatedAdapter().onSaveInstanceState(outState);
2215d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira        }
2216b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal        // If there is a dialog being shown, save the state so we can create a listener for it.
22176cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal        if (mDialogAction != -1) {
22186cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal            outState.putInt(SAVED_ACTION, mDialogAction);
2219b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal            outState.putBoolean(SAVED_ACTION_FROM_SELECTED, mDialogFromSelectedSet);
22206cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal        }
2221a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        if (mDetachedConvUri != null) {
2222a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal            outState.putParcelable(SAVED_DETACHED_CONV_URI, mDetachedConvUri);
2223a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        }
22245747e152fa606c20261b18431f4a9f0ff3be7b5aAndrew Sapperstein
2225b10212ef23a4e2317fc43c62dff9ad177b5bcf2cScott Kennedy        outState.putParcelable(SAVED_HIERARCHICAL_FOLDER, mFolderListFolder);
22265747e152fa606c20261b18431f4a9f0ff3be7b5aAndrew Sapperstein        mSafeToModifyFragments = false;
2227ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang
2228ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang        outState.putParcelable(SAVED_INBOX_KEY, mInbox);
2229f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy
2230f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy        outState.putBundle(SAVED_CONVERSATION_LIST_SCROLL_POSITIONS,
2231f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy                mConversationListScrollPositions);
2232c6801eb828627c37b8992584767c095dfe11df62Jin Cao
2233c6801eb828627c37b8992584767c095dfe11df62Jin Cao        mSearchViewController.saveState(outState);
22341ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    }
22351ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang
22361ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    /**
22371ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang     * @see #mSafeToModifyFragments
22381ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang     */
22391ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    protected boolean safeToModifyFragments() {
22401ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        return mSafeToModifyFragments;
2241a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    }
2242a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal
2243a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    @Override
2244313ac13095a386be366bd25f8106fbb34b4a7098Andy Huang    public void executeSearch(String query) {
2245779dd6012a6c5b4d71255085059e57d94eab1a3fJin Cao        AnalyticsTimer.getInstance().trackStart(AnalyticsTimer.SEARCH_TO_LIST);
224668f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira        Intent intent = new Intent();
224768f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira        intent.setAction(Intent.ACTION_SEARCH);
224868f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira        intent.putExtra(ConversationListContext.EXTRA_SEARCH_QUERY, query);
224968f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira        intent.putExtra(Utils.EXTRA_ACCOUNT, mAccount);
225068f2e222b4ffccd9f67f02b3a9cfdb3841a7eb43Mindy Pereira        intent.setComponent(mActivity.getComponentName());
2251c6801eb828627c37b8992584767c095dfe11df62Jin Cao        mSearchViewController.showSearchActionBar(
2252c6801eb828627c37b8992584767c095dfe11df62Jin Cao                MaterialSearchViewController.SEARCH_VIEW_STATE_GONE);
2253371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon        // Call startActivityForResult here so we can tell if we have navigated to a different folder
2254371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon        // or account from search results.
2255371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon        mActivity.startActivityForResult(intent, CHANGE_NAVIGATION_REQUEST_CODE);
2256a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    }
2257a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal
2258a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    @Override
22591ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    public void onStop() {
2260cb85aeaf327e2c6a326457b5a395b4d6cdb70d63Scott Kennedy        NotificationActionUtils.unregisterUndoNotificationObserver(mUndoNotificationObserver);
2261a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    }
2262a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal
2263632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
2264632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public void onDestroy() {
2265b2ef9c18d1f68543d9c782e1952b70d3bbcd94d6Andy Huang        // stop listening to the cursor on e.g. configuration changes
2266b2ef9c18d1f68543d9c782e1952b70d3bbcd94d6Andy Huang        if (mConversationListCursor != null) {
2267b2ef9c18d1f68543d9c782e1952b70d3bbcd94d6Andy Huang            mConversationListCursor.removeListener(this);
2268b2ef9c18d1f68543d9c782e1952b70d3bbcd94d6Andy Huang        }
2269144bfe739b93afdee0a1700a34806b0b287e5887Andy Huang        mDrawIdler.setListener(null);
2270144bfe739b93afdee0a1700a34806b0b287e5887Andy Huang        mDrawIdler.setRootView(null);
2271632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        // unregister the ViewPager's observer on the conversation cursor
2272632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        mPagerController.onDestroy();
22732d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein        mActionBarController.onDestroy();
22747c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal        mRecentFolderList.destroy();
22754e0158f03fd8f430e88394e8b99e1b65ed01838aAndy Huang        mDestroyed = true;
227659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal        mHandler.removeCallbacks(mLogServiceChecker);
227759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal        mLogServiceChecker = null;
2278c6801eb828627c37b8992584767c095dfe11df62Jin Cao        mSearchViewController.onDestroy();
2279632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
2280632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
2281fa131a2ff399fd1d544f759b063268fb4e8a3e70Vikram Aggarwal    /**
228293dc202420c3bf8adf4d9e8ae18c8e30e3cb1959Vikram Aggarwal     * Set the Action Bar icon according to the mode. The Action Bar icon can contain a back button
228393dc202420c3bf8adf4d9e8ae18c8e30e3cb1959Vikram Aggarwal     * or not. The individual controller is responsible for changing the icon based on the mode.
228493dc202420c3bf8adf4d9e8ae18c8e30e3cb1959Vikram Aggarwal     */
228593dc202420c3bf8adf4d9e8ae18c8e30e3cb1959Vikram Aggarwal    protected abstract void resetActionBarIcon();
228693dc202420c3bf8adf4d9e8ae18c8e30e3cb1959Vikram Aggarwal
228793dc202420c3bf8adf4d9e8ae18c8e30e3cb1959Vikram Aggarwal    /**
2288161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * {@inheritDoc} Subclasses must override this to listen to mode changes
2289161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * from the ViewMode. Subclasses <b>must</b> call the parent's
2290161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * onViewModeChanged since the parent will handle common state changes.
2291fa131a2ff399fd1d544f759b063268fb4e8a3e70Vikram Aggarwal     */
2292a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    @Override
2293fa131a2ff399fd1d544f759b063268fb4e8a3e70Vikram Aggarwal    public void onViewModeChanged(int newMode) {
229449e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal        // When we step away from the conversation mode, we don't have a current conversation
229549e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal        // anymore. Let's blank it out so clients calling getCurrentConversation are not misled.
229649e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal        if (!ViewMode.isConversationMode(newMode)) {
229749e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal            setCurrentConversation(null);
229849e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal        }
22996c570db71b8adc43d7dc2f31e6fa63cf50c1426eAndrew Sapperstein
230093dc202420c3bf8adf4d9e8ae18c8e30e3cb1959Vikram Aggarwal        // If the viewmode is not set, preserve existing icon.
230193dc202420c3bf8adf4d9e8ae18c8e30e3cb1959Vikram Aggarwal        if (newMode != ViewMode.UNKNOWN) {
230293dc202420c3bf8adf4d9e8ae18c8e30e3cb1959Vikram Aggarwal            resetActionBarIcon();
230393dc202420c3bf8adf4d9e8ae18c8e30e3cb1959Vikram Aggarwal        }
230412b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang
230512b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        if (isDrawerEnabled()) {
2306dfbc6985e44c05e37fd8addce65a063ff57a3dc1Vikram Aggarwal            /** If the folder doesn't exist, or its parent URI is empty,
2307dfbc6985e44c05e37fd8addce65a063ff57a3dc1Vikram Aggarwal             * this is not a child folder */
23089695e0046f79bd2d7166a411d6feff4cf0fb2539Jin Cao            final boolean isTopLevel = Folder.isRoot(mFolder);
2309207e662df01c550a4894bec03219820528365508Rohan Shah            updateDrawerIndicator(newMode, isTopLevel);
2310371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon            mDrawerContainer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
231112b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            closeDrawerIfOpen();
231212b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
2313a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    }
2314a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal
2315dfbc6985e44c05e37fd8addce65a063ff57a3dc1Vikram Aggarwal    /**
2316207e662df01c550a4894bec03219820528365508Rohan Shah     * Update the drawer indicator to either be the burger or the back arrow.
2317dfbc6985e44c05e37fd8addce65a063ff57a3dc1Vikram Aggarwal     * @param viewMode the current view mode
2318dfbc6985e44c05e37fd8addce65a063ff57a3dc1Vikram Aggarwal     * @param isTopLevel true if the current folder is not a child
2319dfbc6985e44c05e37fd8addce65a063ff57a3dc1Vikram Aggarwal     */
2320207e662df01c550a4894bec03219820528365508Rohan Shah    private void updateDrawerIndicator(final int viewMode, final boolean isTopLevel) {
2321207e662df01c550a4894bec03219820528365508Rohan Shah        // Show burger if we're either in conversation list or folder list mode.
2322207e662df01c550a4894bec03219820528365508Rohan Shah        if (isDrawerEnabled() && !ViewMode.isSearchMode(viewMode)
2323207e662df01c550a4894bec03219820528365508Rohan Shah            && (viewMode == ViewMode.CONVERSATION_LIST  && isTopLevel)) {
2324375faf0f8cf1d1de1d4aca7b9a7598e168dc9bf3Rohan Shah            mDrawerToggle.setHomeAsUpIndicator(R.drawable.ic_menu_wht_24dp);
2325207e662df01c550a4894bec03219820528365508Rohan Shah
2326207e662df01c550a4894bec03219820528365508Rohan Shah        // Otherwise, show the back arrow for the indicator.
2327207e662df01c550a4894bec03219820528365508Rohan Shah        } else {
2328207e662df01c550a4894bec03219820528365508Rohan Shah            mDrawerToggle.setHomeAsUpIndicator(R.drawable.ic_arrow_back_wht_24dp_with_rtl);
2329207e662df01c550a4894bec03219820528365508Rohan Shah        }
23308a72b8590d9166ada55f69eeb083d2b8c5c915c9Scott Kennedy    }
23318a72b8590d9166ada55f69eeb083d2b8c5c915c9Scott Kennedy
23323825f3d2284b2b57fadcfe6a4ebd9992f3c5c7bbAndy Huang    public void disablePagerUpdates() {
23333825f3d2284b2b57fadcfe6a4ebd9992f3c5c7bbAndy Huang        mPagerController.stopListening();
23343825f3d2284b2b57fadcfe6a4ebd9992f3c5c7bbAndy Huang    }
23353825f3d2284b2b57fadcfe6a4ebd9992f3c5c7bbAndy Huang
23364e0158f03fd8f430e88394e8b99e1b65ed01838aAndy Huang    public boolean isDestroyed() {
23374e0158f03fd8f430e88394e8b99e1b65ed01838aAndy Huang        return mDestroyed;
23384e0158f03fd8f430e88394e8b99e1b65ed01838aAndy Huang    }
23394e0158f03fd8f430e88394e8b99e1b65ed01838aAndy Huang
234054f120f6ada40c7191811dfff99b151e9e192c78mindyp    @Override
234154f120f6ada40c7191811dfff99b151e9e192c78mindyp    public void commitDestructiveActions(boolean animate) {
2342c6adce3cf6887c4c6dd5005724565702751843d0mindyp        ConversationListFragment fragment = getConversationListFragment();
23431e2573bbba613fb700e8fa01c58c9382c0027b5eMindy Pereira        if (fragment != null) {
2344c6adce3cf6887c4c6dd5005724565702751843d0mindyp            fragment.commitDestructiveActions(animate);
23451e2573bbba613fb700e8fa01c58c9382c0027b5eMindy Pereira        }
23461e2573bbba613fb700e8fa01c58c9382c0027b5eMindy Pereira    }
23471e2573bbba613fb700e8fa01c58c9382c0027b5eMindy Pereira
2348a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    @Override
23491ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    public void onWindowFocusChanged(boolean hasFocus) {
235069b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal        final ConversationListFragment convList = getConversationListFragment();
23516422b8d051c93b4f34580c551254fc50c9efa4dbVikram Aggarwal        // hasFocus already ensures that the window is in focus, so we don't need to call
23526422b8d051c93b4f34580c551254fc50c9efa4dbVikram Aggarwal        // AAC.isFragmentVisible(convList) here.
23539f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        if (hasFocus && convList != null && convList.isVisible()) {
23549f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook            // The conversation list is visible.
235569b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal            informCursorVisiblity(true);
23569f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        }
2357a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal    }
2358a55b36cb6b75ce8096812183651e89115300975aVikram Aggarwal
2359ca716f160fa033728fd4e60bcd13a4dc8693ee20Vikram Aggarwal    /**
2360ca716f160fa033728fd4e60bcd13a4dc8693ee20Vikram Aggarwal     * Set the account, and carry out all the account-related changes that rely on this.
2361715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @param account new account to set to.
2362ca716f160fa033728fd4e60bcd13a4dc8693ee20Vikram Aggarwal     */
236375181e846462659d1c9b919d0f1250e34647a280Mindy Pereira    private void setAccount(Account account) {
2364b9ca979fb93cc14246b449a2c02dc615c9e0cdd9Andy Huang        if (account == null) {
2365b9ca979fb93cc14246b449a2c02dc615c9e0cdd9Andy Huang            LogUtils.w(LOG_TAG, new Error(),
2366b9ca979fb93cc14246b449a2c02dc615c9e0cdd9Andy Huang                    "AAC ignoring null (presumably invalid) account restoration");
2367b9ca979fb93cc14246b449a2c02dc615c9e0cdd9Andy Huang            return;
2368b9ca979fb93cc14246b449a2c02dc615c9e0cdd9Andy Huang        }
2369b114841b6f2a91d92b8d0af4ec05ce81a5ba5599Andy Huang        LogUtils.d(LOG_TAG, "AbstractActivityController.setAccount(): account = %s", account.uri);
237091e8737780f631a65633434447b0da864beb5baeVikram Aggarwal        mAccount = account;
23712168cbc9c58d2133bdf77ecd342c3d5ee755d607Régis Décamps
23722168cbc9c58d2133bdf77ecd342c3d5ee755d607Régis Décamps        Analytics.getInstance().setEmail(account.getEmailAddress(), account.getType());
23732168cbc9c58d2133bdf77ecd342c3d5ee755d607Régis Décamps
2374ca716f160fa033728fd4e60bcd13a4dc8693ee20Vikram Aggarwal        // Only change AAC state here. Do *not* modify any other object's state. The object
2375ca716f160fa033728fd4e60bcd13a4dc8693ee20Vikram Aggarwal        // should listen on account changes.
2376177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        restartOptionalLoader(LOADER_RECENT_FOLDERS, mFolderCallbacks, Bundle.EMPTY);
2377ca716f160fa033728fd4e60bcd13a4dc8693ee20Vikram Aggarwal        mActivity.invalidateOptionsMenu();
2378ca716f160fa033728fd4e60bcd13a4dc8693ee20Vikram Aggarwal        disableNotificationsOnAccountChange(mAccount);
2379177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        restartOptionalLoader(LOADER_ACCOUNT_UPDATE_CURSOR, mAccountCallbacks, Bundle.EMPTY);
23802fe343f37226123e7cd5741d40be606dcf4edaabVikram Aggarwal        // The Mail instance can be null during test runs.
23812fe343f37226123e7cd5741d40be606dcf4edaabVikram Aggarwal        final MailAppProvider instance = MailAppProvider.getInstance();
23822fe343f37226123e7cd5741d40be606dcf4edaabVikram Aggarwal        if (instance != null) {
23832fe343f37226123e7cd5741d40be606dcf4edaabVikram Aggarwal            instance.setLastViewedAccount(mAccount.uri.toString());
23842fe343f37226123e7cd5741d40be606dcf4edaabVikram Aggarwal        }
238591e8737780f631a65633434447b0da864beb5baeVikram Aggarwal        if (account.settings == null) {
238691e8737780f631a65633434447b0da864beb5baeVikram Aggarwal            LogUtils.w(LOG_TAG, new Error(), "AAC ignoring account with null settings.");
238791e8737780f631a65633434447b0da864beb5baeVikram Aggarwal            return;
238891e8737780f631a65633434447b0da864beb5baeVikram Aggarwal        }
23897c401b7896910c00e6234e8774aab0be45740d32Vikram Aggarwal        mAccountObservers.notifyChanged();
239034f7b236154528e8632d1306f1250d52427b1036Vikram Aggarwal        perhapsEnterWaitMode();
239175181e846462659d1c9b919d0f1250e34647a280Mindy Pereira    }
239275181e846462659d1c9b919d0f1250e34647a280Mindy Pereira
23931ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    /**
2394161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * Restore the state from the previous bundle. Subclasses should call this
2395161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * method from the parent class, since it performs important UI
2396161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     * initialization.
2397161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     *
2398715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @param savedState previous state
23991ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal     */
2400632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
2401632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public void onRestoreInstanceState(Bundle savedState) {
2402a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        mDetachedConvUri = savedState.getParcelable(SAVED_DETACHED_CONV_URI);
2403632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        if (savedState.containsKey(SAVED_CONVERSATION)) {
2404632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            // Open the conversation.
24052bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang            final Conversation conversation = savedState.getParcelable(SAVED_CONVERSATION);
240687a8982af83168453b6dc219fe984bff68047bebAndy Huang            restoreConversation(conversation);
2407632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        }
2408632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
2409d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira        if (savedState.containsKey(SAVED_TOAST_BAR_OP)) {
24102bc8bc1f69faacaddab41ab61e1d2e8658f4181fAndy Huang            ToastBarOperation op = savedState.getParcelable(SAVED_TOAST_BAR_OP);
2411d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira            if (op != null) {
2412d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira                if (op.getType() == ToastBarOperation.UNDO) {
2413d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira                    onUndoAvailable(op);
24149d7519d18e8ddbcdd59b37eec7c85210fca0e8c2Andrew Sapperstein                } else if (op.getType() == ToastBarOperation.ERROR) {
24159d7519d18e8ddbcdd59b37eec7c85210fca0e8c2Andrew Sapperstein                    onError(mFolder, true);
2416d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira                }
2417d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira            }
2418d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira        }
2419b10212ef23a4e2317fc43c62dff9ad177b5bcf2cScott Kennedy        mFolderListFolder = savedState.getParcelable(SAVED_HIERARCHICAL_FOLDER);
2420649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal        final ConversationListFragment convListFragment = getConversationListFragment();
2421d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira        if (convListFragment != null) {
2422649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal            convListFragment.getAnimatedAdapter().onRestoreInstanceState(savedState);
2423d3367499e56116854ed637b1036fd71057ac0f49Mindy Pereira        }
2424b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal        /*
2425632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang         * Restore the state of selected conversations. This needs to be done after the correct mode
2426632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang         * is set and the action bar is fully initialized. If not, several key pieces of state
2427632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang         * information will be missing, and the split views may not be initialized correctly.
2428632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang         */
2429632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        restoreSelectedConversations(savedState);
2430b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal        // Order is important!!!
2431b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal        // The dialog listener needs to happen *after* the selected set is restored.
2432b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal
2433b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal        // If there has been an orientation change, and we need to recreate the listener for the
2434b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal        // confirm dialog fragment (delete/archive/...), then do it here.
2435b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal        if (mDialogAction != -1) {
243630c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            makeDialogListener(mDialogAction, mDialogFromSelectedSet,
243730c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                    getUndoCallbackForDestructiveActionsWithAutoAdvance(
243830c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                            mDialogAction, mCurrentConversation));
2439b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal        }
2440ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang
2441ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang        mInbox = savedState.getParcelable(SAVED_INBOX_KEY);
2442f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy
2443f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy        mConversationListScrollPositions.clear();
2444f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy        mConversationListScrollPositions.putAll(
2445f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy                savedState.getBundle(SAVED_CONVERSATION_LIST_SCROLL_POSITIONS));
2446632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
2447632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
2448649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal    /**
2449649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal     * Handle an intent to open the app. This method is called only when there is no saved state,
2450649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal     * so we need to set state that wasn't set before. It is correct to change the viewmode here
2451649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal     * since it has not been previously set.
24526aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     *
24536aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     * This method is called for a subset of the reasons mentioned in
24546aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     * {@link #onCreate(android.os.Bundle)}. Notably, this is called when launching the app from
24556aca689261cf43e16534e320dc4a34cadf31c41bVikram Aggarwal     * notifications, widgets, and shortcuts.
2456715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @param intent intent passed to the activity.
2457649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal     */
2458632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    private void handleIntent(Intent intent) {
2459e6459424b3e46d768865c908af1caa932f783095Andy Huang        LogUtils.d(LOG_TAG, "IN AAC.handleIntent. action=%s", intent.getAction());
2460632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        if (Intent.ACTION_VIEW.equals(intent.getAction())) {
2461632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            if (intent.hasExtra(Utils.EXTRA_ACCOUNT)) {
246226a20758413f0f8629ea71a0943304e3ae740165Tony Mantler                setAccount(Account.newInstance(intent.getStringExtra(Utils.EXTRA_ACCOUNT)));
2463632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            }
2464b9ca979fb93cc14246b449a2c02dc615c9e0cdd9Andy Huang            if (mAccount == null) {
2465b9ca979fb93cc14246b449a2c02dc615c9e0cdd9Andy Huang                return;
24665e478d2c4087b50764756b0edc95941cb6094f68Mindy Pereira            }
24671a249e094e961f463ff392390d591a1d37106e69Vikram Aggarwal            final boolean isConversationMode = intent.hasExtra(Utils.EXTRA_CONVERSATION);
24684fe0af81874976a1995191321e35c844b2229811Andy Huang
24694fe0af81874976a1995191321e35c844b2229811Andy Huang            if (intent.getBooleanExtra(Utils.EXTRA_FROM_NOTIFICATION, false)) {
24702168cbc9c58d2133bdf77ecd342c3d5ee755d607Régis Décamps                Analytics.getInstance().setEmail(mAccount.getEmailAddress(), mAccount.getType());
24714fe0af81874976a1995191321e35c844b2229811Andy Huang                Analytics.getInstance().sendEvent("notification_click",
24724fe0af81874976a1995191321e35c844b2229811Andy Huang                        isConversationMode ? "conversation" : "conversation_list", null, 0);
24734fe0af81874976a1995191321e35c844b2229811Andy Huang            }
24744fe0af81874976a1995191321e35c844b2229811Andy Huang
2475649b9ea3653da73fac0c33c8fb0af18baad12ac3Vikram Aggarwal            if (isConversationMode && mViewMode.getMode() == ViewMode.UNKNOWN) {
24761a249e094e961f463ff392390d591a1d37106e69Vikram Aggarwal                mViewMode.enterConversationMode();
24771a249e094e961f463ff392390d591a1d37106e69Vikram Aggarwal            } else {
24781a249e094e961f463ff392390d591a1d37106e69Vikram Aggarwal                mViewMode.enterConversationListMode();
24791a249e094e961f463ff392390d591a1d37106e69Vikram Aggarwal            }
2480177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            // Put the folder and conversation, and ask the loader to create this folder.
2481177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            final Bundle args = new Bundle();
248248cfe4613549cafdf36e2a524afba730522bf291Scott Kennedy
248348cfe4613549cafdf36e2a524afba730522bf291Scott Kennedy            final Uri folderUri;
248448cfe4613549cafdf36e2a524afba730522bf291Scott Kennedy            if (intent.hasExtra(Utils.EXTRA_FOLDER_URI)) {
24855bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein                folderUri = intent.getParcelableExtra(Utils.EXTRA_FOLDER_URI);
248648cfe4613549cafdf36e2a524afba730522bf291Scott Kennedy            } else if (intent.hasExtra(Utils.EXTRA_FOLDER)) {
248748cfe4613549cafdf36e2a524afba730522bf291Scott Kennedy                final Folder folder =
248848cfe4613549cafdf36e2a524afba730522bf291Scott Kennedy                        Folder.fromString(intent.getStringExtra(Utils.EXTRA_FOLDER));
2489259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy                folderUri = folder.folderUri.fullUri;
249048cfe4613549cafdf36e2a524afba730522bf291Scott Kennedy            } else {
249172727efe0bac563c72c208737c381296c35f94a9Scott Kennedy                final Bundle extras = intent.getExtras();
249272727efe0bac563c72c208737c381296c35f94a9Scott Kennedy                LogUtils.d(LOG_TAG, "Couldn't find a folder URI in the extras: %s",
249372727efe0bac563c72c208737c381296c35f94a9Scott Kennedy                        extras == null ? "null" : extras.toString());
249448cfe4613549cafdf36e2a524afba730522bf291Scott Kennedy                folderUri = mAccount.settings.defaultInbox;
249548cfe4613549cafdf36e2a524afba730522bf291Scott Kennedy            }
249648cfe4613549cafdf36e2a524afba730522bf291Scott Kennedy
24975bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein            // Check if we should load all conversations instead of using
24985bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein            // the default behavior which loads an initial subset.
24995bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein            mIgnoreInitialConversationLimit =
25005bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein                    intent.getBooleanExtra(Utils.EXTRA_IGNORE_INITIAL_CONVERSATION_LIMIT, false);
25015bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein
250260593354f1126b9687d09c3b11a852a3459f7118Scott Kennedy            args.putParcelable(Utils.EXTRA_FOLDER_URI, folderUri);
2503177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            args.putParcelable(Utils.EXTRA_CONVERSATION,
2504177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    intent.getParcelableExtra(Utils.EXTRA_CONVERSATION));
2505177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            restartOptionalLoader(LOADER_FIRST_FOLDER, mFolderCallbacks, args);
2506632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        } else if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
2507632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            if (intent.hasExtra(Utils.EXTRA_ACCOUNT)) {
2508f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal                mHaveSearchResults = false;
2509c6801eb828627c37b8992584767c095dfe11df62Jin Cao                // Save this search query for future suggestions
2510632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                final String query = intent.getStringExtra(SearchManager.QUERY);
2511c6801eb828627c37b8992584767c095dfe11df62Jin Cao                mSearchViewController.saveRecentQuery(query);
2512f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal                setAccount((Account) intent.getParcelableExtra(Utils.EXTRA_ACCOUNT));
2513f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal                fetchSearchFolder(intent);
2514f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal                if (shouldEnterSearchConvMode()) {
2515ac2548293e93c14b1d14e6de53328c561ba1cca6Mindy Pereira                    mViewMode.enterSearchResultsConversationMode();
2516ac2548293e93c14b1d14e6de53328c561ba1cca6Mindy Pereira                } else {
2517ac2548293e93c14b1d14e6de53328c561ba1cca6Mindy Pereira                    mViewMode.enterSearchResultsListMode();
2518ac2548293e93c14b1d14e6de53328c561ba1cca6Mindy Pereira                }
2519632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang            } else {
2520632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                LogUtils.e(LOG_TAG, "Missing account extra from search intent.  Finishing");
2521632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang                mActivity.finish();
25222d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook            }
25231ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal        }
2524632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        if (mAccount != null) {
2525177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            restartOptionalLoader(LOADER_ACCOUNT_UPDATE_CURSOR, mAccountCallbacks, Bundle.EMPTY);
2526b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy        }
2527b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy    }
2528b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy
25294556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang    /**
2530f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal     * Returns true if we should enter conversation mode with search.
2531f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal     */
2532f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal    protected final boolean shouldEnterSearchConvMode() {
253387a8982af83168453b6dc219fe984bff68047bebAndy Huang        return mHaveSearchResults && shouldShowFirstConversation();
2534f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal    }
2535f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal
2536f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal    /**
25374556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang     * Copy any selected conversations stored in the saved bundle into our selection set,
25384556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang     * triggering {@link ConversationSetObserver} callbacks as our selection set changes.
25394556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang     *
25404556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang     */
2541715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal    private void restoreSelectedConversations(Bundle savedState) {
2542967ede63e059810d9d4d425c50111d606014c893Mindy Pereira        if (savedState == null) {
2543ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao            mCheckedSet.clear();
2544967ede63e059810d9d4d425c50111d606014c893Mindy Pereira            return;
2545967ede63e059810d9d4d425c50111d606014c893Mindy Pereira        }
2546ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao        final ConversationCheckedSet selectedSet = savedState.getParcelable(SAVED_SELECTED_SET);
25474556a44bef944b90e140d2ba67cc723e0ad5da3aAndy Huang        if (selectedSet == null || selectedSet.isEmpty()) {
2548ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao            mCheckedSet.clear();
2549967ede63e059810d9d4d425c50111d606014c893Mindy Pereira            return;
2550967ede63e059810d9d4d425c50111d606014c893Mindy Pereira        }
2551632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
2552632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        // putAll will take care of calling our registered onSetPopulated method
2553ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao        mCheckedSet.putAll(selectedSet);
2554967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    }
2555967ede63e059810d9d4d425c50111d606014c893Mindy Pereira
255687a8982af83168453b6dc219fe984bff68047bebAndy Huang    protected void restoreConversation(Conversation conversation) {
255787a8982af83168453b6dc219fe984bff68047bebAndy Huang        if (conversation != null && conversation.position < 0) {
255887a8982af83168453b6dc219fe984bff68047bebAndy Huang            // Set the position to 0 on this conversation, as we don't know where it is
255987a8982af83168453b6dc219fe984bff68047bebAndy Huang            // in the list
256087a8982af83168453b6dc219fe984bff68047bebAndy Huang            conversation.position = 0;
256187a8982af83168453b6dc219fe984bff68047bebAndy Huang        }
256287a8982af83168453b6dc219fe984bff68047bebAndy Huang        showConversation(conversation);
256387a8982af83168453b6dc219fe984bff68047bebAndy Huang    }
256487a8982af83168453b6dc219fe984bff68047bebAndy Huang
2565ec5cbf79b825c6f96d45e85015319c66470b7e57Vikram Aggarwal    /**
256649e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * Show the conversation provided in the arguments. It is safe to pass a null conversation
256749e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * object, which is a signal to back out of conversation view mode.
256849e0e991fe8880852b936a0f8d7d57c5b36f2154Vikram Aggarwal     * Child classes must call super.showConversation() <b>before</b> their own implementations.
256959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal     * @param conversation the conversation to be shown, or null if we want to back out to list
257059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal     *                     mode.
2571177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal     * onLoadFinished(Loader, Cursor) on any callback.
2572ec5cbf79b825c6f96d45e85015319c66470b7e57Vikram Aggarwal     */
257330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao    protected void showConversation(Conversation conversation) {
257418512bd09af3a5e3d6e8ae2789a40968aa5e8f07Rohan Shah        showConversation(conversation, false /* shouldAnimate */);
2575d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao    }
2576d23f6d1cd1dd8248cba755bf91c5355a31fbb885Jin Cao
257718512bd09af3a5e3d6e8ae2789a40968aa5e8f07Rohan Shah    /**
257818512bd09af3a5e3d6e8ae2789a40968aa5e8f07Rohan Shah     * Helper method to allow for conversation view animation control. Implementing classes should
257918512bd09af3a5e3d6e8ae2789a40968aa5e8f07Rohan Shah     * directly override this to handle the animation.
258018512bd09af3a5e3d6e8ae2789a40968aa5e8f07Rohan Shah     * @param conversation
258118512bd09af3a5e3d6e8ae2789a40968aa5e8f07Rohan Shah     * @param shouldAnimate true if we want to animate the conversation in, false otherwise
258218512bd09af3a5e3d6e8ae2789a40968aa5e8f07Rohan Shah     */
258318512bd09af3a5e3d6e8ae2789a40968aa5e8f07Rohan Shah    protected void showConversation(Conversation conversation, boolean shouldAnimate) {
258418512bd09af3a5e3d6e8ae2789a40968aa5e8f07Rohan Shah        showConversationWithPeek(conversation, false /* peek */);
258518512bd09af3a5e3d6e8ae2789a40968aa5e8f07Rohan Shah    }
258618512bd09af3a5e3d6e8ae2789a40968aa5e8f07Rohan Shah
258718512bd09af3a5e3d6e8ae2789a40968aa5e8f07Rohan Shah    protected void showConversationWithPeek(Conversation conversation, boolean peek) {
2588243c23618b066bcdcd0ab9e36d8c01f50db2cbd0Andy Huang        if (conversation != null) {
2589243c23618b066bcdcd0ab9e36d8c01f50db2cbd0Andy Huang            Utils.sConvLoadTimer.start();
2590243c23618b066bcdcd0ab9e36d8c01f50db2cbd0Andy Huang        }
2591243c23618b066bcdcd0ab9e36d8c01f50db2cbd0Andy Huang
259254e925ec6eb14bf711af563e8e40979f07f17a68Andy Huang        MailLogService.log("AbstractActivityController", "showConversation(%s)", conversation);
2593c67182dfb4884e92262a2365ecfcc5d254c7999cVikram Aggarwal        // Set the current conversation just in case it wasn't already set.
2594c67182dfb4884e92262a2365ecfcc5d254c7999cVikram Aggarwal        setCurrentConversation(conversation);
2595ec5cbf79b825c6f96d45e85015319c66470b7e57Vikram Aggarwal    }
2596ec5cbf79b825c6f96d45e85015319c66470b7e57Vikram Aggarwal
2597e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal    /**
2598405a344937675f57fc9c6988b2b124410a270f13Jin Cao     * Show the wait for account initialization mode.
25992d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook     * Children can override this method, but they must call super.showWaitForInitialization().
26002d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook     */
2601405a344937675f57fc9c6988b2b124410a270f13Jin Cao    protected void showWaitForInitialization() {
26022d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook        mViewMode.enterWaitingForInitializationMode();
2603c96efcc4a2f65b11a9b2057320ca828ee80f32ffAndy Huang        mWaitFragment = WaitFragment.newInstance(mAccount, true /* expectingMessages */);
26042d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook    }
26052d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook
2606dd6a7ce32c4003bd0941e2f18fcf5b80b5cd43c5Vikram Aggarwal    private void updateWaitMode() {
26072d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook        final FragmentManager manager = mActivity.getFragmentManager();
26082d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook        final WaitFragment waitFragment =
26096902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal                (WaitFragment)manager.findFragmentByTag(TAG_WAIT);
26102d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook        if (waitFragment != null) {
26112d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook            waitFragment.updateAccount(mAccount);
26122d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook        }
26132d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook    }
26142d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook
2615a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal    /**
2616a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal     * Remove the "Waiting for Initialization" fragment. Child classes are free to override this
2617a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal     * method, though they must call the parent implementation <b>after</b> they do anything.
2618a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal     */
2619a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal    protected void hideWaitForInitialization() {
2620a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal        mWaitFragment = null;
2621a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal    }
2622a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal
2623a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal    /**
2624a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal     * Use the instance variable and the wait fragment's tag to get the wait fragment.  This is
2625a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal     * far superior to using the value of mWaitFragment, which might be invalid or might refer
2626a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal     * to a fragment after it has been destroyed.
2627715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @return a wait fragment that is already attached to the activity, if one exists
2628a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal     */
2629a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal    protected final WaitFragment getWaitFragment() {
2630a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal        final FragmentManager manager = mActivity.getFragmentManager();
2631a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal        final WaitFragment waitFrag = (WaitFragment) manager.findFragmentByTag(TAG_WAIT);
2632a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal        if (waitFrag != null) {
2633a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal            // The Fragment Manager knows better, so use its instance.
2634a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal            mWaitFragment = waitFrag;
2635a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal        }
2636a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal        return mWaitFragment;
2637a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal    }
2638dd6a7ce32c4003bd0941e2f18fcf5b80b5cd43c5Vikram Aggarwal
263948b2a6c5fe209f0603fdf85d35cf399f7f7d462eVikram Aggarwal    /**
264048b2a6c5fe209f0603fdf85d35cf399f7f7d462eVikram Aggarwal     * Returns true if we are waiting for the account to sync, and cannot show any folders or
264148b2a6c5fe209f0603fdf85d35cf399f7f7d462eVikram Aggarwal     * conversation for the current account yet.
264248b2a6c5fe209f0603fdf85d35cf399f7f7d462eVikram Aggarwal     */
2643dd6a7ce32c4003bd0941e2f18fcf5b80b5cd43c5Vikram Aggarwal    private boolean inWaitMode() {
2644a3f43d4c2423ab03bdae925042fe8440b7f70025Vikram Aggarwal        final WaitFragment waitFragment = getWaitFragment();
26452d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook        if (waitFragment != null) {
26462d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook            final Account fragmentAccount = waitFragment.getAccount();
2647339004b1d81b34682ef56f6ea0b3b9fccc133753Paul Westbrook            return fragmentAccount != null && fragmentAccount.uri.equals(mAccount.uri) &&
26482d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook                    mViewMode.getMode() == ViewMode.WAITING_FOR_ACCOUNT_INITIALIZATION;
26492d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook        }
26502d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook        return false;
26512d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook    }
26522d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook
26532d50bcdca57756ecad20d21da0d936243215b79cPaul Westbrook    /**
2654405a344937675f57fc9c6988b2b124410a270f13Jin Cao     * Show the conversation List with the list context provided here. On certain layouts, this
2655405a344937675f57fc9c6988b2b124410a270f13Jin Cao     * might show more than just the conversation list. For instance, on tablets this might show
2656405a344937675f57fc9c6988b2b124410a270f13Jin Cao     * the conversations along with the conversation list.
2657405a344937675f57fc9c6988b2b124410a270f13Jin Cao     * @param listContext context providing information on what conversation list to display.
2658e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal     */
2659405a344937675f57fc9c6988b2b124410a270f13Jin Cao    protected abstract void showConversationList(ConversationListContext listContext);
2660e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal
26611ddcf0f2bf44d3c9db89112ef52510d9b2433ac4Vikram Aggarwal    @Override
26620b69338a45faa422ccba8faf64c9816c55d33e4aJin Cao    public void onConversationSelected(Conversation conversation, boolean inLoaderCallbacks) {
2663fe8e081d6230f343dc74b45d69e5f4634a332b5aAlice Yang        final ConversationListFragment convListFragment = getConversationListFragment();
2664fe8e081d6230f343dc74b45d69e5f4634a332b5aAlice Yang        if (convListFragment != null && convListFragment.getAnimatedAdapter() != null) {
2665fe8e081d6230f343dc74b45d69e5f4634a332b5aAlice Yang            convListFragment.getAnimatedAdapter().onConversationSelected();
2666fe8e081d6230f343dc74b45d69e5f4634a332b5aAlice Yang        }
2667aa55bc9cb210a46a950874f368baa07f041a4302mindyp        // Only animate destructive actions if we are going to be showing the
2668aa55bc9cb210a46a950874f368baa07f041a4302mindyp        // conversation list when we show the next conversation.
2669bcb16b98140a83a4db3c51568d21c839595f73dfVikram Aggarwal        commitDestructiveActions(mIsTablet);
267018512bd09af3a5e3d6e8ae2789a40968aa5e8f07Rohan Shah        showConversation(conversation, true /* shouldAnimate */);
26711ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    }
26721ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang
26731ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    @Override
26742f542873183b37fa38a0d468f46bbfebc1f537e2Andrew Sapperstein    public final void onCabModeEntered() {
26752f542873183b37fa38a0d468f46bbfebc1f537e2Andrew Sapperstein        final ConversationListFragment convListFragment = getConversationListFragment();
26762f542873183b37fa38a0d468f46bbfebc1f537e2Andrew Sapperstein        if (convListFragment != null && convListFragment.getAnimatedAdapter() != null) {
26772f542873183b37fa38a0d468f46bbfebc1f537e2Andrew Sapperstein            convListFragment.getAnimatedAdapter().onCabModeEntered();
26782f542873183b37fa38a0d468f46bbfebc1f537e2Andrew Sapperstein        }
26792f542873183b37fa38a0d468f46bbfebc1f537e2Andrew Sapperstein    }
26802f542873183b37fa38a0d468f46bbfebc1f537e2Andrew Sapperstein
26812f542873183b37fa38a0d468f46bbfebc1f537e2Andrew Sapperstein    @Override
2682cc139833bc5829ba70244519e9870de783c41848Scott Kennedy    public final void onCabModeExited() {
2683cc139833bc5829ba70244519e9870de783c41848Scott Kennedy        final ConversationListFragment convListFragment = getConversationListFragment();
2684cc139833bc5829ba70244519e9870de783c41848Scott Kennedy        if (convListFragment != null && convListFragment.getAnimatedAdapter() != null) {
2685cc139833bc5829ba70244519e9870de783c41848Scott Kennedy            convListFragment.getAnimatedAdapter().onCabModeExited();
2686cc139833bc5829ba70244519e9870de783c41848Scott Kennedy        }
2687cc139833bc5829ba70244519e9870de783c41848Scott Kennedy    }
2688cc139833bc5829ba70244519e9870de783c41848Scott Kennedy
2689cc139833bc5829ba70244519e9870de783c41848Scott Kennedy    @Override
26901ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang    public Conversation getCurrentConversation() {
26911ee96b2b100546b5b69ad42c5bc3755a4293d1a3Andy Huang        return mCurrentConversation;
26927d602884ed70d07a42f1adf999fdf51b258f23f9Vikram Aggarwal    }
2693555140c7b7d5129fb2b17df66106cbd6eeef37a0Mindy Pereira
2694c67182dfb4884e92262a2365ecfcc5d254c7999cVikram Aggarwal    /**
2695c67182dfb4884e92262a2365ecfcc5d254c7999cVikram Aggarwal     * Set the current conversation. This is the conversation on which all actions are performed.
2696c67182dfb4884e92262a2365ecfcc5d254c7999cVikram Aggarwal     * Do not modify mCurrentConversation except through this method, which makes it easy to
2697c67182dfb4884e92262a2365ecfcc5d254c7999cVikram Aggarwal     * perform common actions associated with changing the current conversation.
2698715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @param conversation new conversation to view. Passing null indicates that we are backing
2699715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     *                     out to conversation list mode.
2700c67182dfb4884e92262a2365ecfcc5d254c7999cVikram Aggarwal     */
2701632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
2702632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public void setCurrentConversation(Conversation conversation) {
2703a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        // The controller should come out of detached mode if a new conversation is viewed, or if
270481230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal        // we are going back to conversation list mode.
270581230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal        if (mDetachedConvUri != null && (conversation == null
270681230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal                || !mDetachedConvUri.equals(conversation.uri))) {
2707a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal            clearDetachedMode();
2708a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        }
2709a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal
2710a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        // Must happen *before* setting mCurrentConversation because this sets
2711a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        // conversation.position if a cursor is available.
27128883f228890572a8d85d06b1e0bf08c65bd42db4Andy Huang        mTracker.initialize(conversation);
271317f373ecf6961112f77a7516ab1bbcb4fcdf2884Vikram Aggarwal        mCurrentConversation = conversation;
27147d64612104fd861474a09994f13768360f92041dAndy Huang
27157d64612104fd861474a09994f13768360f92041dAndy Huang        if (mCurrentConversation != null) {
27162d86d1123c4183024222dd2eb60f7cbdc24953fdAndrew Sapperstein            mActionBarController.setCurrentConversation(mCurrentConversation);
2717f807ba7ac1011f7fd1e0d382dcc0518ba3529810Yorke Lee            mActivity.invalidateOptionsMenu();
27187d64612104fd861474a09994f13768360f92041dAndy Huang        }
27195040f1a73e3c5684901d8a9e2e30a5d58286509fMindy Pereira    }
27205040f1a73e3c5684901d8a9e2e30a5d58286509fMindy Pereira
2721a9b93f35035dc4badd0ac40f4016e1ff809b26b0Vikram Aggarwal    /**
272287a8982af83168453b6dc219fe984bff68047bebAndy Huang     * Invoked by {@link ConversationPagerAdapter} when a new page in the ViewPager is selected.
272387a8982af83168453b6dc219fe984bff68047bebAndy Huang     *
272487a8982af83168453b6dc219fe984bff68047bebAndy Huang     * @param conversation the conversation of the now currently visible fragment
272587a8982af83168453b6dc219fe984bff68047bebAndy Huang     *
272687a8982af83168453b6dc219fe984bff68047bebAndy Huang     */
272787a8982af83168453b6dc219fe984bff68047bebAndy Huang    @Override
272887a8982af83168453b6dc219fe984bff68047bebAndy Huang    public void onConversationViewSwitched(Conversation conversation) {
272987a8982af83168453b6dc219fe984bff68047bebAndy Huang        setCurrentConversation(conversation);
273087a8982af83168453b6dc219fe984bff68047bebAndy Huang    }
273187a8982af83168453b6dc219fe984bff68047bebAndy Huang
273287a8982af83168453b6dc219fe984bff68047bebAndy Huang    @Override
273387a8982af83168453b6dc219fe984bff68047bebAndy Huang    public boolean isCurrentConversationJustPeeking() {
273487a8982af83168453b6dc219fe984bff68047bebAndy Huang        return false;
273587a8982af83168453b6dc219fe984bff68047bebAndy Huang    }
273687a8982af83168453b6dc219fe984bff68047bebAndy Huang
273787a8982af83168453b6dc219fe984bff68047bebAndy Huang    /**
2738f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huang     * {@link LoaderManager} currently has a bug in
2739f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huang     * {@link LoaderManager#restartLoader(int, Bundle, android.app.LoaderManager.LoaderCallbacks)}
2740f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huang     * where, if a previous onCreateLoader returned a null loader, this method will NPE. Work around
2741f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huang     * this bug by destroying any loaders that may have been created as null (essentially because
2742f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huang     * they are optional loads, and may not apply to a particular account).
2743f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huang     * <p>
2744f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huang     * A simple null check before restarting a loader will not work, because that would not
2745f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huang     * give the controller a chance to invalidate UI corresponding the prior loader result.
2746f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huang     *
2747f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huang     * @param id loader ID to safely restart
2748177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal     * @param handler the LoaderCallback which will handle this loader ID.
2749177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal     * @param args arguments, if any, to be passed to the loader. Use {@link Bundle#EMPTY} if no
2750177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal     *             arguments need to be specified.
2751f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huang     */
2752177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    private void restartOptionalLoader(int id, LoaderManager.LoaderCallbacks handler, Bundle args) {
2753f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huang        final LoaderManager lm = mActivity.getLoaderManager();
2754f9a7348ff7eddd83527e1cf7badf3801d90c77c7Andy Huang        lm.destroyLoader(id);
2755177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        lm.restartLoader(id, args, handler);
275694c94de8f36698be849da8f103387e7e4d744d42Vikram Aggarwal    }
275794c94de8f36698be849da8f103387e7e4d744d42Vikram Aggarwal
2758632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
2759632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public void registerConversationListObserver(DataSetObserver observer) {
2760632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        mConversationListObservable.registerObserver(observer);
2761632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
2762632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
2763632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
2764632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    public void unregisterConversationListObserver(DataSetObserver observer) {
2765647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang        try {
2766647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang            mConversationListObservable.unregisterObserver(observer);
2767647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang        } catch (IllegalStateException e) {
2768647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang            // Log instead of crash
2769647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang            LogUtils.e(LOG_TAG, e, "unregisterConversationListObserver called for an observer that "
2770647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang                    + "hasn't been registered");
2771647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang        }
2772632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
2773632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
2774090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    @Override
2775090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    public void registerFolderObserver(DataSetObserver observer) {
2776090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang        mFolderObservable.registerObserver(observer);
2777090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    }
2778090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang
2779090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    @Override
2780090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    public void unregisterFolderObserver(DataSetObserver observer) {
2781647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang        try {
2782647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang            mFolderObservable.unregisterObserver(observer);
2783647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang        } catch (IllegalStateException e) {
2784647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang            // Log instead of crash
2785647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang            LogUtils.e(LOG_TAG, e, "unregisterFolderObserver called for an observer that "
2786647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang                    + "hasn't been registered");
2787647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang        }
2788090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang    }
2789090db1ec6fef366ab9e319040a00eca078ed41c3Andy Huang
27909d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang    @Override
27919d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang    public void registerConversationLoadedObserver(DataSetObserver observer) {
27929d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang        mPagerController.registerConversationLoadedObserver(observer);
27939d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang    }
27949d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang
27959d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang    @Override
27969d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang    public void unregisterConversationLoadedObserver(DataSetObserver observer) {
2797647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang        try {
2798647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang            mPagerController.unregisterConversationLoadedObserver(observer);
2799647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang        } catch (IllegalStateException e) {
2800647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang            // Log instead of crash
2801647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang            LogUtils.e(LOG_TAG, e, "unregisterConversationLoadedObserver called for an observer "
2802647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang                    + "that hasn't been registered");
2803647aefbb36d4541362495e10e2a328d38ff8d2a2Alice Yang        }
28049d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang    }
28059d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang
28066006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal    /**
2807c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal     * Returns true if the number of accounts is different, or if the current account has
2808c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal     * changed. This method is meant to filter frequent changes to the list of
2809c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal     * accounts, and only return true if the new list is substantially different from the existing
2810c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal     * list. Returning true is safe here, it leads to more work in creating the
2811c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal     * same account list again.
2812715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @param accountCursor the cursor which points to all the accounts.
2813715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @return true if the number of accounts is changed or current account missing from the list.
28146006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal     */
2815177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    private boolean accountsUpdated(ObjectCursor<Account> accountCursor) {
281623b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        // Check to see if the current account hasn't been set, or the account cursor is empty
281723b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        if (mAccount == null || !accountCursor.moveToFirst()) {
28186c51158ad3269f157424e6c7bd488425c98da08fVikram Aggarwal            return true;
281923b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        }
282023b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook
282123b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        // Check to see if the number of accounts are different, from the number we saw on the last
282223b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        // updated
282323b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        if (mCurrentAccountUris.size() != accountCursor.getCount()) {
282423b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook            return true;
282523b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        }
282623b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook
282723b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        // Check to see if the account list is different or if the current account is not found in
282823b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        // the cursor.
282923b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        boolean foundCurrentAccount = false;
28307dedb952c8bb9c7b50f25444a42047d903e5121bVikram Aggarwal        do {
2831c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal            final Account account = accountCursor.getModel();
2832c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal            if (!foundCurrentAccount && mAccount.uri.equals(account.uri)) {
2833c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal                if (mAccount.settingsDiffer(account)) {
2834c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal                    // Settings changed, and we don't need to look any further.
2835c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal                    return true;
2836c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal                }
283723b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook                foundCurrentAccount = true;
283823b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook            }
28396006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal            // Is there a new account that we do not know about?
2840c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal            if (!mCurrentAccountUris.contains(account.uri)) {
284123b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook                return true;
284223b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook            }
28437dedb952c8bb9c7b50f25444a42047d903e5121bVikram Aggarwal        } while (accountCursor.moveToNext());
284423b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook
284523b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        // As long as we found the current account, the list hasn't been updated
284623b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        return !foundCurrentAccount;
28477dedb952c8bb9c7b50f25444a42047d903e5121bVikram Aggarwal    }
28487dedb952c8bb9c7b50f25444a42047d903e5121bVikram Aggarwal
28497dedb952c8bb9c7b50f25444a42047d903e5121bVikram Aggarwal    /**
28506006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal     * Updates accounts for the app. If the current account is missing, the first
28516006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal     * account in the list is set to the current account (we <em>have</em> to choose something).
2852161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira     *
28536c51158ad3269f157424e6c7bd488425c98da08fVikram Aggarwal     * @param accounts cursor into the AccountCache
28547dedb952c8bb9c7b50f25444a42047d903e5121bVikram Aggarwal     * @return true if the update was successful, false otherwise
28557dedb952c8bb9c7b50f25444a42047d903e5121bVikram Aggarwal     */
2856177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    private boolean updateAccounts(ObjectCursor<Account> accounts) {
28577dedb952c8bb9c7b50f25444a42047d903e5121bVikram Aggarwal        if (accounts == null || !accounts.moveToFirst()) {
28587dedb952c8bb9c7b50f25444a42047d903e5121bVikram Aggarwal            return false;
28597dedb952c8bb9c7b50f25444a42047d903e5121bVikram Aggarwal        }
286023b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook
2861a9b93f35035dc4badd0ac40f4016e1ff809b26b0Vikram Aggarwal        final Account[] allAccounts = Account.getAllAccounts(accounts);
28626006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal        // A match for the current account's URI in the list of accounts.
28636006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal        Account currentFromList = null;
286423b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook
2865636c1a1bf144e28a8a1ab022dfcfba83c7b1ca1bVikram Aggarwal        // Save the uris for the accounts and find the current account in the updated cursor.
286623b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        mCurrentAccountUris.clear();
2867636c1a1bf144e28a8a1ab022dfcfba83c7b1ca1bVikram Aggarwal        for (final Account account : allAccounts) {
28686006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal            LogUtils.d(LOG_TAG, "updateAccounts(%s)", account);
286923b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook            mCurrentAccountUris.add(account.uri);
28706006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal            if (mAccount != null && account.uri.equals(mAccount.uri)) {
28716006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal                currentFromList = account;
28726006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal            }
287323b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        }
287423b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook
28756006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal        // 1. current account is already set and is in allAccounts:
28766006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal        //    1a. It has changed -> load the updated account.
2877406ae08b92f78f26fe83830897570c9db1ab23aaGreg Bullock        //    1b. It is unchanged -> no-op
28780d647354209f4bf57cffa35f008e09853977b189Andy Huang        // 2. current account is set and is not in allAccounts -> pick first (acct was deleted?)
28796006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal        // 3. saved preference has an account -> pick that one
28800d647354209f4bf57cffa35f008e09853977b189Andy Huang        // 4. otherwise just pick first
28810d647354209f4bf57cffa35f008e09853977b189Andy Huang
28826006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal        boolean accountChanged = false;
28836006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal        /// Assume case 4, initialize to first account, and see if we can find anything better.
28846006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal        Account newAccount = allAccounts[0];
28856006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal        if (currentFromList != null) {
28866006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal            // Case 1: Current account exists but has changed
28876006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal            if (!currentFromList.equals(mAccount)) {
28886006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal                newAccount = currentFromList;
28896006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal                accountChanged = true;
28900d647354209f4bf57cffa35f008e09853977b189Andy Huang            }
28916006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal            // Case 1b: else, current account is unchanged: nothing to do.
289223b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        } else {
28936006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal            // Case 2: Current account is not in allAccounts, the account needs to change.
28946006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal            accountChanged = true;
28956006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal            if (mAccount == null) {
28966006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal                // Case 3: Check for last viewed account, and check if it exists in the list.
28976006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal                final String lastAccountUri = MailAppProvider.getInstance().getLastViewedAccount();
28986006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal                if (lastAccountUri != null) {
28996006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal                    for (final Account account : allAccounts) {
29006006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal                        if (lastAccountUri.equals(account.uri.toString())) {
29016006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal                            newAccount = account;
29026006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal                            break;
29036006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal                        }
29040d647354209f4bf57cffa35f008e09853977b189Andy Huang                    }
29050d647354209f4bf57cffa35f008e09853977b189Andy Huang                }
29060d647354209f4bf57cffa35f008e09853977b189Andy Huang            }
290723b74b988a4f79a5dfc9e6f8c6e9d3b7d2a2ae27Paul Westbrook        }
29086006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal        if (accountChanged) {
29095fd8afd5c1b40580c069a5a97039ca0601208216Vikram Aggarwal            changeAccount(newAccount);
29106006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal        }
291107dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal
29126006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal        // Whether we have updated the current account or not, we need to update the list of
29136006991395131d7d1db4f883ff8c7a87498b6eecVikram Aggarwal        // accounts in the ActionBar.
2914b905f0ea18ee0dc5774adb29c57cd7c122058c71Rohan Shah        mAllAccounts = allAccounts;
291507dbaa6989ea5c63ecf79b5ce7ec3a6a18a1ff35Vikram Aggarwal        mAllAccountObservers.notifyChanged();
2916a9b93f35035dc4badd0ac40f4016e1ff809b26b0Vikram Aggarwal        return (allAccounts.length > 0);
29177dedb952c8bb9c7b50f25444a42047d903e5121bVikram Aggarwal    }
29187dedb952c8bb9c7b50f25444a42047d903e5121bVikram Aggarwal
29196ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook    private void disableNotifications() {
29206ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook        mNewEmailReceiver.activate(mContext, this);
29216ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook    }
29226ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook
29236ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook    private void enableNotifications() {
29246ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook        mNewEmailReceiver.deactivate();
29256ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook    }
29266ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook
29276ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook    private void disableNotificationsOnAccountChange(Account account) {
29286ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook        // If the new mail suppression receiver is activated for a different account, we want to
29296ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook        // activate it for the new account.
29306ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook        if (mNewEmailReceiver.activated() &&
29316ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook                !mNewEmailReceiver.notificationsDisabledForAccount(account)) {
29326ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook            // Deactivate the current receiver, otherwise multiple receivers may be registered.
29336ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook            mNewEmailReceiver.deactivate();
29346ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook            mNewEmailReceiver.activate(mContext, this);
29356ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook        }
29366ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook    }
29376ead20dd9643e6435d6c1111f99c520870812ea8Paul Westbrook
2938a9b93f35035dc4badd0ac40f4016e1ff809b26b0Vikram Aggarwal    /**
2939c7694221dfa5cec3f4ae290f2266b081b2639d80Vikram Aggarwal     * Destructive actions on Conversations. This class should only be created by controllers, and
2940c7694221dfa5cec3f4ae290f2266b081b2639d80Vikram Aggarwal     * clients should only require {@link DestructiveAction}s, not specific implementations of the.
2941c7694221dfa5cec3f4ae290f2266b081b2639d80Vikram Aggarwal     * Only the controllers should know what kind of destructive actions are being created.
2942c7694221dfa5cec3f4ae290f2266b081b2639d80Vikram Aggarwal     */
2943de3e74a82043733243c7391d7f983a5af8842891Mindy Pereira    public class ConversationAction implements DestructiveAction {
2944acaa3c0aa2335e0a635601e09c955388d698dfdaVikram Aggarwal        /**
2945acaa3c0aa2335e0a635601e09c955388d698dfdaVikram Aggarwal         * The action to be performed. This is specified as the resource ID of the menu item
2946acaa3c0aa2335e0a635601e09c955388d698dfdaVikram Aggarwal         * corresponding to this action: R.id.delete, R.id.report_spam, etc.
2947acaa3c0aa2335e0a635601e09c955388d698dfdaVikram Aggarwal         */
29487f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal        private final int mAction;
2949bc67bb1211654e69b1233e0f8759771873ca773eVikram Aggarwal        /** The action will act upon these conversations */
295077eee625cd8c317c605acfd06cd3a7e22120a0fdPaul Westbrook        private final Collection<Conversation> mTarget;
29517f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal        /** Whether this destructive action has already been performed */
29527f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal        private boolean mCompleted;
2953c41139509b1eb6dfc9bdae88ccc7d16eac85e2cfVikram Aggarwal        /** Whether this is an action on the currently selected set. */
2954c41139509b1eb6dfc9bdae88ccc7d16eac85e2cfVikram Aggarwal        private final boolean mIsSelectedSet;
2955e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal
295630c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        private UndoCallback mCallback;
295730c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao
2958fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira        /**
2959715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal         * Create a listener object.
2960715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal         * @param action action is one of four constants: R.id.y_button (archive),
2961fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira         * R.id.delete , R.id.mute, and R.id.report_spam.
2962bc67bb1211654e69b1233e0f8759771873ca773eVikram Aggarwal         * @param target Conversation that we want to apply the action to.
2963c41139509b1eb6dfc9bdae88ccc7d16eac85e2cfVikram Aggarwal         * @param isBatch whether the conversations are in the currently selected batch set.
2964c41139509b1eb6dfc9bdae88ccc7d16eac85e2cfVikram Aggarwal         */
2965c41139509b1eb6dfc9bdae88ccc7d16eac85e2cfVikram Aggarwal        public ConversationAction(int action, Collection<Conversation> target, boolean isBatch) {
2966fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira            mAction = action;
296777eee625cd8c317c605acfd06cd3a7e22120a0fdPaul Westbrook            mTarget = ImmutableList.copyOf(target);
2968c41139509b1eb6dfc9bdae88ccc7d16eac85e2cfVikram Aggarwal            mIsSelectedSet = isBatch;
2969fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira        }
2970fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira
297130c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        @Override
297230c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        public void setUndoCallback(UndoCallback undoCallback) {
297330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            mCallback = undoCallback;
297430c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        }
297530c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao
2976acaa3c0aa2335e0a635601e09c955388d698dfdaVikram Aggarwal        /**
2977acaa3c0aa2335e0a635601e09c955388d698dfdaVikram Aggarwal         * The action common to child classes. This performs the action specified in the constructor
2978acaa3c0aa2335e0a635601e09c955388d698dfdaVikram Aggarwal         * on the conversations given here.
2979acaa3c0aa2335e0a635601e09c955388d698dfdaVikram Aggarwal         */
2980bc67bb1211654e69b1233e0f8759771873ca773eVikram Aggarwal        @Override
2981bc67bb1211654e69b1233e0f8759771873ca773eVikram Aggarwal        public void performAction() {
29827f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal            if (isPerformed()) {
29837f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal                return;
29847f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal            }
29853cb28b5e1598f74dedbdb87b187d9bd7e132ff0fMindy Pereira            boolean undoEnabled = mAccount.supportsCapability(AccountCapabilities.UNDO);
29867dd054e39986de84a213c56a3c11ac94731402e6Vikram Aggarwal
29877dd054e39986de84a213c56a3c11ac94731402e6Vikram Aggarwal            // Are we destroying the currently shown conversation? Show the next one.
29887dd054e39986de84a213c56a3c11ac94731402e6Vikram Aggarwal            if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)){
29898742a614a238c5ae55d767461000d0a10d1da4d4Vikram Aggarwal                LogUtils.d(LOG_TAG, "ConversationAction.performAction():"
29908742a614a238c5ae55d767461000d0a10d1da4d4Vikram Aggarwal                        + "\nmTarget=%s\nCurrent=%s",
29917dd054e39986de84a213c56a3c11ac94731402e6Vikram Aggarwal                        Conversation.toString(mTarget), mCurrentConversation);
29927dd054e39986de84a213c56a3c11ac94731402e6Vikram Aggarwal            }
29937dd054e39986de84a213c56a3c11ac94731402e6Vikram Aggarwal
2994e1221d2807826e2ee272df81c271a0f444522890Paul Westbrook            if (mConversationListCursor == null) {
2995e1221d2807826e2ee272df81c271a0f444522890Paul Westbrook                LogUtils.e(LOG_TAG, "null ConversationCursor in ConversationAction.performAction():"
2996e1221d2807826e2ee272df81c271a0f444522890Paul Westbrook                        + "\nmTarget=%s\nCurrent=%s",
2997e1221d2807826e2ee272df81c271a0f444522890Paul Westbrook                        Conversation.toString(mTarget), mCurrentConversation);
2998e1221d2807826e2ee272df81c271a0f444522890Paul Westbrook                return;
2999e1221d2807826e2ee272df81c271a0f444522890Paul Westbrook            }
3000e1221d2807826e2ee272df81c271a0f444522890Paul Westbrook
30012b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            if (mAction == R.id.archive) {
30022b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                LogUtils.d(LOG_TAG, "Archiving");
300330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                mConversationListCursor.archive(mTarget, mCallback);
30042b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            } else if (mAction == R.id.delete) {
30052b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                LogUtils.d(LOG_TAG, "Deleting");
300630c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                mConversationListCursor.delete(mTarget, mCallback);
30072b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                if (mFolder.supportsCapability(FolderCapabilities.DELETE_ACTION_FINAL)) {
30082b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                    undoEnabled = false;
30092b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                }
30102b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            } else if (mAction == R.id.mute) {
30112b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                LogUtils.d(LOG_TAG, "Muting");
30122b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                if (mFolder.supportsCapability(FolderCapabilities.DESTRUCTIVE_MUTE)) {
30132b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                    for (Conversation c : mTarget) {
30142b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                        c.localDeleteOnUpdate = true;
3015bc67bb1211654e69b1233e0f8759771873ca773eVikram Aggarwal                    }
30162b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                }
301730c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                mConversationListCursor.mute(mTarget, mCallback);
30182b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            } else if (mAction == R.id.report_spam) {
30192b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                LogUtils.d(LOG_TAG, "Reporting spam");
302030c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                mConversationListCursor.reportSpam(mTarget, mCallback);
30212b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            } else if (mAction == R.id.mark_not_spam) {
30222b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                LogUtils.d(LOG_TAG, "Marking not spam");
302330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                mConversationListCursor.reportNotSpam(mTarget, mCallback);
30242b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            } else if (mAction == R.id.report_phishing) {
30252b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                LogUtils.d(LOG_TAG, "Reporting phishing");
302630c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                mConversationListCursor.reportPhishing(mTarget, mCallback);
30272b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            } else if (mAction == R.id.remove_star) {
30282b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                LogUtils.d(LOG_TAG, "Removing star");
30292b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                // Star removal is destructive in the Starred folder.
30302b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                mConversationListCursor.updateBoolean(mTarget, ConversationColumns.STARRED,
30312b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                        false);
30322b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            } else if (mAction == R.id.mark_not_important) {
30332b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                LogUtils.d(LOG_TAG, "Marking not-important");
30342b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                // Marking not important is destructive in a mailbox
30352b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                // containing only important messages
30362b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                if (mFolder != null && mFolder.isImportantOnly()) {
30372b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                    for (Conversation conv : mTarget) {
30382b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                        conv.localDeleteOnUpdate = true;
3039445be2114aed6b28cab53b488735d4bb812353a5Mindy Pereira                    }
30402b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                }
30412b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                mConversationListCursor.updateInt(mTarget, ConversationColumns.PRIORITY,
30422b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                        UIProvider.ConversationPriority.LOW);
30432b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy            } else if (mAction == R.id.discard_drafts) {
30442b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                LogUtils.d(LOG_TAG, "Discarding draft messages");
30452b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                // Discarding draft messages is destructive in a "draft" mailbox
30462b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                if (mFolder != null && mFolder.isDraft()) {
30472b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                    for (Conversation conv : mTarget) {
30482b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                        conv.localDeleteOnUpdate = true;
3049ef3625472596326b910a4445307d1a8eb8c9cc3fPaul Westbrook                    }
30502b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                }
30512b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                mConversationListCursor.discardDrafts(mTarget);
30522b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                // We don't support undoing discarding drafts
30532b9d80eb16156173f11a02eae4f770d8c975927cScott Kennedy                undoEnabled = false;
3054512821c11d89d49908f3cfdee0b582601f500f3dJin Cao            } else if (mAction == R.id.discard_outbox) {
3055512821c11d89d49908f3cfdee0b582601f500f3dJin Cao                LogUtils.d(LOG_TAG, "Discarding failed messages in Outbox");
3056512821c11d89d49908f3cfdee0b582601f500f3dJin Cao                mConversationListCursor.moveFailedIntoDrafts(mTarget);
3057512821c11d89d49908f3cfdee0b582601f500f3dJin Cao                undoEnabled = false;
30583d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal            }
30592cb6c1ca2b508331ee29d16b71f5ec024cd22555Jin Cao            if (undoEnabled && mTarget.size() > 0) {
3060ead5039b6d606fcc39668a472f8973cbd0bef4e1mindyp                mHandler.postDelayed(new Runnable() {
3061ead5039b6d606fcc39668a472f8973cbd0bef4e1mindyp                    @Override
3062ead5039b6d606fcc39668a472f8973cbd0bef4e1mindyp                    public void run() {
3063ead5039b6d606fcc39668a472f8973cbd0bef4e1mindyp                        onUndoAvailable(new ToastBarOperation(mTarget.size(), mAction,
30646a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                                ToastBarOperation.UNDO, mIsSelectedSet, mFolder));
3065ead5039b6d606fcc39668a472f8973cbd0bef4e1mindyp                    }
3066ead5039b6d606fcc39668a472f8973cbd0bef4e1mindyp                }, mShowUndoBarDelay);
30673d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal            }
3068bc67bb1211654e69b1233e0f8759771873ca773eVikram Aggarwal            refreshConversationList();
3069c41139509b1eb6dfc9bdae88ccc7d16eac85e2cfVikram Aggarwal            if (mIsSelectedSet) {
3070ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao                mCheckedSet.clear();
3071c41139509b1eb6dfc9bdae88ccc7d16eac85e2cfVikram Aggarwal            }
3072fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira        }
30737f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal
30747f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal        /**
30757f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal         * Returns true if this action has been performed, false otherwise.
3076839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang         *
30777f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal         */
30787f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal        private synchronized boolean isPerformed() {
30797f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal            if (mCompleted) {
30807f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal                return true;
30817f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal            }
30827f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal            mCompleted = true;
30837f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal            return false;
30847f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal        }
3085fbe4019a262f6b1934a44e598009ae63dc5745eeMindy Pereira    }
3086e5f4dc0661dad02e2cb39ffa62ff2157147ac387Mindy Pereira
3087d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal    // Called from the FolderSelectionDialog after a user is done selecting folders to assign the
3088d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal    // conversations to.
3089e5f4dc0661dad02e2cb39ffa62ff2157147ac387Mindy Pereira    @Override
30908db7e407109532557718c6b8064792f2df7a073dMindy Pereira    public final void assignFolder(Collection<FolderOperation> folderOps,
30916a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy            Collection<Conversation> target, boolean batch, boolean showUndo,
30926a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy            final boolean isMoveTo) {
3093479753fdbb4d71558c4d2c8d9cc8c7eb803a7837Jin Cao        // Actions are destructive only when the current folder can be un-assigned from and
30948db7e407109532557718c6b8064792f2df7a073dMindy Pereira        // when the list of folders contains the current folder.
30958db7e407109532557718c6b8064792f2df7a073dMindy Pereira        final boolean isDestructive = mFolder
3096479753fdbb4d71558c4d2c8d9cc8c7eb803a7837Jin Cao                .supportsCapability(FolderCapabilities.ALLOWS_REMOVE_CONVERSATION)
30978db7e407109532557718c6b8064792f2df7a073dMindy Pereira                && FolderOperation.isDestructive(folderOps, mFolder);
3098d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal        LogUtils.d(LOG_TAG, "onFolderChangesCommit: isDestructive = %b", isDestructive);
3099d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal        if (isDestructive) {
3100d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal            for (final Conversation c : target) {
3101d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal                c.localDeleteOnUpdate = true;
31026778f4663a790d3e9d4c6a2f43292eef43c1e0bdMindy Pereira            }
3103e5f4dc0661dad02e2cb39ffa62ff2157147ac387Mindy Pereira        }
3104c84759cc974256b6af5545c4bd993b81a0a98f6fmindyp        final DestructiveAction folderChange;
310530c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        final UndoCallback undoCallback = isMoveTo ?
310630c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                getUndoCallbackForDestructiveActionsWithAutoAdvance(R.id.move_to,
310730c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                        mCurrentConversation)
310830c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                : null;
3109d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal        // Update the UI elements depending no their visibility and availability
31103d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal        // TODO(viki): Consolidate this into a single method requestDelete.
3111d503df4f0c31bbf842c6a1d3cba18df8c074bf67Vikram Aggarwal        if (isDestructive) {
31126a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy            /*
31136a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy             * If this is a MOVE operation, we want the action folder to be the destination folder.
31146a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy             * Otherwise, we want it to be the current folder.
31156a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy             *
31166a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy             * A set of folder operations is a move if there are exactly two operations: an add and
31176a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy             * a remove.
31186a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy             */
31196a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy            final Folder actionFolder;
31206a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy            if (folderOps.size() != 2) {
31216a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                actionFolder = mFolder;
31226a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy            } else {
31236a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                Folder addedFolder = null;
31246a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                boolean hasRemove = false;
31256a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                for (final FolderOperation folderOperation : folderOps) {
31266a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                    if (folderOperation.mAdd) {
31276a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                        addedFolder = folderOperation.mFolder;
31286a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                    } else {
31296a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                        hasRemove = true;
31306a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                    }
31316a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                }
31326a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy
31336a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                if (hasRemove && addedFolder != null) {
31346a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                    actionFolder = addedFolder;
31356a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                } else {
31366a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                    actionFolder = mFolder;
31376a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                }
31386a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy            }
31396a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy
3140c84759cc974256b6af5545c4bd993b81a0a98f6fmindyp            folderChange = getDeferredFolderChange(target, folderOps, isDestructive,
314130c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                    batch, showUndo, isMoveTo, actionFolder, undoCallback);
3142caaeed3837316175111bb16e6b60aeb0d523debfScott Kennedy            delete(0, target, folderChange, batch);
31436902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal        } else {
3144c84759cc974256b6af5545c4bd993b81a0a98f6fmindyp            folderChange = getFolderChange(target, folderOps, isDestructive,
314530c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                    batch, showUndo, false /* isMoveTo */, mFolder, undoCallback);
3146715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal            requestUpdate(folderChange);
3147e5f4dc0661dad02e2cb39ffa62ff2157147ac387Mindy Pereira        }
3148e5f4dc0661dad02e2cb39ffa62ff2157147ac387Mindy Pereira    }
3149e5f4dc0661dad02e2cb39ffa62ff2157147ac387Mindy Pereira
3150967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    @Override
31513d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal    public final void onRefreshRequired() {
315212a05d22f6ac528e96716de9a451c5b7bee7a26dAndy Huang        if (isAnimating()) {
3153f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang            final ConversationListFragment f = getConversationListFragment();
3154f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang            LogUtils.w(ConversationCursor.LOG_TAG,
3155f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang                    "onRefreshRequired: delay until animating done. cursor=%s adapter=%s",
3156f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang                    mConversationListCursor, (f != null) ? f.getAnimatedAdapter() : null);
315769e88dd6779a8f88212a049d49d69503cd746ef7Mindy Pereira            return;
315869e88dd6779a8f88212a049d49d69503cd746ef7Mindy Pereira        }
3159bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        // Refresh the query in the background
3160cff1aeaff92efb9f7a03203d9f757a2820b87686Paul Westbrook        if (mConversationListCursor.isRefreshRequired()) {
3161cff1aeaff92efb9f7a03203d9f757a2820b87686Paul Westbrook            mConversationListCursor.refresh();
3162cff1aeaff92efb9f7a03203d9f757a2820b87686Paul Westbrook        }
3163bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    }
3164bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank
31655390fcabb7baf683fc14bd68d8612913dac70a81mindyp    @Override
31666f54e1b0e8beae2e624852f9f2f2b1b38745e3bemindyp    public boolean isAnimating() {
316769e88dd6779a8f88212a049d49d69503cd746ef7Mindy Pereira        boolean isAnimating = false;
316869e88dd6779a8f88212a049d49d69503cd746ef7Mindy Pereira        ConversationListFragment convListFragment = getConversationListFragment();
316969e88dd6779a8f88212a049d49d69503cd746ef7Mindy Pereira        if (convListFragment != null) {
317048ccbc53ef90bf6420f831f63e6243008e02a346Andy Huang            isAnimating = convListFragment.isAnimating();
317169e88dd6779a8f88212a049d49d69503cd746ef7Mindy Pereira        }
317269e88dd6779a8f88212a049d49d69503cd746ef7Mindy Pereira        return isAnimating;
317369e88dd6779a8f88212a049d49d69503cd746ef7Mindy Pereira    }
317469e88dd6779a8f88212a049d49d69503cd746ef7Mindy Pereira
3175bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    /**
3176bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank     * Called when the {@link ConversationCursor} is changed or has new data in it.
3177bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank     * <p>
3178bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank     * {@inheritDoc}
3179bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank     */
3180bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    @Override
31813d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal    public final void onRefreshReady() {
31825c1d8354ce98be2fc77d458bcc5c3346765046a0mindyp        LogUtils.d(LOG_TAG, "Received refresh ready callback for folder %s",
31835c1d8354ce98be2fc77d458bcc5c3346765046a0mindyp                mFolder != null ? mFolder.id : "-1");
3184b2ef9c18d1f68543d9c782e1952b70d3bbcd94d6Andy Huang
3185b2ef9c18d1f68543d9c782e1952b70d3bbcd94d6Andy Huang        if (mDestroyed) {
3186b2ef9c18d1f68543d9c782e1952b70d3bbcd94d6Andy Huang            LogUtils.i(LOG_TAG, "ignoring onRefreshReady on destroyed AAC");
3187b2ef9c18d1f68543d9c782e1952b70d3bbcd94d6Andy Huang            return;
3188b2ef9c18d1f68543d9c782e1952b70d3bbcd94d6Andy Huang        }
3189b2ef9c18d1f68543d9c782e1952b70d3bbcd94d6Andy Huang
3190cff1aeaff92efb9f7a03203d9f757a2820b87686Paul Westbrook        if (!isAnimating()) {
3191bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank            // Swap cursors
3192bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank            mConversationListCursor.sync();
3193f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang        } else {
3194f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang            // (CLF guaranteed to be non-null due to check in isAnimating)
3195f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang            LogUtils.w(LOG_TAG,
3196f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang                    "AAC.onRefreshReady suppressing sync() due to animation. cursor=%s aa=%s",
3197f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang                    mConversationListCursor, getConversationListFragment().getAnimatedAdapter());
3198bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        }
3199937c94f025ed55c19bd46c7c077fa3575ab6be52Paul Westbrook        mTracker.onCursorUpdated();
320087a8982af83168453b6dc219fe984bff68047bebAndy Huang        perhapsShowFirstConversation();
3201bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    }
3202bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank
3203bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    @Override
32043d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal    public final void onDataSetChanged() {
32059f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook        updateConversationListFragment();
3206632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang        mConversationListObservable.notifyChanged();
3207ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao        mCheckedSet.validateAgainstCursor(mConversationListCursor);
3208bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    }
3209bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank
32103d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal    /**
32113d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal     * If the Conversation List Fragment is visible, updates the fragment.
32123d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal     */
3213715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal    private void updateConversationListFragment() {
3214bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        final ConversationListFragment convList = getConversationListFragment();
3215bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        if (convList != null) {
321675daee52f08c9bf66652b9bddf42cfbd1ca71987Vikram Aggarwal            refreshConversationList();
32176422b8d051c93b4f34580c551254fc50c9efa4dbVikram Aggarwal            if (isFragmentVisible(convList)) {
321869b5c304f9572f18c0fcb456d81cccf2e560c72fVikram Aggarwal                informCursorVisiblity(true);
32199f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook            }
3220bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        }
3221bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    }
3222bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank
3223bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    /**
3224bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank     * This class handles throttled refresh of the conversation list
3225bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank     */
3226bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    static class RefreshTimerTask extends TimerTask {
3227bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        final Handler mHandler;
3228bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        final AbstractActivityController mController;
3229bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank
3230bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        RefreshTimerTask(AbstractActivityController controller, Handler handler) {
3231bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank            mHandler = handler;
3232bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank            mController = controller;
3233bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        }
3234bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank
3235bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        @Override
3236bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        public void run() {
3237bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank            mHandler.post(new Runnable() {
3238bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank                @Override
3239bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank                public void run() {
3240bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank                    LogUtils.d(LOG_TAG, "Delay done... calling onRefreshRequired");
3241bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank                    mController.onRefreshRequired();
3242bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank                }});
3243bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        }
3244bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    }
3245bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank
3246bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    /**
3247bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank     * Cancel the refresh task, if it's running
3248bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank     */
3249bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    private void cancelRefreshTask () {
3250bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        if (mConversationListRefreshTask != null) {
3251bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank            mConversationListRefreshTask.cancel();
3252bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank            mConversationListRefreshTask = null;
3253bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        }
3254bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    }
3255bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank
3256bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    @Override
3257cff1aeaff92efb9f7a03203d9f757a2820b87686Paul Westbrook    public void onAnimationEnd(AnimatedAdapter animatedAdapter) {
3258f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang        if (animatedAdapter != null) {
3259f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang            LogUtils.i(LOG_TAG, "AAC.onAnimationEnd. cursor=%s adapter=%s", mConversationListCursor,
3260f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang                    animatedAdapter);
3261f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang        }
3262026139c958c563266294b7788ed54f57345626b8Paul Westbrook        if (mConversationListCursor == null) {
3263026139c958c563266294b7788ed54f57345626b8Paul Westbrook            LogUtils.e(LOG_TAG, "null ConversationCursor in onAnimationEnd");
3264026139c958c563266294b7788ed54f57345626b8Paul Westbrook            return;
3265026139c958c563266294b7788ed54f57345626b8Paul Westbrook        }
3266cff1aeaff92efb9f7a03203d9f757a2820b87686Paul Westbrook        if (mConversationListCursor.isRefreshReady()) {
3267c1922a93fb9540d4ff8c28a30d1ae58d3a3d73f9Andy Huang            LogUtils.i(ConversationCursor.LOG_TAG, "Stopped animating: try sync");
3268cff1aeaff92efb9f7a03203d9f757a2820b87686Paul Westbrook            onRefreshReady();
3269bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank        }
3270bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank
3271cff1aeaff92efb9f7a03203d9f757a2820b87686Paul Westbrook        if (mConversationListCursor.isRefreshRequired()) {
3272c1922a93fb9540d4ff8c28a30d1ae58d3a3d73f9Andy Huang            LogUtils.i(ConversationCursor.LOG_TAG, "Stopped animating: refresh");
3273cff1aeaff92efb9f7a03203d9f757a2820b87686Paul Westbrook            mConversationListCursor.refresh();
3274cff1aeaff92efb9f7a03203d9f757a2820b87686Paul Westbrook        }
32756f54e1b0e8beae2e624852f9f2f2b1b38745e3bemindyp        if (mRecentsDataUpdated) {
32766f54e1b0e8beae2e624852f9f2f2b1b38745e3bemindyp            mRecentsDataUpdated = false;
32776f54e1b0e8beae2e624852f9f2f2b1b38745e3bemindyp            mRecentFolderObservers.notifyChanged();
32786f54e1b0e8beae2e624852f9f2f2b1b38745e3bemindyp        }
3279bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    }
3280bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank
3281bf128ebfb9aa53d28118e7143d5674e506fedfefMarc Blank    @Override
3282967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    public void onSetEmpty() {
32836cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal        // There are no selected conversations. Ensure that the listener and its associated actions
32846cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal        // are blanked out.
32856cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal        setListener(null, -1);
3286967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    }
3287967ede63e059810d9d4d425c50111d606014c893Mindy Pereira
3288967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    @Override
3289ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao    public void onSetPopulated(ConversationCheckedSet set) {
32907704d79def5e5f36a1cbe22fbbe6e9520503899cVikram Aggarwal        mCabActionMenu = new SelectedConversationsActionMenu(mActivity, set, mFolder);
32915b9ed2b056e0757ccc26e51aba7d5ff12378baaaVikram Aggarwal        if (mViewMode.isListMode() || (mIsTablet && mViewMode.isConversationMode())) {
32927704d79def5e5f36a1cbe22fbbe6e9520503899cVikram Aggarwal            enableCabMode();
32936902dcf8688d7d0691639f49365a78a3a78fe9e3Vikram Aggarwal        }
3294967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    }
3295967ede63e059810d9d4d425c50111d606014c893Mindy Pereira
3296967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    @Override
3297ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao    public void onSetChanged(ConversationCheckedSet set) {
3298967ede63e059810d9d4d425c50111d606014c893Mindy Pereira        // Do nothing. We don't care about changes to the set.
3299967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    }
3300967ede63e059810d9d4d425c50111d606014c893Mindy Pereira
3301967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    @Override
3302ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao    public ConversationCheckedSet getCheckedSet() {
3303ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao        return mCheckedSet;
3304967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    }
3305967ede63e059810d9d4d425c50111d606014c893Mindy Pereira
3306e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal    /**
3307e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal     * Disable the Contextual Action Bar (CAB). The selected set is not changed.
3308e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal     */
3309e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal    protected void disableCabMode() {
33108937bf1552a86853efc798a4d8df34c01115cdfdMindy Pereira        // Commit any previous destructive actions when entering/ exiting CAB mode.
3311c6adce3cf6887c4c6dd5005724565702751843d0mindyp        commitDestructiveActions(true);
3312e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal        if (mCabActionMenu != null) {
3313e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal            mCabActionMenu.deactivate();
3314e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal        }
3315e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal    }
3316e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal
3317e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal    /**
3318e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal     * Re-enable the CAB menu if required. The selection set is not changed.
3319e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal     */
3320e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal    protected void enableCabMode() {
33213d6751ad5328d5a17fa637e3157974d776591b26Tony Mantler        if (mCabActionMenu != null &&
33223d6751ad5328d5a17fa637e3157974d776591b26Tony Mantler                !(isDrawerEnabled() && mDrawerContainer.isDrawerOpen(mDrawerPullout))) {
3323e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal            mCabActionMenu.activate();
3324e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal        }
3325e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal    }
3326e128fc21fab03f6d66c656efeee2155fab449a7fVikram Aggarwal
33274eb5271e0f0a48ea624e048eb69fbe86d6c62dc3Vikram Aggarwal    /**
332843fab3224230c235db5be395f96096e1cac6b376Tony Mantler     * Re-enable CAB mode only if we have an active selection
332943fab3224230c235db5be395f96096e1cac6b376Tony Mantler     */
333043fab3224230c235db5be395f96096e1cac6b376Tony Mantler    protected void maybeEnableCabMode() {
3331ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao        if (!mCheckedSet.isEmpty()) {
3332a703d8a647a7272347c47bbb394b1a4c403106a5Tony Mantler            if (mCabActionMenu != null) {
3333a703d8a647a7272347c47bbb394b1a4c403106a5Tony Mantler                mCabActionMenu.activate();
3334a703d8a647a7272347c47bbb394b1a4c403106a5Tony Mantler            }
333543fab3224230c235db5be395f96096e1cac6b376Tony Mantler        }
333643fab3224230c235db5be395f96096e1cac6b376Tony Mantler    }
333743fab3224230c235db5be395f96096e1cac6b376Tony Mantler
333843fab3224230c235db5be395f96096e1cac6b376Tony Mantler    /**
33394eb5271e0f0a48ea624e048eb69fbe86d6c62dc3Vikram Aggarwal     * Unselect conversations and exit CAB mode.
33404eb5271e0f0a48ea624e048eb69fbe86d6c62dc3Vikram Aggarwal     */
33414eb5271e0f0a48ea624e048eb69fbe86d6c62dc3Vikram Aggarwal    protected final void exitCabMode() {
3342ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao        mCheckedSet.clear();
33434eb5271e0f0a48ea624e048eb69fbe86d6c62dc3Vikram Aggarwal    }
33444eb5271e0f0a48ea624e048eb69fbe86d6c62dc3Vikram Aggarwal
3345967ede63e059810d9d4d425c50111d606014c893Mindy Pereira    @Override
3346fd0c2974af56ad244006faab2651e054974a4b7bMindy Pereira    public void startSearch() {
334735f19d7c052ca8074b48b2ab9d8689a72dc369c5Vikram Aggarwal        if (mAccount == null) {
334835f19d7c052ca8074b48b2ab9d8689a72dc369c5Vikram Aggarwal            // We cannot search if there is no account. Drop the request to the floor.
334935f19d7c052ca8074b48b2ab9d8689a72dc369c5Vikram Aggarwal            LogUtils.d(LOG_TAG, "AbstractActivityController.startSearch(): null account");
335035f19d7c052ca8074b48b2ab9d8689a72dc369c5Vikram Aggarwal            return;
335135f19d7c052ca8074b48b2ab9d8689a72dc369c5Vikram Aggarwal        }
33523531d7ee9b36c9f9dd909be79ab899dd60cd71aeJames Lemieux        if (mAccount.supportsSearch()) {
3353c6801eb828627c37b8992584767c095dfe11df62Jin Cao            mSearchViewController.showSearchActionBar(
3354c6801eb828627c37b8992584767c095dfe11df62Jin Cao                    MaterialSearchViewController.SEARCH_VIEW_STATE_VISIBLE);
3355fd0c2974af56ad244006faab2651e054974a4b7bMindy Pereira        } else {
3356fd0c2974af56ad244006faab2651e054974a4b7bMindy Pereira            Toast.makeText(mActivity.getActivityContext(), mActivity.getActivityContext()
3357a46c29980f2d81da3529b2f1dfddf7291c94a102Mindy Pereira                    .getString(R.string.search_unsupported), Toast.LENGTH_SHORT).show();
3358fd0c2974af56ad244006faab2651e054974a4b7bMindy Pereira        }
3359fd0c2974af56ad244006faab2651e054974a4b7bMindy Pereira    }
3360acf6039a23382f18c35f6b487d90d53cb67b5858Mindy Pereira
33610963ef8394e1f0bca3f931f930b127b00790361aMindy Pereira    @Override
33620963ef8394e1f0bca3f931f930b127b00790361aMindy Pereira    public void onTouchEvent(MotionEvent event) {
33630963ef8394e1f0bca3f931f930b127b00790361aMindy Pereira        if (event.getAction() == MotionEvent.ACTION_DOWN) {
3364c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein            if (mToastBar != null && !mToastBar.isEventInToastBar(event)) {
33659a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux                // if the toast bar is still animating, ignore this attempt to hide it
33669a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux                if (mToastBar.isAnimating()) {
33679a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux                    return;
33689a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux                }
33699a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux
33709a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux                // if the toast bar has not been seen long enough, ignore this attempt to hide it
33719a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux                if (mToastBar.cannotBeHidden()) {
33729a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux                    return;
33739a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux                }
33749a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux
33759a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux                // hide the toast bar
33769a11011dc42ea3c8934c90455b17bd77a0a5ee17James Lemieux                mToastBar.hide(true /* animated */, false /* actionClicked */);
33770963ef8394e1f0bca3f931f930b127b00790361aMindy Pereira            }
33780963ef8394e1f0bca3f931f930b127b00790361aMindy Pereira        }
33790963ef8394e1f0bca3f931f930b127b00790361aMindy Pereira    }
3380b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang
3381632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    @Override
33823b965d78774a42358ce6bbdcc43b4c8df130a60eScott Kennedy    public void onConversationSeen() {
33833b965d78774a42358ce6bbdcc43b4c8df130a60eScott Kennedy        mPagerController.onConversationSeen();
3384632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang    }
3385632721e6b3a9ba8c476456f2e0fb1b564561e0b5Andy Huang
33869d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang    @Override
33879d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang    public boolean isInitialConversationLoading() {
33889d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang        return mPagerController.isInitialConversationLoading();
33899d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang    }
33909d3fd92ed6091dbd0d38799222a1cf841f1c3f29Andy Huang
33916422b8d051c93b4f34580c551254fc50c9efa4dbVikram Aggarwal    /**
33926422b8d051c93b4f34580c551254fc50c9efa4dbVikram Aggarwal     * Check if the fragment given here is visible. Checking {@link Fragment#isVisible()} is
33936422b8d051c93b4f34580c551254fc50c9efa4dbVikram Aggarwal     * insufficient because that doesn't check if the window is currently in focus or not.
33946422b8d051c93b4f34580c551254fc50c9efa4dbVikram Aggarwal     */
3395715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal    private boolean isFragmentVisible(Fragment in) {
33966422b8d051c93b4f34580c551254fc50c9efa4dbVikram Aggarwal        return in != null && in.isVisible() && mActivity.hasWindowFocus();
33976422b8d051c93b4f34580c551254fc50c9efa4dbVikram Aggarwal    }
33986422b8d051c93b4f34580c551254fc50c9efa4dbVikram Aggarwal
3399d70fe49cabf896e87574bcb2e57e8a32608537beVikram Aggarwal    /**
3400d70fe49cabf896e87574bcb2e57e8a32608537beVikram Aggarwal     * This class handles callbacks that create a {@link ConversationCursor}.
3401d70fe49cabf896e87574bcb2e57e8a32608537beVikram Aggarwal     */
3402b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang    private class ConversationListLoaderCallbacks implements
3403b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang        LoaderManager.LoaderCallbacks<ConversationCursor> {
3404b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang
3405b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang        @Override
3406b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang        public Loader<ConversationCursor> onCreateLoader(int id, Bundle args) {
3407a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal            final Account account = args.getParcelable(BUNDLE_ACCOUNT_KEY);
3408a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal            final Folder folder = args.getParcelable(BUNDLE_FOLDER_KEY);
34095bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein            final boolean ignoreInitialConversationLimit =
34105bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein                    args.getBoolean(BUNDLE_IGNORE_INITIAL_CONVERSATION_LIMIT_KEY, false);
3411a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal            if (account == null || folder == null) {
3412a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal                return null;
3413a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal            }
34145bb4d053519be10324752f323bcf73f13b9a2604Andrew Sapperstein            return new ConversationCursorLoader(mActivity, account,
3415f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang                    folder.conversationListUri, folder.getTypeDescription(),
3416f21787ad2ac7d68e3620ffa3ae6e8e7fadf0bd54Andy Huang                    ignoreInitialConversationLimit);
3417b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang        }
3418b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang
3419b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang        @Override
3420b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang        public void onLoadFinished(Loader<ConversationCursor> loader, ConversationCursor data) {
3421144bfe739b93afdee0a1700a34806b0b287e5887Andy Huang            LogUtils.d(LOG_TAG,
3422144bfe739b93afdee0a1700a34806b0b287e5887Andy Huang                    "IN AAC.ConversationCursor.onLoadFinished, data=%s loader=%s this=%s",
3423144bfe739b93afdee0a1700a34806b0b287e5887Andy Huang                    data, loader, this);
34240c532efa4ec664b50a0ccff989667599e72cc4fcTony Mantler            if (isDestroyed()) {
34250c532efa4ec664b50a0ccff989667599e72cc4fcTony Mantler                return;
34260c532efa4ec664b50a0ccff989667599e72cc4fcTony Mantler            }
3427a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein            if (isDrawerEnabled() && mDrawerListener.getDrawerState() != DrawerLayout.STATE_IDLE) {
3428a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal                LogUtils.d(LOG_TAG, "ConversationListLoaderCallbacks.onLoadFinished: ignoring.");
3429a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein                mConversationListLoadFinishedIgnored = true;
3430a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal                return;
3431a1b59dcb4a8aaef821aa2f5c996f715ef130c985Vikram Aggarwal            }
3432e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal            // Clear our all pending destructive actions before swapping the conversation cursor
3433e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal            destroyPending(null);
3434b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang            mConversationListCursor = data;
3435bf232c3735f65b1a4746943e4a134e59e36f0bdePaul Westbrook            mConversationListCursor.addListener(AbstractActivityController.this);
3436144bfe739b93afdee0a1700a34806b0b287e5887Andy Huang            mDrawIdler.setListener(mConversationListCursor);
3437937c94f025ed55c19bd46c7c077fa3575ab6be52Paul Westbrook            mTracker.onCursorUpdated();
3438e3df1ad8e630d7d61b4d68f8328f97dd11448b5dAndy Huang            mConversationListObservable.notifyChanged();
34397c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu            // Handle actions that were deferred until after the conversation list was loaded.
34407c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu            for (LoadFinishedCallback callback : mConversationListLoadFinishedCallbacks) {
34417c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu                callback.onLoadFinished();
34427c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu            }
34437c909c76deb748e6aca82e885a9fbaee52adeb9cYu Ping Hu            mConversationListLoadFinishedCallbacks.clear();
34449f119c72042e4d0382017d5ddcee9aa2113b6425Paul Westbrook
344517f373ecf6961112f77a7516ab1bbcb4fcdf2884Vikram Aggarwal            final ConversationListFragment convList = getConversationListFragment();
34466422b8d051c93b4f34580c551254fc50c9efa4dbVikram Aggarwal            if (isFragmentVisible(convList)) {
344717f373ecf6961112f77a7516ab1bbcb4fcdf2884Vikram Aggarwal                // The conversation list is already listening to list changes and gets notified
344817f373ecf6961112f77a7516ab1bbcb4fcdf2884Vikram Aggarwal                // in the mConversationListObservable.notifyChanged() line above. We only need to
344917f373ecf6961112f77a7516ab1bbcb4fcdf2884Vikram Aggarwal                // check and inform the cursor of the change in visibility here.
345017f373ecf6961112f77a7516ab1bbcb4fcdf2884Vikram Aggarwal                informCursorVisiblity(true);
3451b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang            }
345287a8982af83168453b6dc219fe984bff68047bebAndy Huang            perhapsShowFirstConversation();
3453b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang        }
3454b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang
3455b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang        @Override
3456b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang        public void onLoaderReset(Loader<ConversationCursor> loader) {
3457144bfe739b93afdee0a1700a34806b0b287e5887Andy Huang            LogUtils.d(LOG_TAG,
3458144bfe739b93afdee0a1700a34806b0b287e5887Andy Huang                    "IN AAC.ConversationCursor.onLoaderReset, data=%s loader=%s this=%s",
3459144bfe739b93afdee0a1700a34806b0b287e5887Andy Huang                    mConversationListCursor, loader, this);
34609a70e919125d63971604e6dd3de8bc998e9c86daPaul Westbrook
34619a70e919125d63971604e6dd3de8bc998e9c86daPaul Westbrook            if (mConversationListCursor != null) {
34629a70e919125d63971604e6dd3de8bc998e9c86daPaul Westbrook                // Unregister the listener
34639a70e919125d63971604e6dd3de8bc998e9c86daPaul Westbrook                mConversationListCursor.removeListener(AbstractActivityController.this);
3464144bfe739b93afdee0a1700a34806b0b287e5887Andy Huang                mDrawIdler.setListener(null);
34659a70e919125d63971604e6dd3de8bc998e9c86daPaul Westbrook                mConversationListCursor = null;
34669a70e919125d63971604e6dd3de8bc998e9c86daPaul Westbrook
34679a70e919125d63971604e6dd3de8bc998e9c86daPaul Westbrook                // Inform anyone who is interested about the change
34689a70e919125d63971604e6dd3de8bc998e9c86daPaul Westbrook                mTracker.onCursorUpdated();
34699a70e919125d63971604e6dd3de8bc998e9c86daPaul Westbrook                mConversationListObservable.notifyChanged();
3470b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang            }
3471b1c34dc418e31ae0bfe1b73b6642ed42f5dad67bAndy Huang        }
3472bf232c3735f65b1a4746943e4a134e59e36f0bdePaul Westbrook    }
3473bf232c3735f65b1a4746943e4a134e59e36f0bdePaul Westbrook
3474e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal    /**
3475177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal     * Class to perform {@link LoaderManager.LoaderCallbacks} for creating {@link Folder} objects.
3476177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal     */
3477177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    private class FolderLoads implements LoaderManager.LoaderCallbacks<ObjectCursor<Folder>> {
3478177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        @Override
3479177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        public Loader<ObjectCursor<Folder>> onCreateLoader(int id, Bundle args) {
3480177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            final String[] everything = UIProvider.FOLDERS_PROJECTION;
3481177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            switch (id) {
3482177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                case LOADER_FOLDER_CURSOR:
3483177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    LogUtils.d(LOG_TAG, "LOADER_FOLDER_CURSOR created");
3484177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    final ObjectCursorLoader<Folder> loader = new
3485177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            ObjectCursorLoader<Folder>(
3486259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy                            mContext, mFolder.folderUri.fullUri, everything, Folder.FACTORY);
3487177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    loader.setUpdateThrottle(mFolderItemUpdateDelayMs);
3488177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    return loader;
3489177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                case LOADER_RECENT_FOLDERS:
3490177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    LogUtils.d(LOG_TAG, "LOADER_RECENT_FOLDERS created");
3491f2632b979d7f17a548c34cccae94b2a0046cee32Yu Ping Hu                    if (mAccount != null && mAccount.recentFolderListUri != null
3492f2632b979d7f17a548c34cccae94b2a0046cee32Yu Ping Hu                            && !mAccount.recentFolderListUri.equals(Uri.EMPTY)) {
3493177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        return new ObjectCursorLoader<Folder>(mContext,
3494177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                mAccount.recentFolderListUri, everything, Folder.FACTORY);
3495177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3496177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    break;
3497177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                case LOADER_ACCOUNT_INBOX:
3498177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    LogUtils.d(LOG_TAG, "LOADER_ACCOUNT_INBOX created");
3499177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    final Uri defaultInbox = Settings.getDefaultInboxUri(mAccount.settings);
3500177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    final Uri inboxUri = defaultInbox.equals(Uri.EMPTY) ?
3501177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            mAccount.folderListUri : defaultInbox;
3502177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    LogUtils.d(LOG_TAG, "Loading the default inbox: %s", inboxUri);
3503177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    if (inboxUri != null) {
3504177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        return new ObjectCursorLoader<Folder>(mContext, inboxUri,
3505177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                everything, Folder.FACTORY);
3506177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3507177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    break;
3508177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                case LOADER_SEARCH:
3509177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    LogUtils.d(LOG_TAG, "LOADER_SEARCH created");
3510177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    return Folder.forSearchResults(mAccount,
3511177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            args.getString(ConversationListContext.EXTRA_SEARCH_QUERY),
35121a864cce6321f46031d6cd43e205278d0f219f92Jin Cao                            // We can just use current time as a unique identifier for this search
35131a864cce6321f46031d6cd43e205278d0f219f92Jin Cao                            Long.toString(SystemClock.uptimeMillis()),
3514177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            mActivity.getActivityContext());
3515177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                case LOADER_FIRST_FOLDER:
3516177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    LogUtils.d(LOG_TAG, "LOADER_FIRST_FOLDER created");
3517177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    final Uri folderUri = args.getParcelable(Utils.EXTRA_FOLDER_URI);
3518177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    mConversationToShow = args.getParcelable(Utils.EXTRA_CONVERSATION);
3519177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    if (mConversationToShow != null && mConversationToShow.position < 0){
3520177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        mConversationToShow.position = 0;
3521177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3522177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    return new ObjectCursorLoader<Folder>(mContext, folderUri,
3523177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            everything, Folder.FACTORY);
3524177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                default:
3525177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    LogUtils.wtf(LOG_TAG, "FolderLoads.onCreateLoader(%d) for invalid id", id);
3526177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    return null;
3527177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            }
3528177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            return null;
3529177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        }
3530177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal
3531177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        @Override
3532177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        public void onLoadFinished(Loader<ObjectCursor<Folder>> loader, ObjectCursor<Folder> data) {
3533177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            if (data == null) {
3534177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                LogUtils.e(LOG_TAG, "Received null cursor from loader id: %d", loader.getId());
3535177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            }
35360c532efa4ec664b50a0ccff989667599e72cc4fcTony Mantler            if (isDestroyed()) {
35370c532efa4ec664b50a0ccff989667599e72cc4fcTony Mantler                return;
35380c532efa4ec664b50a0ccff989667599e72cc4fcTony Mantler            }
3539177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            switch (loader.getId()) {
3540177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                case LOADER_FOLDER_CURSOR:
3541177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    if (data != null && data.moveToFirst()) {
3542177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        final Folder folder = data.getModel();
3543177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        setHasFolderChanged(folder);
3544177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        mFolder = folder;
3545177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        mFolderObservable.notifyChanged();
3546177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    } else {
3547177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        LogUtils.d(LOG_TAG, "Unable to get the folder %s",
354826a20758413f0f8629ea71a0943304e3ae740165Tony Mantler                                mFolder != null ? mFolder.name : "");
3549177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3550177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    break;
3551177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                case LOADER_RECENT_FOLDERS:
3552177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    // Few recent folders and we are running on a phone? Populate the default
3553177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    // recents. The number of default recent folders is at least 2: every provider
3554177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    // has at least two folders, and the recent folder count never decreases.
3555177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    // Having a single recent folder is an erroneous case, and we can gracefully
3556177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    // recover by populating default recents. The default recents will not stomp on
3557177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    // the existing value: it will be shown in addition to the default folders:
3558177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    // the max number of recent folders is more than 1+num(defaultRecents).
3559177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    if (data != null && data.getCount() <= 1 && !mIsTablet) {
3560177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        final class PopulateDefault extends AsyncTask<Uri, Void, Void> {
3561177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            @Override
3562177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            protected Void doInBackground(Uri... uri) {
3563177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                // Asking for an update on the URI and ignore the result.
3564177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                final ContentResolver resolver = mContext.getContentResolver();
3565177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                resolver.update(uri[0], null, null, null);
3566177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                return null;
3567177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            }
3568177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        }
3569177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        final Uri uri = mAccount.defaultRecentFolderListUri;
3570177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        LogUtils.v(LOG_TAG, "Default recents at %s", uri);
3571177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        new PopulateDefault().execute(uri);
3572177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        break;
3573177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3574177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    LogUtils.v(LOG_TAG, "Reading recent folders from the cursor.");
3575177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    mRecentFolderList.loadFromUiProvider(data);
3576177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    if (isAnimating()) {
3577177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        mRecentsDataUpdated = true;
3578177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    } else {
3579177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        mRecentFolderObservers.notifyChanged();
3580177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3581177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    break;
3582177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                case LOADER_ACCOUNT_INBOX:
3583177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    if (data != null && !data.isClosed() && data.moveToFirst()) {
3584177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        final Folder inbox = data.getModel();
3585ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                        onFolderChanged(inbox, false /* force */);
3586177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        // Just want to get the inbox, don't care about updates to it
3587177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        // as this will be tracked by the folder change listener.
3588177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        mActivity.getLoaderManager().destroyLoader(LOADER_ACCOUNT_INBOX);
3589177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    } else {
3590177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        LogUtils.d(LOG_TAG, "Unable to get the account inbox for account %s",
359126a20758413f0f8629ea71a0943304e3ae740165Tony Mantler                                mAccount != null ? mAccount.getEmailAddress() : "");
3592177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3593177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    break;
3594177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                case LOADER_SEARCH:
3595177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    if (data != null && data.getCount() > 0) {
3596177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        data.moveToFirst();
3597177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        final Folder search = data.getModel();
3598177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        updateFolder(search);
3599177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        mConvListContext = ConversationListContext.forSearchQuery(mAccount, mFolder,
3600177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                mActivity.getIntent()
3601177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                        .getStringExtra(UIProvider.SearchQueryParameters.QUERY));
3602177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        showConversationList(mConvListContext);
3603177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        mActivity.invalidateOptionsMenu();
3604177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        mHaveSearchResults = search.totalCount > 0;
3605177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        mActivity.getLoaderManager().destroyLoader(LOADER_SEARCH);
3606177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    } else {
3607177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        LogUtils.e(LOG_TAG, "Null/empty cursor returned by LOADER_SEARCH loader");
3608177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3609177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    break;
3610177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                case LOADER_FIRST_FOLDER:
3611177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    if (data == null || data.getCount() <=0 || !data.moveToFirst()) {
3612177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        return;
3613177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3614177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    final Folder folder = data.getModel();
3615177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    boolean handled = false;
3616177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    if (folder != null) {
3617ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                        onFolderChanged(folder, false /* force */);
3618177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        handled = true;
3619177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3620177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    if (mConversationToShow != null) {
3621177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        // Open the conversation.
3622177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        showConversation(mConversationToShow);
3623177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        handled = true;
3624177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3625177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    if (!handled) {
3626177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        // We have an account, but nothing else: load the default inbox.
3627177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        loadAccountInbox();
3628177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3629177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    mConversationToShow = null;
3630177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    // And don't run this anymore.
3631177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    mActivity.getLoaderManager().destroyLoader(LOADER_FIRST_FOLDER);
3632177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    break;
3633177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            }
3634177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        }
3635177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal
3636177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        @Override
3637177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        public void onLoaderReset(Loader<ObjectCursor<Folder>> loader) {
3638177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        }
3639177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    }
3640177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal
3641177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    /**
3642177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal     * Class to perform {@link LoaderManager.LoaderCallbacks} for creating {@link Account} objects.
3643177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal     */
3644177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    private class AccountLoads implements LoaderManager.LoaderCallbacks<ObjectCursor<Account>> {
3645177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        final String[] mProjection = UIProvider.ACCOUNTS_PROJECTION;
3646177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        final CursorCreator<Account> mFactory = Account.FACTORY;
3647177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal
3648177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        @Override
3649177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        public Loader<ObjectCursor<Account>> onCreateLoader(int id, Bundle args) {
3650177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            switch (id) {
3651177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                case LOADER_ACCOUNT_CURSOR:
3652177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    LogUtils.d(LOG_TAG,  "LOADER_ACCOUNT_CURSOR created");
3653177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    return new ObjectCursorLoader<Account>(mContext,
3654177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            MailAppProvider.getAccountsUri(), mProjection, mFactory);
3655177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                case LOADER_ACCOUNT_UPDATE_CURSOR:
3656177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    LogUtils.d(LOG_TAG,  "LOADER_ACCOUNT_UPDATE_CURSOR created");
3657177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    return new ObjectCursorLoader<Account>(mContext, mAccount.uri, mProjection,
3658177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            mFactory);
3659177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                default:
3660177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    LogUtils.wtf(LOG_TAG, "Got an id  (%d) that I cannot create!", id);
3661177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    break;
3662177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            }
3663177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            return null;
3664177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        }
3665177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal
3666177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        @Override
3667177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        public void onLoadFinished(Loader<ObjectCursor<Account>> loader,
3668177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                ObjectCursor<Account> data) {
3669177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            if (data == null) {
3670177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                LogUtils.e(LOG_TAG, "Received null cursor from loader id: %d", loader.getId());
3671177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            }
36720c532efa4ec664b50a0ccff989667599e72cc4fcTony Mantler            if (isDestroyed()) {
36730c532efa4ec664b50a0ccff989667599e72cc4fcTony Mantler                return;
36740c532efa4ec664b50a0ccff989667599e72cc4fcTony Mantler            }
3675177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            switch (loader.getId()) {
3676177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                case LOADER_ACCOUNT_CURSOR:
3677d70fe49cabf896e87574bcb2e57e8a32608537beVikram Aggarwal                    // We have received an update on the list of accounts.
3678177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    if (data == null) {
3679177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        // Nothing useful to do if we have no valid data.
3680177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        break;
3681177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3682761522c1ac7a8fa05acde714f0e26fde47f3415fAndy Huang                    final long count = data.getCount();
3683761522c1ac7a8fa05acde714f0e26fde47f3415fAndy Huang                    if (count == 0) {
3684177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        // If an empty cursor is returned, the MailAppProvider is indicating that
3685177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        // no accounts have been specified.  We want to navigate to the
3686177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        // "add account" activity that will handle the intent returned by the
3687177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        // MailAppProvider
3688177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal
3689177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        // If the MailAppProvider believes that all accounts have been loaded,
3690177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        // and the account list is still empty, we want to prompt the user to add
3691177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        // an account.
3692177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        final Bundle extras = data.getExtras();
3693177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        final boolean accountsLoaded =
3694177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                extras.getInt(AccountCursorExtraKeys.ACCOUNTS_LOADED) != 0;
3695177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal
3696177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        if (accountsLoaded) {
3697177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            final Intent noAccountIntent = MailAppProvider.getNoAccountIntent
3698177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                    (mContext);
3699177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            if (noAccountIntent != null) {
3700177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                mActivity.startActivityForResult(noAccountIntent,
3701177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                        ADD_ACCOUNT_REQUEST_CODE);
3702177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            }
3703177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        }
3704177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    } else {
3705177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        final boolean accountListUpdated = accountsUpdated(data);
3706c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal                        if (!mHaveAccountList || accountListUpdated) {
3707c04cf7e8a7be800902c59ac6d771449d04f1bca9Vikram Aggarwal                            mHaveAccountList = updateAccounts(data);
3708177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        }
3709761522c1ac7a8fa05acde714f0e26fde47f3415fAndy Huang                        Analytics.getInstance().setCustomDimension(Analytics.CD_INDEX_ACCOUNT_COUNT,
3710761522c1ac7a8fa05acde714f0e26fde47f3415fAndy Huang                                Long.toString(count));
3711177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3712177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    break;
3713177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                case LOADER_ACCOUNT_UPDATE_CURSOR:
3714177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    // We have received an update for current account.
3715177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    if (data != null && data.moveToFirst()) {
3716177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        final Account updatedAccount = data.getModel();
3717d70fe49cabf896e87574bcb2e57e8a32608537beVikram Aggarwal                        // Make sure that this is an update for the current account
3718177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        if (updatedAccount.uri.equals(mAccount.uri)) {
3719177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            final Settings previousSettings = mAccount.settings;
3720177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal
3721177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            // Update the controller's reference to the current account
3722177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            mAccount = updatedAccount;
3723177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            LogUtils.d(LOG_TAG, "AbstractActivityController.onLoadFinished(): "
3724177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                    + "mAccount = %s", mAccount.uri);
3725177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal
3726177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            // Only notify about a settings change if something differs
3727177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            if (!Objects.equal(mAccount.settings, previousSettings)) {
3728177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                mAccountObservers.notifyChanged();
3729177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            }
3730177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            perhapsEnterWaitMode();
3731177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        } else {
3732177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            LogUtils.e(LOG_TAG, "Got update for account: %s with current account:"
3733177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                                    + " %s", updatedAccount.uri, mAccount.uri);
3734177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            // We need to restart the loader, so the correct account information
3735177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            // will be returned.
3736177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                            restartOptionalLoader(LOADER_ACCOUNT_UPDATE_CURSOR, this, Bundle.EMPTY);
3737177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                        }
3738177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    }
3739177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal                    break;
3740177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal            }
3741177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        }
3742177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal
3743177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        @Override
3744177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        public void onLoaderReset(Loader<ObjectCursor<Account>> loader) {
3745d70fe49cabf896e87574bcb2e57e8a32608537beVikram Aggarwal            // Do nothing. In onLoadFinished() we copy the relevant data from the cursor.
3746177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal        }
3747177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    }
3748177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal
3749177097fad8fc26b8a215f9f1af6dd5fd2c8eb06cVikram Aggarwal    /**
3750f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal     * Updates controller state based on search results and shows first conversation if required.
375187a8982af83168453b6dc219fe984bff68047bebAndy Huang     * Be sure to call the super-implementation if overriding.
3752f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal     */
375387a8982af83168453b6dc219fe984bff68047bebAndy Huang    protected void perhapsShowFirstConversation() {
375487a8982af83168453b6dc219fe984bff68047bebAndy Huang        mHaveSearchResults = Intent.ACTION_SEARCH.equals(mActivity.getIntent().getAction())
375587a8982af83168453b6dc219fe984bff68047bebAndy Huang                && mConversationListCursor.getCount() > 0;
3756f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal    }
3757f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal
3758f0ef23054a2311f898955178ff53a2f29ca425c9Vikram Aggarwal    /**
3759e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal     * Destroy the pending {@link DestructiveAction} till now and assign the given action as the
3760e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal     * next destructive action..
3761e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal     * @param nextAction the next destructive action to be performed. This can be null.
3762e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal     */
3763715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal    private void destroyPending(DestructiveAction nextAction) {
3764e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal        // If there is a pending action, perform that first.
3765e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal        if (mPendingDestruction != null) {
3766e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal            mPendingDestruction.performAction();
3767e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal        }
3768e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal        mPendingDestruction = nextAction;
3769e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal    }
3770e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal
3771e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal    /**
3772e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal     * Register a destructive action with the controller. This performs the previous destructive
3773acaa3c0aa2335e0a635601e09c955388d698dfdaVikram Aggarwal     * action as a side effect. This method is final because we don't want the child classes to
37743d7ca9d2aefd20dc115f20532c1e19fdb60f7d68Vikram Aggarwal     * embellish this method any more.
3775715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @param action the action to register.
3776e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal     */
3777715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal    private void registerDestructiveAction(DestructiveAction action) {
3778e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal        // TODO(viki): This is not a good idea. The best solution is for clients to request a
3779e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal        // destructive action from the controller and for the controller to own the action. This is
3780e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal        // a half-way solution while refactoring DestructiveAction.
3781e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal        destroyPending(action);
3782e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal    }
3783e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal
3784531488ed848766708252a5fdc01c4ecf0fd167d1Vikram Aggarwal    @Override
378530c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao    public final DestructiveAction getBatchAction(int action, UndoCallback undoCallback) {
3786ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao        final DestructiveAction da = new ConversationAction(action, mCheckedSet.values(), true);
378730c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        da.setUndoCallback(undoCallback);
3788e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal        registerDestructiveAction(da);
3789e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal        return da;
3790e8a85328eadff76fcd78141f1343bde0a1cd27e7Vikram Aggarwal    }
379141e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal
3792de3e74a82043733243c7391d7f983a5af8842891Mindy Pereira    @Override
379330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao    public final DestructiveAction getDeferredBatchAction(int action, UndoCallback undoCallback) {
3794ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao        return getDeferredAction(action, mCheckedSet.values(), true, undoCallback);
3795f0656a1ed2aaf07ee738505faa80b0723c8d217fmindyp    }
3796f0656a1ed2aaf07ee738505faa80b0723c8d217fmindyp
3797f0656a1ed2aaf07ee738505faa80b0723c8d217fmindyp    /**
3798f0656a1ed2aaf07ee738505faa80b0723c8d217fmindyp     * Get a destructive action for a menu action. This is a temporary method,
3799f0656a1ed2aaf07ee738505faa80b0723c8d217fmindyp     * to control the profusion of {@link DestructiveAction} classes that are
3800f0656a1ed2aaf07ee738505faa80b0723c8d217fmindyp     * created. Please do not copy this paradigm.
3801f0656a1ed2aaf07ee738505faa80b0723c8d217fmindyp     * @param action the resource ID of the menu action: R.id.delete, for
3802f0656a1ed2aaf07ee738505faa80b0723c8d217fmindyp     *            example
3803f0656a1ed2aaf07ee738505faa80b0723c8d217fmindyp     * @param target the conversations to act upon.
3804f0656a1ed2aaf07ee738505faa80b0723c8d217fmindyp     * @return a {@link DestructiveAction} that performs the specified action.
3805f0656a1ed2aaf07ee738505faa80b0723c8d217fmindyp     */
3806715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal    private DestructiveAction getDeferredAction(int action, Collection<Conversation> target,
380730c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            boolean batch, UndoCallback callback) {
380830c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        ConversationAction cAction = new ConversationAction(action, target, batch);
380930c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        cAction.setUndoCallback(callback);
381030c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        return cAction;
3811de3e74a82043733243c7391d7f983a5af8842891Mindy Pereira    }
3812de3e74a82043733243c7391d7f983a5af8842891Mindy Pereira
3813bc67bb1211654e69b1233e0f8759771873ca773eVikram Aggarwal    /**
3814bc67bb1211654e69b1233e0f8759771873ca773eVikram Aggarwal     * Class to change the folders that are assigned to a set of conversations. This is destructive
3815bc67bb1211654e69b1233e0f8759771873ca773eVikram Aggarwal     * because the user can remove the current folder from the conversation, in which case it has
3816bc67bb1211654e69b1233e0f8759771873ca773eVikram Aggarwal     * to be animated away from the current folder.
3817bc67bb1211654e69b1233e0f8759771873ca773eVikram Aggarwal     */
381841e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal    private class FolderDestruction implements DestructiveAction {
381977eee625cd8c317c605acfd06cd3a7e22120a0fdPaul Westbrook        private final Collection<Conversation> mTarget;
38208db7e407109532557718c6b8064792f2df7a073dMindy Pereira        private final ArrayList<FolderOperation> mFolderOps = new ArrayList<FolderOperation>();
382141e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal        private final boolean mIsDestructive;
38227f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal        /** Whether this destructive action has already been performed */
38237f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal        private boolean mCompleted;
3824e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon        private final boolean mIsSelectedSet;
3825e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon        private final boolean mShowUndo;
3826e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon        private final int mAction;
38276a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy        private final Folder mActionFolder;
382841e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal
382930c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        private UndoCallback mUndoCallback;
383030c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao
383141e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal        /**
383241e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal         * Create a new folder destruction object to act on the given conversations.
3833715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal         * @param target conversations to act upon.
38346a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy         * @param actionFolder the {@link Folder} being acted upon, used for displaying the undo bar
383541e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal         */
38367f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal        private FolderDestruction(final Collection<Conversation> target,
38378db7e407109532557718c6b8064792f2df7a073dMindy Pereira                final Collection<FolderOperation> folders, boolean isDestructive, boolean isBatch,
38386a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                boolean showUndo, int action, final Folder actionFolder) {
383977eee625cd8c317c605acfd06cd3a7e22120a0fdPaul Westbrook            mTarget = ImmutableList.copyOf(target);
38408db7e407109532557718c6b8064792f2df7a073dMindy Pereira            mFolderOps.addAll(folders);
384141e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal            mIsDestructive = isDestructive;
3842f3a455644f0899a01fff4782811fcff473f9aff0Mindy Pereira            mIsSelectedSet = isBatch;
384306642fab1bf4ab95b5dd97a65b262845cf60c865Mindy Pereira            mShowUndo = showUndo;
384401f30509c4c8ad0d13b7635447b7f023c7a0cab7Mindy Pereira            mAction = action;
38456a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy            mActionFolder = actionFolder;
384641e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal        }
384741e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal
384841e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal        @Override
384930c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        public void setUndoCallback(UndoCallback undoCallback) {
385030c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            mUndoCallback = undoCallback;
385130c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        }
385230c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao
385330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        @Override
385441e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal        public void performAction() {
38557f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal            if (isPerformed()) {
38567f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal                return;
38577f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal            }
38582cb6c1ca2b508331ee29d16b71f5ec024cd22555Jin Cao            if (mIsDestructive && mShowUndo && mTarget.size() > 0) {
3859cb0b30ee1d5cfcc267bc7de1e6ad78ed766c1e50mindyp                ToastBarOperation undoOp = new ToastBarOperation(mTarget.size(), mAction,
38606a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                        ToastBarOperation.UNDO, mIsSelectedSet, mActionFolder);
386141e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal                onUndoAvailable(undoOp);
386241e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal            }
38638db7e407109532557718c6b8064792f2df7a073dMindy Pereira            // For each conversation, for each operation, add/ remove the
38648db7e407109532557718c6b8064792f2df7a073dMindy Pereira            // appropriate folders.
3865cb0b30ee1d5cfcc267bc7de1e6ad78ed766c1e50mindyp            ArrayList<ConversationOperation> ops = new ArrayList<ConversationOperation>();
3866cb0b30ee1d5cfcc267bc7de1e6ad78ed766c1e50mindyp            ArrayList<Uri> folderUris;
3867cb0b30ee1d5cfcc267bc7de1e6ad78ed766c1e50mindyp            ArrayList<Boolean> adds;
38688db7e407109532557718c6b8064792f2df7a073dMindy Pereira            for (Conversation target : mTarget) {
3869cb0b30ee1d5cfcc267bc7de1e6ad78ed766c1e50mindyp                HashMap<Uri, Folder> targetFolders = Folder.hashMapForFolders(target
3870cb0b30ee1d5cfcc267bc7de1e6ad78ed766c1e50mindyp                        .getRawFolders());
3871cb0b30ee1d5cfcc267bc7de1e6ad78ed766c1e50mindyp                folderUris = new ArrayList<Uri>();
3872cb0b30ee1d5cfcc267bc7de1e6ad78ed766c1e50mindyp                adds = new ArrayList<Boolean>();
387301f30509c4c8ad0d13b7635447b7f023c7a0cab7Mindy Pereira                if (mIsDestructive) {
387401f30509c4c8ad0d13b7635447b7f023c7a0cab7Mindy Pereira                    target.localDeleteOnUpdate = true;
387501f30509c4c8ad0d13b7635447b7f023c7a0cab7Mindy Pereira                }
38768db7e407109532557718c6b8064792f2df7a073dMindy Pereira                for (FolderOperation op : mFolderOps) {
3877259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy                    folderUris.add(op.mFolder.folderUri.fullUri);
3878cb0b30ee1d5cfcc267bc7de1e6ad78ed766c1e50mindyp                    adds.add(op.mAdd ? Boolean.TRUE : Boolean.FALSE);
38798db7e407109532557718c6b8064792f2df7a073dMindy Pereira                    if (op.mAdd) {
3880259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy                        targetFolders.put(op.mFolder.folderUri.fullUri, op.mFolder);
38818db7e407109532557718c6b8064792f2df7a073dMindy Pereira                    } else {
3882259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy                        targetFolders.remove(op.mFolder.folderUri.fullUri);
38838db7e407109532557718c6b8064792f2df7a073dMindy Pereira                    }
38848db7e407109532557718c6b8064792f2df7a073dMindy Pereira                }
388526746eb4acb159f3ce0d411c10d85a1de3e958a0Paul Westbrook                ops.add(mConversationListCursor.getConversationFolderOperation(target,
388630c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                        folderUris, adds, targetFolders.values(), mUndoCallback));
3887389f0b21a9b21bc3607b810c88272d06d48d0fe6mindyp            }
3888389f0b21a9b21bc3607b810c88272d06d48d0fe6mindyp            if (mConversationListCursor != null) {
38899e2d407fdafeb874e640eb84017feaf784309075Scott Kennedy                mConversationListCursor.updateBulkValues(ops);
38908db7e407109532557718c6b8064792f2df7a073dMindy Pereira            }
38917f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal            refreshConversationList();
3892f3a455644f0899a01fff4782811fcff473f9aff0Mindy Pereira            if (mIsSelectedSet) {
3893ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao                mCheckedSet.clear();
3894f3a455644f0899a01fff4782811fcff473f9aff0Mindy Pereira            }
38957f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal        }
38968db7e407109532557718c6b8064792f2df7a073dMindy Pereira
38977f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal        /**
38987f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal         * Returns true if this action has been performed, false otherwise.
3899839ada22ea84251dde3305003d2f8fc5bf14914eAndy Huang         *
39007f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal         */
39017f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal        private synchronized boolean isPerformed() {
39027f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal            if (mCompleted) {
39037f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal                return true;
390441e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal            }
39057f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal            mCompleted = true;
39067f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal            return false;
390741e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal        }
390841e6e71568a0e7a32ebeaf909a4f6cb3ba4f47cfVikram Aggarwal    }
39097f602f7a64f176894ccb7942a6642f22584c3894Vikram Aggarwal
3910c84759cc974256b6af5545c4bd993b81a0a98f6fmindyp    public final DestructiveAction getFolderChange(Collection<Conversation> target,
3911c84759cc974256b6af5545c4bd993b81a0a98f6fmindyp            Collection<FolderOperation> folders, boolean isDestructive, boolean isBatch,
391230c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            boolean showUndo, final boolean isMoveTo, final Folder actionFolder,
391330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            UndoCallback undoCallback) {
3914c84759cc974256b6af5545c4bd993b81a0a98f6fmindyp        final DestructiveAction da = getDeferredFolderChange(target, folders, isDestructive,
391530c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                isBatch, showUndo, isMoveTo, actionFolder, undoCallback);
3916c84759cc974256b6af5545c4bd993b81a0a98f6fmindyp        registerDestructiveAction(da);
3917c84759cc974256b6af5545c4bd993b81a0a98f6fmindyp        return da;
3918c84759cc974256b6af5545c4bd993b81a0a98f6fmindyp    }
3919c84759cc974256b6af5545c4bd993b81a0a98f6fmindyp
3920c84759cc974256b6af5545c4bd993b81a0a98f6fmindyp    public final DestructiveAction getDeferredFolderChange(Collection<Conversation> target,
39218db7e407109532557718c6b8064792f2df7a073dMindy Pereira            Collection<FolderOperation> folders, boolean isDestructive, boolean isBatch,
392230c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            boolean showUndo, final boolean isMoveTo, final Folder actionFolder,
392330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            UndoCallback undoCallback) {
392430c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        final DestructiveAction fd = new FolderDestruction(target, folders, isDestructive, isBatch,
392530c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                showUndo, isMoveTo ? R.id.move_folder : R.id.change_folders, actionFolder);
392630c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        fd.setUndoCallback(undoCallback);
392730c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        return fd;
392801f30509c4c8ad0d13b7635447b7f023c7a0cab7Mindy Pereira    }
392901f30509c4c8ad0d13b7635447b7f023c7a0cab7Mindy Pereira
393001f30509c4c8ad0d13b7635447b7f023c7a0cab7Mindy Pereira    @Override
393101f30509c4c8ad0d13b7635447b7f023c7a0cab7Mindy Pereira    public final DestructiveAction getDeferredRemoveFolder(Collection<Conversation> target,
393201f30509c4c8ad0d13b7635447b7f023c7a0cab7Mindy Pereira            Folder toRemove, boolean isDestructive, boolean isBatch,
393330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            boolean showUndo, UndoCallback undoCallback) {
393401f30509c4c8ad0d13b7635447b7f023c7a0cab7Mindy Pereira        Collection<FolderOperation> folderOps = new ArrayList<FolderOperation>();
393501f30509c4c8ad0d13b7635447b7f023c7a0cab7Mindy Pereira        folderOps.add(new FolderOperation(toRemove, false));
393630c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        final DestructiveAction da = new FolderDestruction(target, folderOps, isDestructive, isBatch,
39376a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                showUndo, R.id.remove_folder, mFolder);
393830c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        da.setUndoCallback(undoCallback);
393930c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        return da;
394001f30509c4c8ad0d13b7635447b7f023c7a0cab7Mindy Pereira    }
394101f30509c4c8ad0d13b7635447b7f023c7a0cab7Mindy Pereira
39424f4782bcc53e13b30f1fd417beb1a6c04a54faedVikram Aggarwal    @Override
39434f4782bcc53e13b30f1fd417beb1a6c04a54faedVikram Aggarwal    public final void refreshConversationList() {
394475daee52f08c9bf66652b9bddf42cfbd1ca71987Vikram Aggarwal        final ConversationListFragment convList = getConversationListFragment();
394575daee52f08c9bf66652b9bddf42cfbd1ca71987Vikram Aggarwal        if (convList == null) {
394675daee52f08c9bf66652b9bddf42cfbd1ca71987Vikram Aggarwal            return;
394775daee52f08c9bf66652b9bddf42cfbd1ca71987Vikram Aggarwal        }
394875daee52f08c9bf66652b9bddf42cfbd1ca71987Vikram Aggarwal        convList.requestListRefresh();
394975daee52f08c9bf66652b9bddf42cfbd1ca71987Vikram Aggarwal    }
3950c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein
3951c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein    protected final ActionClickedListener getUndoClickedListener(
3952c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein            final AnimatedAdapter listAdapter) {
3953c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein        return new ActionClickedListener() {
3954c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein            @Override
3955f8ccdcfc26f362d9c20771e2295a0c0e8d1195f3Andrew Sapperstein            public void onActionClicked(Context context) {
3956c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                if (mAccount.undoUri != null) {
3957c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                    // NOTE: We might want undo to return the messages affected, in which case
3958c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                    // the resulting cursor might be interesting...
3959c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                    // TODO: Use UIProvider.SEQUENCE_QUERY_PARAMETER to indicate the set of
3960c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                    // commands to undo
3961c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                    if (mConversationListCursor != null) {
3962c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                        mConversationListCursor.undo(
3963c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                                mActivity.getActivityContext(), mAccount.undoUri);
3964c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                    }
3965c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                    if (listAdapter != null) {
3966c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                        listAdapter.setUndo(true);
3967c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                    }
3968c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                }
3969c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein            }
3970c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein        };
3971c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein    }
3972c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein
397341b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal    /**
397441b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal     * Shows an error toast in the bottom when a folder was not fetched successfully.
397541b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal     * @param folder the folder which could not be fetched.
397641b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal     * @param replaceVisibleToast if true, this should replace any currently visible toast.
397741b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal     */
39789d7519d18e8ddbcdd59b37eec7c85210fca0e8c2Andrew Sapperstein    protected final void showErrorToast(final Folder folder, boolean replaceVisibleToast) {
397900179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein
398041b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal        final ActionClickedListener listener;
398141b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal        final int actionTextResourceId;
398200179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein        final int lastSyncResult = folder.lastSyncResult;
3983deee5bee475a678bc2a4efe81c2900b1e5ac54dcJames Lemieux        switch (UIProvider.getResultFromLastSyncResult(lastSyncResult)) {
398400179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein            case UIProvider.LastSyncResult.CONNECTION_ERROR:
3985deee5bee475a678bc2a4efe81c2900b1e5ac54dcJames Lemieux                // The sync status that caused this failure.
3986deee5bee475a678bc2a4efe81c2900b1e5ac54dcJames Lemieux                final int syncStatus = UIProvider.getStatusFromLastSyncResult(lastSyncResult);
398741b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal                // Show: User explicitly pressed the refresh button and there is no connection
398841b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal                // Show: The first time the user enters the app and there is no connection
398941b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal                //       TODO(viki): Implement this.
399041b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal                // Reference: http://b/7202801
3991deee5bee475a678bc2a4efe81c2900b1e5ac54dcJames Lemieux                final boolean showToast = (syncStatus & UIProvider.SyncStatus.USER_REFRESH) != 0;
399241b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal                // Don't show: Already in the app; user switches to a synced label
399341b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal                // Don't show: In a live label and a background sync fails
399441b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal                final boolean avoidToast = !showToast && (folder.syncWindow > 0
3995deee5bee475a678bc2a4efe81c2900b1e5ac54dcJames Lemieux                        || (syncStatus & UIProvider.SyncStatus.BACKGROUND_SYNC) != 0);
399641b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal                if (avoidToast) {
399741b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal                    return;
399841b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal                }
399900179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                listener = getRetryClickedListener(folder);
400000179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                actionTextResourceId = R.string.retry;
400100179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                break;
400200179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein            case UIProvider.LastSyncResult.AUTH_ERROR:
400300179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                listener = getSignInClickedListener();
400400179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                actionTextResourceId = R.string.signin;
400500179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                break;
400600179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein            case UIProvider.LastSyncResult.SECURITY_ERROR:
400700179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                return; // Currently we do nothing for security errors.
400800179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein            case UIProvider.LastSyncResult.STORAGE_ERROR:
400900179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                listener = getStorageErrorClickedListener();
401000179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                actionTextResourceId = R.string.info;
401100179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                break;
401200179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein            case UIProvider.LastSyncResult.INTERNAL_ERROR:
401300179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                listener = getInternalErrorClickedListener();
401400179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                actionTextResourceId = R.string.report;
401500179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                break;
401600179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein            default:
401700179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                return;
401800179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein        }
401941b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal        mToastBar.show(listener,
402041b9e8f7bea47bbcae71b9ae81c3608a00a90e70Vikram Aggarwal                Utils.getSyncStatusText(mActivity.getActivityContext(), lastSyncResult),
402100179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein                actionTextResourceId,
40229d7519d18e8ddbcdd59b37eec7c85210fca0e8c2Andrew Sapperstein                replaceVisibleToast,
40230ec03e8d930a58c9138b5ebe84468f99c332c504James Lemieux                true /* autohide */,
40246a3d5ce0b18f58fcfa1af0315dc8ddc7331c2c5fScott Kennedy                new ToastBarOperation(1, 0, ToastBarOperation.ERROR, false, folder));
4025c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein    }
4026c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein
402700179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein    private ActionClickedListener getRetryClickedListener(final Folder folder) {
4028c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein        return new ActionClickedListener() {
4029c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein            @Override
4030f8ccdcfc26f362d9c20771e2295a0c0e8d1195f3Andrew Sapperstein            public void onActionClicked(Context context) {
4031c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                final Uri uri = folder.refreshUri;
4032c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein
4033c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                if (uri != null) {
40344969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook                    startAsyncRefreshTask(uri);
4035c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein                }
4036c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein            }
4037c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein        };
4038c2c9dc14aa184db1f05b8c060b27d97dda5a3ca4Andrew Sapperstein    }
403900179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein
404000179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein    private ActionClickedListener getSignInClickedListener() {
404100179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein        return new ActionClickedListener() {
404200179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein            @Override
4043f8ccdcfc26f362d9c20771e2295a0c0e8d1195f3Andrew Sapperstein            public void onActionClicked(Context context) {
4044122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                promptUserForAuthentication(mAccount);
404500179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein            }
404600179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein        };
404700179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein    }
404800179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein
404900179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein    private ActionClickedListener getStorageErrorClickedListener() {
405000179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein        return new ActionClickedListener() {
405100179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein            @Override
4052f8ccdcfc26f362d9c20771e2295a0c0e8d1195f3Andrew Sapperstein            public void onActionClicked(Context context) {
40534969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook                showStorageErrorDialog();
405400179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein            }
405500179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein        };
405600179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein    }
405700179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein
40584969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook    private void showStorageErrorDialog() {
40594969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook        DialogFragment fragment = (DialogFragment)
40604969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook                mFragmentManager.findFragmentByTag(SYNC_ERROR_DIALOG_FRAGMENT_TAG);
40614969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook        if (fragment == null) {
40624969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook            fragment = SyncErrorDialogFragment.newInstance();
40634969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook        }
40644969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook        fragment.show(mFragmentManager, SYNC_ERROR_DIALOG_FRAGMENT_TAG);
40654969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook    }
40664969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook
406700179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein    private ActionClickedListener getInternalErrorClickedListener() {
406800179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein        return new ActionClickedListener() {
406900179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein            @Override
4070f8ccdcfc26f362d9c20771e2295a0c0e8d1195f3Andrew Sapperstein            public void onActionClicked(Context context) {
407183e6b574cffd06164cad18ee388237a9427ceb3fPaul Westbrook                Utils.sendFeedback(mActivity, mAccount, true /* reportingProblem */);
407200179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein            }
407300179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein        };
407400179f19e413461498599ab7e6eb6914cc5c3671Andrew Sapperstein    }
40754969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook
40764969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook    @Override
40774969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook    public void onFooterViewLoadMoreClick(Folder folder) {
40784969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook        if (folder != null && folder.loadMoreUri != null) {
40794969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook            startAsyncRefreshTask(folder.loadMoreUri);
40804969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook        }
40814969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook    }
40824969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook
40834969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook    private void startAsyncRefreshTask(Uri uri) {
40844969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook        if (mFolderSyncTask != null) {
40854969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook            mFolderSyncTask.cancel(true);
40864969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook        }
40874969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook        mFolderSyncTask = new AsyncRefreshTask(mActivity.getActivityContext(), uri);
40884969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook        mFolderSyncTask.execute();
40894969e0c5f8ab837e1b0d29ea56d220e6404e9250Paul Westbrook    }
4090122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook
4091122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook    private void promptUserForAuthentication(Account account) {
4092429399b738f444911117978bf8229036e8814b1ePaul Westbrook        if (account != null && !Utils.isEmpty(account.reauthenticationIntentUri)) {
4093122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook            final Intent authenticationIntent =
4094122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook                    new Intent(Intent.ACTION_VIEW, account.reauthenticationIntentUri);
4095122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook            mActivity.startActivityForResult(authenticationIntent, REAUTHENTICATE_REQUEST_CODE);
4096122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook        }
4097122f7c2cf45cd5f07462a7b0a553f82828a8b1a2Paul Westbrook    }
4098ca87de41285bde382a894b6cb2d13c112c5d7a2fmindyp
4099ca87de41285bde382a894b6cb2d13c112c5d7a2fmindyp    @Override
4100ca87de41285bde382a894b6cb2d13c112c5d7a2fmindyp    public void onAccessibilityStateChanged() {
4101ca87de41285bde382a894b6cb2d13c112c5d7a2fmindyp        // Clear the cache of objects.
4102ca87de41285bde382a894b6cb2d13c112c5d7a2fmindyp        ConversationItemViewModel.onAccessibilityUpdated();
4103ca87de41285bde382a894b6cb2d13c112c5d7a2fmindyp        // Re-render the list if it exists.
4104a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        final ConversationListFragment frag = getConversationListFragment();
4105ca87de41285bde382a894b6cb2d13c112c5d7a2fmindyp        if (frag != null) {
4106ca87de41285bde382a894b6cb2d13c112c5d7a2fmindyp            AnimatedAdapter adapter = frag.getAnimatedAdapter();
4107ca87de41285bde382a894b6cb2d13c112c5d7a2fmindyp            if (adapter != null) {
4108ca87de41285bde382a894b6cb2d13c112c5d7a2fmindyp                adapter.notifyDataSetInvalidated();
4109ca87de41285bde382a894b6cb2d13c112c5d7a2fmindyp            }
4110ca87de41285bde382a894b6cb2d13c112c5d7a2fmindyp        }
4111ca87de41285bde382a894b6cb2d13c112c5d7a2fmindyp    }
41126cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal
41136cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal    @Override
411430c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao    public void makeDialogListener (final int action, final boolean isBatch,
411530c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao            UndoCallback undoCallback) {
4116b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal        final Collection<Conversation> target;
4117b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal        if (isBatch) {
4118ec0fa48764bede0772e9c711f3e33361b62f3608Jin Cao            target = mCheckedSet.values();
4119b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal        } else {
4120b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal            LogUtils.d(LOG_TAG, "Will act upon %s", mCurrentConversation);
4121b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal            target = Conversation.listOf(mCurrentConversation);
4122b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal        }
412330c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao        final DestructiveAction destructiveAction = getDeferredAction(action, target, isBatch,
412430c881a7d31203fc7b233abf4a0c1413de4cd517Jin Cao                undoCallback);
41256cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal        mDialogAction = action;
4126b8c3171710b7f97247fd0c841ce95f5f515ef576Vikram Aggarwal        mDialogFromSelectedSet = isBatch;
41276cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal        mDialogListener = new AlertDialog.OnClickListener() {
41286cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal            @Override
41296cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal            public void onClick(DialogInterface dialog, int which) {
4130caaeed3837316175111bb16e6b60aeb0d523debfScott Kennedy                delete(action, target, destructiveAction, isBatch);
41316cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal                // Afterwards, let's remove references to the listener and the action.
41326cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal                setListener(null, -1);
41336cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal            }
41346cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal        };
41356cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal    }
41366cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal
41376cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal    @Override
41386cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal    public AlertDialog.OnClickListener getListener() {
41396cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal        return mDialogListener;
41406cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal    }
41416cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal
41426cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal    /**
41436cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal     * Sets the listener for the positive action on a confirmation dialog.  Since only a single
41446cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal     * confirmation dialog can be shown, this overwrites the previous listener.  It is safe to
41456cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal     * unset the listener; in which case action should be set to -1.
4146715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @param listener the listener that will perform the task for this dialog's positive action.
4147715f83dc3556a0e33cba63acdf4b4842cc042af8Vikram Aggarwal     * @param action the action that created this dialog.
41486cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal     */
41496cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal    private void setListener(AlertDialog.OnClickListener listener, final int action){
41506cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal        mDialogListener = listener;
41516cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal        mDialogAction = action;
41526cadbfce032c5f8d61c873d91989bff1fb7cc4e5Vikram Aggarwal    }
415369a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal
415469a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    @Override
415569a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal    public VeiledAddressMatcher getVeiledAddressMatcher() {
415669a6cdff8afde77ec9bcd75a5651ee212344019eVikram Aggarwal        return mVeiledMatcher;
4157a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal    }
4158a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal
4159a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal    @Override
4160a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal    public void setDetachedMode() {
4161a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        // Tell the conversation list not to select anything.
4162a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        final ConversationListFragment frag = getConversationListFragment();
4163a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        if (frag != null) {
4164a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal            frag.setChoiceNone();
416581230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal        } else if (mIsTablet) {
416681230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal            // How did we ever land here? Detached mode, and no CLF on tablet???
4167a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal            LogUtils.e(LOG_TAG, "AAC.setDetachedMode(): CLF = null!");
4168a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        }
4169a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        mDetachedConvUri = mCurrentConversation.uri;
4170a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal    }
4171a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal
4172a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal    private void clearDetachedMode() {
4173a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        // Tell the conversation list to go back to its usual selection behavior.
4174a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        final ConversationListFragment frag = getConversationListFragment();
4175a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        if (frag != null) {
4176a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal            frag.revertChoiceMode();
417781230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal        } else if (mIsTablet) {
417881230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal            // How did we ever land here? Detached mode, and no CLF on tablet???
417981230ee24b65099109ee91add5a137d3795c454aVikram Aggarwal            LogUtils.e(LOG_TAG, "AAC.clearDetachedMode(): CLF = null on tablet!");
4180a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        }
4181a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal        mDetachedConvUri = null;
4182a91d00b4de3092b41af5f36436d3b49fe4586f64Vikram Aggarwal    }
418312b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang
418461f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang    @Override
41858712f412130f06cd21e68e89d87d28b226830e1aAndy Huang    public boolean shouldPreventListSwipesEntirely() {
41868712f412130f06cd21e68e89d87d28b226830e1aAndy Huang        return false;
41878712f412130f06cd21e68e89d87d28b226830e1aAndy Huang    }
41888712f412130f06cd21e68e89d87d28b226830e1aAndy Huang
41898712f412130f06cd21e68e89d87d28b226830e1aAndy Huang    @Override
419061f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang    public DrawerController getDrawerController() {
419161f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang        return mDrawerListener;
419261f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang    }
419361f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang
419461f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang    private class MailDrawerListener extends Observable<DrawerLayout.DrawerListener>
419561f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang            implements DrawerLayout.DrawerListener, DrawerController {
4196a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein        private int mDrawerState;
4197fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein        private float mOldSlideOffset;
4198a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein
4199a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein        public MailDrawerListener() {
4200a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein            mDrawerState = DrawerLayout.STATE_IDLE;
4201fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            mOldSlideOffset = 0.f;
4202a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein        }
4203a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein
420412b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        @Override
42053e95fe9af0d1f8d7557d6b8e9daf4cf08aade140Andy Huang        public boolean isDrawerEnabled() {
42063e95fe9af0d1f8d7557d6b8e9daf4cf08aade140Andy Huang            return AbstractActivityController.this.isDrawerEnabled();
42073e95fe9af0d1f8d7557d6b8e9daf4cf08aade140Andy Huang        }
42083e95fe9af0d1f8d7557d6b8e9daf4cf08aade140Andy Huang
42093e95fe9af0d1f8d7557d6b8e9daf4cf08aade140Andy Huang        @Override
421061f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang        public void registerDrawerListener(DrawerLayout.DrawerListener l) {
421161f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang            registerObserver(l);
421261f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang        }
421361f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang
421461f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang        @Override
421561f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang        public void unregisterDrawerListener(DrawerLayout.DrawerListener l) {
421661f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang            unregisterObserver(l);
421761f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang        }
421861f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang
421961f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang        @Override
422061f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang        public boolean isDrawerOpen() {
422161f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang            return isDrawerEnabled() && mDrawerContainer.isDrawerOpen(mDrawerPullout);
422261f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang        }
422361f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang
422461f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang        @Override
422561f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang        public boolean isDrawerVisible() {
422661f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang            return isDrawerEnabled() && mDrawerContainer.isDrawerVisible(mDrawerPullout);
422761f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang        }
422861f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang
422961f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang        @Override
4230f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang        public void toggleDrawerState() {
4231f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang            AbstractActivityController.this.toggleDrawerState();
4232f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang        }
4233f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang
4234f58e4c3c942033fce12b5f75f9e4d9e708c9ea6aAndy Huang        @Override
423512b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        public void onDrawerOpened(View drawerView) {
423612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            mDrawerToggle.onDrawerOpened(drawerView);
423761f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang
423861f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang            for (DrawerLayout.DrawerListener l : mObservers) {
423961f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang                l.onDrawerOpened(drawerView);
424061f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang            }
424112b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
424212b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang
424312b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        @Override
424412b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        public void onDrawerClosed(View drawerView) {
424512b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            mDrawerToggle.onDrawerClosed(drawerView);
424612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            if (mHasNewAccountOrFolder) {
424712b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang                refreshDrawer();
424812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            }
424910c99efe431335ce8db9924c3dcb0f977f3c63f3Scott Kennedy
425010c99efe431335ce8db9924c3dcb0f977f3c63f3Scott Kennedy            // When closed, we want to use either the burger, or up, based on where we are
425110c99efe431335ce8db9924c3dcb0f977f3c63f3Scott Kennedy            final int mode = mViewMode.getMode();
42529695e0046f79bd2d7166a411d6feff4cf0fb2539Jin Cao            final boolean isTopLevel = Folder.isRoot(mFolder);
4253207e662df01c550a4894bec03219820528365508Rohan Shah            updateDrawerIndicator(mode, isTopLevel);
425461f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang
425561f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang            for (DrawerLayout.DrawerListener l : mObservers) {
425661f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang                l.onDrawerClosed(drawerView);
425761f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang            }
425812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
425912b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang
426012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        /**
426112b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         * As part of the overriden function, it will animate the alpha of the conversation list
426212b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         * view along with the drawer sliding when we're in the process of switching accounts or
426312b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         * folders. Note, this is the same amount of work done as {@link ValueAnimator#ofFloat}.
426412b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         */
426512b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        @Override
426612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        public void onDrawerSlide(View drawerView, float slideOffset) {
426712b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            mDrawerToggle.onDrawerSlide(drawerView, slideOffset);
426812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            if (mHasNewAccountOrFolder && mListViewForAnimating != null) {
426912b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang                mListViewForAnimating.setAlpha(slideOffset);
427012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            }
4271fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein
4272fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            // This code handles when to change the visibility of action items
4273fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            // based on drawer state. The basic logic is that right when we
4274fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            // open the drawer, we hide the action items. We show the action items
4275fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            // when the drawer closes. However, due to the animation of the drawer closing,
4276fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            // to make the reshowing of the action items feel right, we make the items visible
4277fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            // slightly sooner.
4278fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            //
4279fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            // However, to make the animating behavior work properly, we have to know whether
4280fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            // we're animating open or closed. Only if we're animating closed do we want to
4281fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            // show the action items early. We save the last slide offset so that we can compare
4282fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            // the current slide offset to it to determine if we're opening or closing.
4283fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            if (mDrawerState == DrawerLayout.STATE_SETTLING) {
4284fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein                if (mHideMenuItems && slideOffset < 0.15f && mOldSlideOffset > slideOffset) {
4285fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein                    mHideMenuItems = false;
428652882ff08fe9420fe2dbb3f681545a330222e9c8Andrew Sapperstein                    mActivity.supportInvalidateOptionsMenu();
428743fab3224230c235db5be395f96096e1cac6b376Tony Mantler                    maybeEnableCabMode();
4288fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein                } else if (!mHideMenuItems && slideOffset > 0.f && mOldSlideOffset < slideOffset) {
4289fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein                    mHideMenuItems = true;
429052882ff08fe9420fe2dbb3f681545a330222e9c8Andrew Sapperstein                    mActivity.supportInvalidateOptionsMenu();
429143fab3224230c235db5be395f96096e1cac6b376Tony Mantler                    disableCabMode();
4292fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein                }
4293fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            } else {
4294fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein                if (mHideMenuItems && Float.compare(slideOffset, 0.f) == 0) {
4295fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein                    mHideMenuItems = false;
429652882ff08fe9420fe2dbb3f681545a330222e9c8Andrew Sapperstein                    mActivity.supportInvalidateOptionsMenu();
429743fab3224230c235db5be395f96096e1cac6b376Tony Mantler                    maybeEnableCabMode();
4298fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein                } else if (!mHideMenuItems && slideOffset > 0.f) {
4299fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein                    mHideMenuItems = true;
430052882ff08fe9420fe2dbb3f681545a330222e9c8Andrew Sapperstein                    mActivity.supportInvalidateOptionsMenu();
430143fab3224230c235db5be395f96096e1cac6b376Tony Mantler                    disableCabMode();
4302fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein                }
4303fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            }
4304fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein
4305fc5be1c9fb937c1e0b6e981581fbba2157a009acAndrew Sapperstein            mOldSlideOffset = slideOffset;
430610c99efe431335ce8db9924c3dcb0f977f3c63f3Scott Kennedy
430761f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang            for (DrawerLayout.DrawerListener l : mObservers) {
430861f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang                l.onDrawerSlide(drawerView, slideOffset);
430961f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang            }
431012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
431112b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang
431212b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        /**
431312b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         * This condition here should only be called when the drawer is stuck in a weird state
431412b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         * and doesn't register the onDrawerClosed, but shows up as idle. Make sure to refresh
431512b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         * and, more importantly, unlock the drawer when this is the case.
431612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         */
431712b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        @Override
431812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        public void onDrawerStateChanged(int newState) {
4319371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon            LogUtils.d(LOG_TAG, "AAC onDrawerStateChanged %d", newState);
4320a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein            mDrawerState = newState;
4321a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein            mDrawerToggle.onDrawerStateChanged(mDrawerState);
432261f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang
432361f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang            for (DrawerLayout.DrawerListener l : mObservers) {
432461f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang                l.onDrawerStateChanged(newState);
432561f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang            }
432661f26c2d1c1d3735cf883b58fe7e45550bb1a54cAndy Huang
4327e764cfd130c4f92ae823fd1f361ab70439e8ec95Andy Huang            if (mViewMode.isSearchMode()) {
4328371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon                return;
4329371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon            }
4330a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein            if (mDrawerState == DrawerLayout.STATE_IDLE) {
4331a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein                if (mHasNewAccountOrFolder) {
4332a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein                    refreshDrawer();
4333a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein                }
4334a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein                if (mConversationListLoadFinishedIgnored) {
4335a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein                    mConversationListLoadFinishedIgnored = false;
4336a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein                    final Bundle args = new Bundle();
4337a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein                    args.putParcelable(BUNDLE_ACCOUNT_KEY, mAccount);
4338a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein                    args.putParcelable(BUNDLE_FOLDER_KEY, mFolder);
4339a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein                    mActivity.getLoaderManager().initLoader(
4340a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein                            LOADER_CONVERSATION_LIST, args, mListCursorCallbacks);
4341a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein                }
434212b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            }
434312b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
434412b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang
434512b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        /**
434612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         * If we've reached a stable drawer state, unlock the drawer for usage, clear the
434712b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         * conversation list, and finish end actions. Also, make
434812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         * {@link #mHasNewAccountOrFolder} false to reflect we're done changing.
434912b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang         */
435012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        public void refreshDrawer() {
435112b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            mHasNewAccountOrFolder = false;
435212b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            mDrawerContainer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
435312b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            ConversationListFragment conversationList = getConversationListFragment();
435412b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            if (conversationList != null) {
435512b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang                conversationList.clear();
435612b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang            }
435754022ee4c98f02401ca567e643f24ed42aebe203Tony Mantler            mFolderOrAccountObservers.notifyChanged();
435812b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang        }
4359a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein
4360a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein        /**
4361a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein         * Returns the most recent update of the {@link DrawerLayout}'s state provided
4362a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein         * by {@link #onDrawerStateChanged(int)}.
4363a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein         * @return The {@link DrawerLayout}'s current state. One of
4364a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein         * {@link DrawerLayout#STATE_DRAGGING}, {@link DrawerLayout#STATE_IDLE},
4365a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein         * or {@link DrawerLayout#STATE_SETTLING}.
4366a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein         */
4367a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein        public int getDrawerState() {
4368a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein            return mDrawerState;
4369a3ce678020691f9aef7a3a4efcd015b396595486Andrew Sapperstein        }
437012b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang    }
437112b3ee458e04ac7e0b69302e671d10b24a5c98cfAndy Huang
43728a72b8590d9166ada55f69eeb083d2b8c5c915c9Scott Kennedy    @Override
43738a72b8590d9166ada55f69eeb083d2b8c5c915c9Scott Kennedy    public boolean isDrawerPullEnabled() {
4374371a71c7f41155a83526e4745c993585c236b9d7Martin Hibdon        return true;
43758a72b8590d9166ada55f69eeb083d2b8c5c915c9Scott Kennedy    }
43765747e152fa606c20261b18431f4a9f0ff3be7b5aAndrew Sapperstein
43775747e152fa606c20261b18431f4a9f0ff3be7b5aAndrew Sapperstein    @Override
43785747e152fa606c20261b18431f4a9f0ff3be7b5aAndrew Sapperstein    public boolean shouldHideMenuItems() {
43795747e152fa606c20261b18431f4a9f0ff3be7b5aAndrew Sapperstein        return mHideMenuItems;
43805747e152fa606c20261b18431f4a9f0ff3be7b5aAndrew Sapperstein    }
4381ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang
4382ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang    protected void navigateUpFolderHierarchy() {
4383ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang        new AsyncTask<Void, Void, Folder>() {
4384ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang            @Override
4385ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang            protected Folder doInBackground(final Void... params) {
4386ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                if (mInbox == null) {
4387ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                    // We don't have an inbox, but we need it
4388ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                    final Cursor cursor = mContext.getContentResolver().query(
4389ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                            mAccount.settings.defaultInbox, UIProvider.FOLDERS_PROJECTION, null,
4390ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                            null, null);
4391ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang
4392ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                    if (cursor != null) {
4393ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                        try {
4394ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                            if (cursor.moveToFirst()) {
4395ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                                mInbox = new Folder(cursor);
4396ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                            }
4397ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                        } finally {
4398ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                            cursor.close();
4399ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                        }
4400ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                    }
4401ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                }
4402ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang
4403ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                // Now try to load our parent
4404ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                final Folder folder;
4405ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang
44065ac10036fd45cd2a6da32abd02e942617edec50dAlice Yang                if (mFolder != null) {
4407e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon                    Cursor cursor = null;
4408e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon                    try {
4409e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon                        cursor = mContext.getContentResolver().query(mFolder.parent,
4410e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon                                UIProvider.FOLDERS_PROJECTION, null, null, null);
4411e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon
4412e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon                        if (cursor == null || !cursor.moveToFirst()) {
4413e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon                            // We couldn't load the parent, so use the inbox
4414e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon                            folder = mInbox;
4415e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon                        } else {
44165ac10036fd45cd2a6da32abd02e942617edec50dAlice Yang                            folder = new Folder(cursor);
4417e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon                        }
4418e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon                    } finally {
4419e78c40f7ea2159df17aec33ecc2599dfb866c3cdMartin Hibdon                        if (cursor != null) {
44205ac10036fd45cd2a6da32abd02e942617edec50dAlice Yang                            cursor.close();
44215ac10036fd45cd2a6da32abd02e942617edec50dAlice Yang                        }
4422ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                    }
44235ac10036fd45cd2a6da32abd02e942617edec50dAlice Yang                } else {
44245ac10036fd45cd2a6da32abd02e942617edec50dAlice Yang                    folder = mInbox;
4425ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                }
4426ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang
4427ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                return folder;
4428ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang            }
4429ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang
4430ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang            @Override
4431ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang            protected void onPostExecute(final Folder result) {
4432ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang                onFolderSelected(result);
4433ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang            }
4434ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
4435ebeef1babe39de2c1d97429a93ea956c655cd377Alice Yang    }
4436f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy
4437f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy    @Override
4438f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy    public Parcelable getConversationListScrollPosition(final String folderUri) {
4439f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy        return mConversationListScrollPositions.getParcelable(folderUri);
4440f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy    }
4441f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy
4442f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy    @Override
4443f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy    public void setConversationListScrollPosition(final String folderUri,
4444f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy            final Parcelable savedPosition) {
4445f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy        mConversationListScrollPositions.putParcelable(folderUri, savedPosition);
4446f77806ed36ab49861a5ea13a3ab34e5a3a21c022Scott Kennedy    }
444753de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein
444853de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein    @Override
444984662ce9ac05081b16dd58202acbc69c7e24a354Jin Cao    public boolean setupEmptyIconView(Folder folder, boolean isEmpty) {
445084662ce9ac05081b16dd58202acbc69c7e24a354Jin Cao        return false;
445184662ce9ac05081b16dd58202acbc69c7e24a354Jin Cao    }
445284662ce9ac05081b16dd58202acbc69c7e24a354Jin Cao
445384662ce9ac05081b16dd58202acbc69c7e24a354Jin Cao    @Override
445453de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein    public View.OnClickListener getNavigationViewClickListener() {
445553de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein        return mHomeButtonListener;
445653de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein    }
445753de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein
445853de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein    // TODO: Fold this into the outer class when b/16627877 is fixed
445953de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein    private class HomeButtonListener implements View.OnClickListener {
446053de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein        @Override
446153de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein        public void onClick(View v) {
4462405a344937675f57fc9c6988b2b124410a270f13Jin Cao            handleUpPress();
446353de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein        }
446453de448d6c67e56df4e3bcefb3e2d84c810e2e89Andrew Sapperstein    }
44654a5c530b0a67e22bd74df8f10f29278dc8d86459Vikram Aggarwal}
4466