blob: a1a5e173654490515087c5efc58a33503b8c6a80 [file] [log] [blame]
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -08001/*******************************************************************************
2 * Copyright (C) 2012 Google Inc.
3 * Licensed to The Android Open Source Project.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *******************************************************************************/
17
18package com.android.mail.ui;
19
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -080020import android.app.ActionBar;
Vikram Aggarwalb9e1a352012-01-24 15:23:38 -080021import android.app.ActionBar.LayoutParams;
Vikram Aggarwala55b36c2012-01-13 11:45:02 -080022import android.app.Activity;
Vikram Aggarwal54452ae2012-03-13 15:29:00 -070023import android.app.AlertDialog;
Vikram Aggarwala55b36c2012-01-13 11:45:02 -080024import android.app.Dialog;
Andy Huangf9a73482012-03-13 15:54:02 -070025import android.app.LoaderManager;
Vikram Aggarwal80aeac52012-02-07 15:27:20 -080026import android.content.ContentResolver;
Vikram Aggarwala55b36c2012-01-13 11:45:02 -080027import android.content.Context;
Vikram Aggarwal7dedb952012-02-16 16:10:23 -080028import android.content.CursorLoader;
Vikram Aggarwal54452ae2012-03-13 15:29:00 -070029import android.content.DialogInterface;
Vikram Aggarwala55b36c2012-01-13 11:45:02 -080030import android.content.Intent;
Vikram Aggarwal7dedb952012-02-16 16:10:23 -080031import android.content.Loader;
Vikram Aggarwal80aeac52012-02-07 15:27:20 -080032import android.database.Cursor;
Paul Westbrook23b74b92012-02-29 11:36:12 -080033import android.net.Uri;
Paul Westbrookdaef1ce2012-02-23 16:42:00 -080034import android.os.AsyncTask;
Vikram Aggarwala55b36c2012-01-13 11:45:02 -080035import android.os.Bundle;
Mindy Pereira21ab4902012-03-19 18:48:03 -070036import android.os.Handler;
Vikram Aggarwal6fbc87a2012-03-15 15:24:00 -070037import android.text.TextUtils;
Vikram Aggarwala55b36c2012-01-13 11:45:02 -080038import android.view.KeyEvent;
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -080039import android.view.LayoutInflater;
Vikram Aggarwala55b36c2012-01-13 11:45:02 -080040import android.view.Menu;
Mindy Pereira28d5f722012-02-15 12:32:40 -080041import android.view.MenuInflater;
Vikram Aggarwala55b36c2012-01-13 11:45:02 -080042import android.view.MenuItem;
43import android.view.MotionEvent;
44
Vikram Aggarwalb9e1a352012-01-24 15:23:38 -080045import com.android.mail.ConversationListContext;
Andy Huangf9a73482012-03-13 15:54:02 -070046import com.android.mail.R;
Mindy Pereira967ede62012-03-22 09:29:09 -070047import com.android.mail.browse.ConversationCursor;
48import com.android.mail.browse.SelectedConversationsActionMenu;
49import com.android.mail.browse.ConversationCursor.ConversationListener;
Mindy Pereira9b875682012-02-15 18:10:54 -080050import com.android.mail.compose.ComposeActivity;
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -080051import com.android.mail.providers.Account;
Mindy Pereira9b875682012-02-15 18:10:54 -080052import com.android.mail.providers.Conversation;
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -080053import com.android.mail.providers.Folder;
Paul Westbrookc2074c42012-03-22 15:26:58 -070054import com.android.mail.providers.MailAppProvider;
Mindy Pereiradac00542012-03-01 10:50:33 -080055import com.android.mail.providers.Settings;
Vikram Aggarwal80aeac52012-02-07 15:27:20 -080056import com.android.mail.providers.UIProvider;
Vikram Aggarwal54452ae2012-03-13 15:29:00 -070057import com.android.mail.providers.UIProvider.AutoAdvance;
Mindy Pereirac9d59182012-03-22 16:06:46 -070058import com.android.mail.providers.UIProvider.ConversationColumns;
Vikram Aggarwal54452ae2012-03-13 15:29:00 -070059import com.android.mail.providers.UIProvider.FolderCapabilities;
Mindy Pereira5cb0c3e2012-02-22 15:22:47 -080060import com.android.mail.utils.LogUtils;
Vikram Aggarwalfa131a22012-02-02 13:56:22 -080061import com.android.mail.utils.Utils;
Marc Blank167faa82012-03-21 13:11:53 -070062import com.google.common.collect.ImmutableList;
63import com.google.common.collect.Sets;
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -080064
Marc Blank167faa82012-03-21 13:11:53 -070065import java.util.ArrayList;
Vikram Aggarwal6fbc87a2012-03-15 15:24:00 -070066import java.util.Arrays;
Mindy Pereirafbe40192012-03-20 10:40:45 -070067import java.util.Collection;
Vikram Aggarwal6fbc87a2012-03-15 15:24:00 -070068import java.util.HashSet;
Paul Westbrook23b74b92012-02-29 11:36:12 -080069import java.util.Set;
70
71
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -080072/**
Mindy Pereira161f50d2012-02-28 15:47:19 -080073 * This is an abstract implementation of the Activity Controller. This class
74 * knows how to respond to menu items, state changes, layout changes, etc. It
75 * weaves together the views and listeners, dispatching actions to the
76 * respective underlying classes.
Vikram Aggarwala55b36c2012-01-13 11:45:02 -080077 * <p>
Mindy Pereira161f50d2012-02-28 15:47:19 -080078 * Even though this class is abstract, it should provide default implementations
79 * for most, if not all the methods in the ActivityController interface. This
80 * makes the task of the subclasses easier: OnePaneActivityController and
81 * TwoPaneActivityController can be concise when the common functionality is in
82 * AbstractActivityController.
83 * </p>
84 * <p>
85 * In the Gmail codebase, this was called BaseActivityController
86 * </p>
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -080087 */
Mindy Pereira967ede62012-03-22 09:29:09 -070088public abstract class AbstractActivityController implements ActivityController, ConversationListener {
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -080089 // Keys for serialization of various information in Bundles.
90 private static final String SAVED_LIST_CONTEXT = "saved-list-context";
Vikram Aggarwal6c511582012-02-27 10:59:47 -080091 private static final String SAVED_ACCOUNT = "saved-account";
Mindy Pereira967ede62012-03-22 09:29:09 -070092 // Batch conversations stored in the Bundle using this key.
93 private static final String SAVED_CONVERSATIONS = "saved-conversations";
Mindy Pereira5cb0c3e2012-02-22 15:22:47 -080094
Vikram Aggarwal6fbc87a2012-03-15 15:24:00 -070095 /** Are we on a tablet device or not. */
Vikram Aggarwald7a12cd2012-02-03 09:36:20 -080096 public final boolean IS_TABLET_DEVICE;
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -080097
Vikram Aggarwald7a12cd2012-02-03 09:36:20 -080098 protected Account mAccount;
Mindy Pereira28e0c342012-02-17 15:05:13 -080099 protected Folder mFolder;
Vikram Aggarwald7a12cd2012-02-03 09:36:20 -0800100 protected ActionBarView mActionBarView;
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800101 protected final RestrictedActivity mActivity;
Vikram Aggarwala55b36c2012-01-13 11:45:02 -0800102 protected final Context mContext;
Vikram Aggarwalec5cbf72012-03-08 15:10:35 -0800103 protected final RecentFolderList mRecentFolderList;
Vikram Aggarwal80aeac52012-02-07 15:27:20 -0800104 protected ConversationListContext mConvListContext;
Mindy Pereira9b875682012-02-15 18:10:54 -0800105 protected Conversation mCurrentConversation;
Vikram Aggarwald7a12cd2012-02-03 09:36:20 -0800106
Paul Westbrook6ead20d2012-03-19 14:48:14 -0700107 /** A {@link android.content.BroadcastReceiver} that suppresses new e-mail notifications. */
108 private SuppressNotificationReceiver mNewEmailReceiver = null;
109
Mindy Pereirafbe40192012-03-20 10:40:45 -0700110 protected Handler mHandler = new Handler();
Vikram Aggarwalb9e1a352012-01-24 15:23:38 -0800111 protected ConversationListFragment mConversationListFragment;
Mindy Pereira967ede62012-03-22 09:29:09 -0700112 private SelectedConversationsActionMenu mSelectedConversationsActionMenu;
Vikram Aggarwalfa131a22012-02-02 13:56:22 -0800113 /**
Mindy Pereira161f50d2012-02-28 15:47:19 -0800114 * The current mode of the application. All changes in mode are initiated by
115 * the activity controller. View mode changes are propagated to classes that
116 * attach themselves as listeners of view mode changes.
Vikram Aggarwalfa131a22012-02-02 13:56:22 -0800117 */
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800118 protected final ViewMode mViewMode;
Vikram Aggarwal80aeac52012-02-07 15:27:20 -0800119 protected ContentResolver mResolver;
Mindy Pereira0825efd2012-02-15 17:30:32 -0800120 protected FolderListFragment mFolderListFragment;
Mindy Pereira9b875682012-02-15 18:10:54 -0800121 protected ConversationViewFragment mConversationViewFragment;
Vikram Aggarwal7dedb952012-02-16 16:10:23 -0800122 protected boolean isLoaderInitialized = false;
Mindy Pereirab7b33e02012-02-21 15:32:19 -0800123 private AsyncRefreshTask mAsyncRefreshTask;
Mindy Pereira5cb0c3e2012-02-22 15:22:47 -0800124
Paul Westbrook23b74b92012-02-29 11:36:12 -0800125 private final Set<Uri> mCurrentAccountUris = Sets.newHashSet();
Mindy Pereira559c3b32012-03-01 13:17:47 -0800126 protected Settings mCachedSettings;
Mindy Pereira967ede62012-03-22 09:29:09 -0700127 protected ConversationCursor mConversationListCursor;
128 protected boolean mConversationListenerAdded = false;
129 /**
130 * Selected conversations, if any.
131 */
132 private ConversationSelectionSet mSelectedSet = new ConversationSelectionSet();
Mindy Pereiradac00542012-03-01 10:50:33 -0800133
Mindy Pereiraab486362012-03-21 18:18:53 -0700134
Vikram Aggarwal04ff99c2012-02-28 15:29:13 -0800135 protected static final String LOG_TAG = new LogUtils().getLogTag();
Vikram Aggarwalfa4b47e2012-03-09 13:02:46 -0800136 /** Constants used to differentiate between the types of loaders. */
137 private static final int LOADER_ACCOUNT_CURSOR = 0;
138 private static final int LOADER_ACCOUNT_SETTINGS = 1;
139 private static final int LOADER_FOLDER_CURSOR = 2;
140 private static final int LOADER_RECENT_FOLDERS = 3;
Mindy Pereira967ede62012-03-22 09:29:09 -0700141 private static final int LOADER_CONVERSATION_LIST = 4;
Mindy Pereiraab486362012-03-21 18:18:53 -0700142 private static final int LOADER_ACCOUNT_INBOX = 5;
143 private static final int LOADER_SEARCH = 6;
Vikram Aggarwala55b36c2012-01-13 11:45:02 -0800144
145 public AbstractActivityController(MailActivity activity, ViewMode viewMode) {
146 mActivity = activity;
147 mViewMode = viewMode;
148 mContext = activity.getApplicationContext();
Vikram Aggarwald7a12cd2012-02-03 09:36:20 -0800149 IS_TABLET_DEVICE = Utils.useTabletUI(mContext);
Marc Blank167faa82012-03-21 13:11:53 -0700150 mRecentFolderList = new RecentFolderList(mContext, this);
Mindy Pereira967ede62012-03-22 09:29:09 -0700151 // Allow the fragment to observe changes to its own selection set. No other object is
152 // aware of the selected set.
153 mSelectedSet.addObserver(this);
Vikram Aggarwala55b36c2012-01-13 11:45:02 -0800154 }
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800155
156 @Override
Vikram Aggarwal2175d0a2012-02-17 16:04:23 -0800157 public synchronized void attachConversationList(ConversationListFragment fragment) {
158 // If there is an existing fragment, unregister it
159 if (mConversationListFragment != null) {
160 mViewMode.removeListener(mConversationListFragment);
161 }
162 mConversationListFragment = fragment;
163 // If the current fragment is non-null, add it as a listener.
164 if (fragment != null) {
165 mViewMode.addListener(mConversationListFragment);
166 }
Vikram Aggarwalb9e1a352012-01-24 15:23:38 -0800167 }
168
169 @Override
Vikram Aggarwal2175d0a2012-02-17 16:04:23 -0800170 public synchronized void attachFolderList(FolderListFragment fragment) {
171 // If there is an existing fragment, unregister it
172 if (mFolderListFragment != null) {
173 mViewMode.removeListener(mFolderListFragment);
174 }
175 mFolderListFragment = fragment;
176 if (fragment != null) {
177 mViewMode.addListener(mFolderListFragment);
178 }
Mindy Pereira555140c2012-02-15 14:55:29 -0800179 }
180
181 @Override
Mindy Pereira9b875682012-02-15 18:10:54 -0800182 public void attachConversationView(ConversationViewFragment conversationViewFragment) {
183 mConversationViewFragment = conversationViewFragment;
184 }
185
186 @Override
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800187 public void clearSubject() {
188 // TODO(viki): Auto-generated method stub
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800189 }
190
191 @Override
Vikram Aggarwale9a81032012-02-22 13:15:35 -0800192 public Account getCurrentAccount() {
193 return mAccount;
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800194 }
195
196 @Override
197 public ConversationListContext getCurrentListContext() {
Vikram Aggarwale9a81032012-02-22 13:15:35 -0800198 return mConvListContext;
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800199 }
200
201 @Override
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800202 public String getHelpContext() {
Vikram Aggarwala55b36c2012-01-13 11:45:02 -0800203 return "Mail";
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800204 }
205
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800206 @Override
Vikram Aggarwald7a12cd2012-02-03 09:36:20 -0800207 public int getMode() {
208 return mViewMode.getMode();
209 }
210
211 @Override
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800212 public String getUnshownSubject(String subject) {
213 // Calculate how much of the subject is shown, and return the remaining.
214 return null;
215 }
216
217 @Override
218 public void handleConversationLoadError() {
219 // TODO(viki): Auto-generated method stub
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800220 }
221
Mindy Pereira967ede62012-03-22 09:29:09 -0700222 @Override
223 public ConversationCursor getConversationListCursor() {
224 return mConversationListCursor;
225 }
226
227 public void initConversationListCursor() {
228 mActivity.getLoaderManager().restartLoader(LOADER_CONVERSATION_LIST, Bundle.EMPTY,
229 new LoaderManager.LoaderCallbacks<ConversationCursor>() {
230
231 @Override
232 public void onLoadFinished(Loader<ConversationCursor> loader,
233 ConversationCursor data) {
234 mConversationListCursor = data;
235 if (mConversationListCursor.isRefreshReady()) {
236 mConversationListCursor.sync();
237 }
238 if (mConversationListFragment != null) {
239 mConversationListFragment.onCursorUpdated();
240 if (!mConversationListenerAdded) {
241 // TODO(mindyp): when we move to the cursor loader, we need
242 // to add/remove the listener when we create/ destroy loaders.
243 mConversationListCursor
244 .addListener(AbstractActivityController.this);
245 mConversationListenerAdded = true;
246 }
247 }
248 if (shouldShowFirstConversation()) {
249 if (mConversationListCursor.getCount() > 0) {
250 mConversationListCursor.moveToPosition(0);
251 mConversationListFragment.getListView().setItemChecked(0, true);
252 Conversation conv = new Conversation(mConversationListCursor);
253 conv.position = 0;
254 onConversationSelected(conv);
255 }
256 }
257
258 }
259
260 @Override
261 public void onLoaderReset(Loader<ConversationCursor> loader) {
262 if (mConversationListFragment == null) {
263 return;
264 }
265 mConversationListFragment.onCursorUpdated();
266 }
267
268 @Override
269 public Loader<ConversationCursor> onCreateLoader(int id, Bundle args) {
Mindy Pereira12a676a2012-03-23 13:00:22 -0700270 if (mConversationListFragment != null) {
271 mConversationListFragment.configureSearchResultHeader();
272 AnimatedAdapter adapter = mConversationListFragment
273 .getAnimatedAdapter();
274 if (adapter != null) {
275 adapter.hideFooter();
276 }
Mindy Pereira967ede62012-03-22 09:29:09 -0700277 }
278 return new ConversationCursorLoader((Activity) mActivity, mAccount,
279 UIProvider.CONVERSATION_PROJECTION, mFolder.conversationListUri);
280 }
281
282 });
283 }
284
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800285 /**
Mindy Pereira161f50d2012-02-28 15:47:19 -0800286 * Initialize the action bar. This is not visible to OnePaneController and
287 * TwoPaneController so they cannot override this behavior.
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800288 */
289 private void initCustomActionBarView() {
290 ActionBar actionBar = mActivity.getActionBar();
Mindy Pereira68f2e222012-03-07 10:36:54 -0800291 mActionBarView = (ActionBarView) LayoutInflater.from(mContext).inflate(
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800292 R.layout.actionbar_view, null);
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800293 if (actionBar != null && mActionBarView != null) {
Mindy Pereira161f50d2012-02-28 15:47:19 -0800294 // Why have a different variable for the same thing? We should apply
295 // the same actions
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800296 // on mActionBarView instead.
Vikram Aggarwalec5cbf72012-03-08 15:10:35 -0800297 mActionBarView.initialize(mActivity, this, mViewMode, actionBar, mRecentFolderList);
298 actionBar.setCustomView(mActionBarView, new ActionBar.LayoutParams(
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800299 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
300 actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
Vikram Aggarwal2a25d0c2012-02-21 16:43:10 -0800301 ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_TITLE);
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800302 }
303 }
304
305 /**
Mindy Pereira161f50d2012-02-28 15:47:19 -0800306 * Returns whether the conversation list fragment is visible or not.
307 * Different layouts will have their own notion on the visibility of
308 * fragments, so this method needs to be overriden.
309 *
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800310 * @return
311 */
312 protected abstract boolean isConversationListVisible();
313
314 @Override
Mindy Pereira28e0c342012-02-17 15:05:13 -0800315 public void onAccountChanged(Account account) {
Mindy Pereira28d5f722012-02-15 12:32:40 -0800316 if (!account.equals(mAccount)) {
317 mAccount = account;
Mindy Pereira12a676a2012-03-23 13:00:22 -0700318 mFolder = null;
319 // Reset settings; they are no longer valid.
320 onSettingsChanged(null);
Vikram Aggarwalfa4b47e2012-03-09 13:02:46 -0800321 mRecentFolderList.setCurrentAccount(account);
Andy Huangf9a73482012-03-13 15:54:02 -0700322 restartOptionalLoader(LOADER_RECENT_FOLDERS, null /* args */);
323 restartOptionalLoader(LOADER_ACCOUNT_SETTINGS, null /* args */);
Mindy Pereirabc57bf12012-02-29 14:39:09 -0800324 mActionBarView.setAccount(mAccount);
Mindy Pereira68f2e222012-03-07 10:36:54 -0800325 mActivity.invalidateOptionsMenu();
Paul Westbrook6ead20d2012-03-19 14:48:14 -0700326
327 disableNotificationsOnAccountChange(mAccount);
328
Paul Westbrookc2074c42012-03-22 15:26:58 -0700329 MailAppProvider.getInstance().setLastViewedAccount(mAccount.uri.toString());
Mindy Pereiraab486362012-03-21 18:18:53 -0700330 loadAccountInbox();
Mindy Pereira28d5f722012-02-15 12:32:40 -0800331 }
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800332 }
333
Mindy Pereiradac00542012-03-01 10:50:33 -0800334 public void onSettingsChanged(Settings settings) {
Mindy Pereirab4a43282012-03-23 16:20:03 -0700335 String oldUri = mCachedSettings != null ? mCachedSettings.defaultInbox.toString() : null;
336 String newUri = settings != null ? settings.defaultInbox.toString() : null;
Mindy Pereira12a676a2012-03-23 13:00:22 -0700337 mCachedSettings = settings;
338 resetActionBarIcon();
339 // Only restart the loader if the defaultInboxUri is not the same as
340 // the folder we are already loading.
Mindy Pereirab4a43282012-03-23 16:20:03 -0700341 boolean changed = !TextUtils.equals(oldUri, newUri);
Mindy Pereira12a676a2012-03-23 13:00:22 -0700342 if (settings != null && settings.defaultInbox != null && mFolder != null
Mindy Pereirab4a43282012-03-23 16:20:03 -0700343 && changed) {
Mindy Pereira12a676a2012-03-23 13:00:22 -0700344 mActivity.getLoaderManager().restartLoader(LOADER_ACCOUNT_INBOX, null, this);
Mindy Pereiraab486362012-03-21 18:18:53 -0700345 }
Mindy Pereiradac00542012-03-01 10:50:33 -0800346 }
347
Mindy Pereiraefe3d252012-03-01 14:20:44 -0800348 @Override
349 public Settings getSettings() {
350 return mCachedSettings;
351 }
352
Mindy Pereirae0828392012-03-08 10:38:40 -0800353 private void fetchSearchFolder(Intent intent) {
Mindy Pereiraab486362012-03-21 18:18:53 -0700354 Bundle args = new Bundle();
355 args.putString(ConversationListContext.EXTRA_SEARCH_QUERY, intent
Mindy Pereirae0828392012-03-08 10:38:40 -0800356 .getStringExtra(ConversationListContext.EXTRA_SEARCH_QUERY));
Mindy Pereiraab486362012-03-21 18:18:53 -0700357 mActivity.getLoaderManager().restartLoader(LOADER_SEARCH, args, this);
Mindy Pereirae0828392012-03-08 10:38:40 -0800358 }
359
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800360 @Override
Mindy Pereira28e0c342012-02-17 15:05:13 -0800361 public void onFolderChanged(Folder folder) {
Mindy Pereira7a3471f2012-03-06 12:23:41 -0800362 if (folder != null && !folder.equals(mFolder)) {
Mindy Pereira5cb0c3e2012-02-22 15:22:47 -0800363 setFolder(folder);
Mindy Pereira161f50d2012-02-28 15:47:19 -0800364 mConvListContext = ConversationListContext.forFolder(mContext, mAccount, mFolder);
Mindy Pereira28e0c342012-02-17 15:05:13 -0800365 showConversationList(mConvListContext);
Paul Westbrook9024b6d2012-03-19 13:57:55 -0700366
367 // Add the folder that we were viewing to the recent folders list.
368 // TODO: this may need to be fine tuned. If this is the signal that is indicating that
369 // the list is shown to the user, this could fire in one pane if the user goes directly
370 // to a conversation
Paul Westbrook7ebdfd02012-03-21 15:55:30 -0700371 updateRecentFolderList();
Mindy Pereira28e0c342012-02-17 15:05:13 -0800372 }
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800373 }
374
Paul Westbrook7ebdfd02012-03-21 15:55:30 -0700375 private void updateRecentFolderList() {
Mindy Pereiraab486362012-03-21 18:18:53 -0700376 if (mFolder != null) {
377 mRecentFolderList.setCurrentAccount(mAccount);
378 mRecentFolderList.touchFolder(mFolder);
379 }
Paul Westbrook7ebdfd02012-03-21 15:55:30 -0700380 }
381
Mindy Pereiraab486362012-03-21 18:18:53 -0700382 // TODO(mindyp): set this up to store a copy of the folder as a transient
383 // field in the account.
384 public void loadAccountInbox() {
385 mActivity.getLoaderManager().restartLoader(LOADER_ACCOUNT_INBOX, null, this);
Mindy Pereiraf6acdad2012-03-15 11:21:13 -0700386 }
387
Vikram Aggarwalec5cbf72012-03-08 15:10:35 -0800388 /** Set the current folder */
Mindy Pereira5cb0c3e2012-02-22 15:22:47 -0800389 private void setFolder(Folder folder) {
390 // Start watching folder for sync status.
Mindy Pereirae0458e82012-03-06 11:54:55 -0800391 if (folder != null && !folder.equals(mFolder)) {
Mindy Pereira9b623802012-03-07 17:15:49 -0800392 mActionBarView.setRefreshInProgress(false);
Mindy Pereira5cb0c3e2012-02-22 15:22:47 -0800393 mFolder = folder;
Mindy Pereiraf9323cd2012-02-29 13:47:09 -0800394 mActionBarView.setFolder(mFolder);
Vikram Aggarwalfa4b47e2012-03-09 13:02:46 -0800395 mActivity.getLoaderManager().restartLoader(LOADER_FOLDER_CURSOR, null, this);
Mindy Pereira12a676a2012-03-23 13:00:22 -0700396 initConversationListCursor();
Mindy Pereirae0458e82012-03-06 11:54:55 -0800397 } else if (folder == null) {
398 LogUtils.wtf(LOG_TAG, "Folder in setFolder is null");
Mindy Pereira5cb0c3e2012-02-22 15:22:47 -0800399 }
400 }
401
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800402 @Override
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800403 public void onActivityResult(int requestCode, int resultCode, Intent data) {
404 // TODO(viki): Auto-generated method stub
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800405 }
406
407 @Override
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800408 public void onConversationListVisibilityChanged(boolean visible) {
409 // TODO(viki): Auto-generated method stub
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800410 }
411
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800412 /**
Vikram Aggarwala55b36c2012-01-13 11:45:02 -0800413 * By default, doing nothing is right. A two-pane controller will need to
414 * override this.
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800415 */
416 @Override
417 public void onConversationVisibilityChanged(boolean visible) {
418 // Do nothing.
419 return;
420 }
421
422 @Override
Vikram Aggarwald7a12cd2012-02-03 09:36:20 -0800423 public boolean onCreate(Bundle savedState) {
Vikram Aggarwala55b36c2012-01-13 11:45:02 -0800424 // Initialize the action bar view.
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800425 initCustomActionBarView();
Vikram Aggarwala55b36c2012-01-13 11:45:02 -0800426 // Allow shortcut keys to function for the ActionBar and menus.
427 mActivity.setDefaultKeyMode(Activity.DEFAULT_KEYS_SHORTCUT);
Vikram Aggarwal80aeac52012-02-07 15:27:20 -0800428 mResolver = mActivity.getContentResolver();
Vikram Aggarwal2175d0a2012-02-17 16:04:23 -0800429
Paul Westbrook6ead20d2012-03-19 14:48:14 -0700430 mNewEmailReceiver = new SuppressNotificationReceiver();
431
Mindy Pereira161f50d2012-02-28 15:47:19 -0800432 // All the individual UI components listen for ViewMode changes. This
Mindy Pereirab849dfb2012-03-07 18:13:15 -0800433 // simplifies the amount of logic in the AbstractActivityController, but increases the
434 // possibility of timing-related bugs.
Vikram Aggarwala55b36c2012-01-13 11:45:02 -0800435 mViewMode.addListener(this);
Vikram Aggarwal2175d0a2012-02-17 16:04:23 -0800436 assert (mActionBarView != null);
437 mViewMode.addListener(mActionBarView);
438
Vikram Aggarwala55b36c2012-01-13 11:45:02 -0800439 restoreState(savedState);
Vikram Aggarwald7a12cd2012-02-03 09:36:20 -0800440 return true;
Vikram Aggarwala55b36c2012-01-13 11:45:02 -0800441 }
442
Vikram Aggarwala55b36c2012-01-13 11:45:02 -0800443 @Override
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800444 public Dialog onCreateDialog(int id, Bundle bundle) {
Vikram Aggarwala55b36c2012-01-13 11:45:02 -0800445 // TODO(viki): Auto-generated method stub
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800446 return null;
Vikram Aggarwala55b36c2012-01-13 11:45:02 -0800447 }
448
449 @Override
450 public boolean onCreateOptionsMenu(Menu menu) {
Mindy Pereira28d5f722012-02-15 12:32:40 -0800451 MenuInflater inflater = mActivity.getMenuInflater();
Mindy Pereiraf5acda42012-02-15 20:13:59 -0800452 inflater.inflate(mActionBarView.getOptionsMenuId(), menu);
Mindy Pereira68f2e222012-03-07 10:36:54 -0800453 mActionBarView.onCreateOptionsMenu(menu);
Mindy Pereira28d5f722012-02-15 12:32:40 -0800454 return true;
Vikram Aggarwala55b36c2012-01-13 11:45:02 -0800455 }
456
457 @Override
Vikram Aggarwalb9e1a352012-01-24 15:23:38 -0800458 public boolean onKeyDown(int keyCode, KeyEvent event) {
459 // TODO(viki): Auto-generated method stub
460 return false;
461 }
462
463 @Override
Vikram Aggarwala55b36c2012-01-13 11:45:02 -0800464 public boolean onOptionsItemSelected(MenuItem item) {
Vikram Aggarwal54452ae2012-03-13 15:29:00 -0700465 final int id = item.getItemId();
Mindy Pereira9b875682012-02-15 18:10:54 -0800466 boolean handled = true;
Mindy Pereira28d5f722012-02-15 12:32:40 -0800467 switch (id) {
Mindy Pereiraf5acda42012-02-15 20:13:59 -0800468 case android.R.id.home:
469 onUpPressed();
470 break;
Mindy Pereira9b875682012-02-15 18:10:54 -0800471 case R.id.compose:
472 ComposeActivity.compose(mActivity.getActivityContext(), mAccount);
473 break;
Mindy Pereira28d5f722012-02-15 12:32:40 -0800474 case R.id.show_all_folders:
475 showFolderList();
476 break;
Mindy Pereira28e0c342012-02-17 15:05:13 -0800477 case R.id.refresh:
478 requestFolderRefresh();
479 break;
Mindy Pereira1f936682012-03-02 11:30:33 -0800480 case R.id.settings:
481 Utils.showSettings(mActivity.getActivityContext(), mAccount);
Paul Westbrook2861b6a2012-02-15 15:25:34 -0800482 break;
Paul Westbrook94e440d2012-02-24 11:03:47 -0800483 case R.id.help_info_menu_item:
484 // TODO: enable context sensitive help
485 Utils.showHelp(mActivity.getActivityContext(), mAccount.helpIntentUri, null);
486 break;
Vikram Aggarwal54452ae2012-03-13 15:29:00 -0700487 case R.id.feedback_menu_item:
Mindy Pereirafbe40192012-03-20 10:40:45 -0700488 Utils.sendFeedback(mActivity.getActivityContext(), mAccount);
489 break;
Mindy Pereira9b875682012-02-15 18:10:54 -0800490 default:
491 handled = false;
492 break;
Mindy Pereira28d5f722012-02-15 12:32:40 -0800493 }
Mindy Pereira9b875682012-02-15 18:10:54 -0800494 return handled;
Vikram Aggarwala55b36c2012-01-13 11:45:02 -0800495 }
496
Vikram Aggarwal6fbc87a2012-03-15 15:24:00 -0700497 /**
Mindy Pereira21ab4902012-03-19 18:48:03 -0700498 * Return the auto advance setting for the current account.
499 * @param activity
500 * @return the autoadvance setting, a constant from {@link AutoAdvance}
501 */
502 static int getAutoAdvanceSetting(RestrictedActivity activity) {
503 final Settings settings = activity.getSettings();
504 // TODO(mindyp): if this isn't set, then show the dialog telling the user to set it.
505 // Remove defaulting to AutoAdvance.LIST.
506 final int autoAdvance = (settings != null) ?
507 (settings.autoAdvance == AutoAdvance.UNSET ?
508 AutoAdvance.LIST : settings.autoAdvance)
509 : AutoAdvance.LIST;
510 return autoAdvance;
511 }
512
513 /**
Vikram Aggarwal6fbc87a2012-03-15 15:24:00 -0700514 * Implements folder changes. This class is a listener because folder changes need to be
515 * performed <b>after</b> the ConversationListFragment has finished animating away the
516 * removal of the conversation.
517 *
518 */
Mindy Pereirae5f4dc02012-03-21 16:08:53 -0700519 protected abstract class FolderChangeListener implements ActionCompleteListener {
520 protected final String mFolderChangeList;
521 protected final boolean mDestructiveChange;
Vikram Aggarwal6fbc87a2012-03-15 15:24:00 -0700522
523 public FolderChangeListener(String changeList, boolean destructive) {
524 mFolderChangeList = changeList;
525 mDestructiveChange = destructive;
526 }
527
Mindy Pereirae5f4dc02012-03-21 16:08:53 -0700528 public abstract void onActionComplete();
Vikram Aggarwal6fbc87a2012-03-15 15:24:00 -0700529 }
530
531 /**
Vikram Aggarwal54452ae2012-03-13 15:29:00 -0700532 * Update the specified column name in conversation for a boolean value.
533 * @param columnName
534 * @param value
535 */
Mindy Pereirafbe40192012-03-20 10:40:45 -0700536 protected void updateCurrentConversation(String columnName, boolean value) {
Vikram Aggarwal54452ae2012-03-13 15:29:00 -0700537 Conversation.updateBoolean(mContext, ImmutableList.of(mCurrentConversation), columnName,
538 value);
Mindy Pereirac9d59182012-03-22 16:06:46 -0700539 if (mConversationListFragment != null) {
540 mConversationListFragment.requestListRefresh();
541 }
Vikram Aggarwal54452ae2012-03-13 15:29:00 -0700542 }
543
544 /**
545 * Update the specified column name in conversation for an integer value.
546 * @param columnName
547 * @param value
548 */
Mindy Pereirafbe40192012-03-20 10:40:45 -0700549 protected void updateCurrentConversation(String columnName, int value) {
Vikram Aggarwal54452ae2012-03-13 15:29:00 -0700550 Conversation.updateInt(mContext, ImmutableList.of(mCurrentConversation), columnName, value);
Mindy Pereirac9d59182012-03-22 16:06:46 -0700551 if (mConversationListFragment != null) {
552 mConversationListFragment.requestListRefresh();
553 }
554 }
555
556 protected void updateCurrentConversation(String columnName, String value) {
557 Conversation.updateString(mContext, ImmutableList.of(mCurrentConversation), columnName,
558 value);
559 if (mConversationListFragment != null) {
560 mConversationListFragment.requestListRefresh();
561 }
Vikram Aggarwal54452ae2012-03-13 15:29:00 -0700562 }
563
Mindy Pereira28e0c342012-02-17 15:05:13 -0800564 private void requestFolderRefresh() {
565 if (mFolder != null) {
Mindy Pereirab7b33e02012-02-21 15:32:19 -0800566 if (mAsyncRefreshTask != null) {
567 mAsyncRefreshTask.cancel(true);
568 }
Mindy Pereira5cb0c3e2012-02-22 15:22:47 -0800569 mAsyncRefreshTask = new AsyncRefreshTask(mContext, mFolder);
Mindy Pereirab7b33e02012-02-21 15:32:19 -0800570 mAsyncRefreshTask.execute();
Mindy Pereira28e0c342012-02-17 15:05:13 -0800571 }
572 }
573
Mindy Pereirafbe40192012-03-20 10:40:45 -0700574 /**
575 * Confirm (based on user's settings) and delete a conversation from the conversation list and
576 * from the database.
577 * @param showDialog
578 * @param confirmResource
579 * @param listener
580 */
581 protected void confirmAndDelete(boolean showDialog, int confirmResource,
582 final ActionCompleteListener listener) {
583 final ArrayList<Conversation> single = new ArrayList<Conversation>();
584 single.add(mCurrentConversation);
585 if (showDialog) {
586 final AlertDialog.OnClickListener onClick = new AlertDialog.OnClickListener() {
587 @Override
588 public void onClick(DialogInterface dialog, int which) {
589 requestDelete(listener);
590 }
591 };
592 final CharSequence message = Utils.formatPlural(mContext, confirmResource, 1);
593 new AlertDialog.Builder(mActivity.getActivityContext()).setMessage(message)
594 .setPositiveButton(R.string.ok, onClick)
595 .setNegativeButton(R.string.cancel, null)
596 .create().show();
597 } else {
598 requestDelete(listener);
599 }
600 }
601
602
603 protected abstract void requestDelete(ActionCompleteListener listener);
604
Vikram Aggarwal6fbc87a2012-03-15 15:24:00 -0700605
606 @Override
Vikram Aggarwala55b36c2012-01-13 11:45:02 -0800607 public void onPrepareDialog(int id, Dialog dialog, Bundle bundle) {
608 // TODO(viki): Auto-generated method stub
609
610 }
611
612 @Override
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800613 public boolean onPrepareOptionsMenu(Menu menu) {
Mindy Pereirab849dfb2012-03-07 18:13:15 -0800614 mActionBarView.onPrepareOptionsMenu(menu);
Mindy Pereira363451a2012-02-22 14:14:46 -0800615 return true;
Vikram Aggarwala55b36c2012-01-13 11:45:02 -0800616 }
617
Mindy Pereira68f2e222012-03-07 10:36:54 -0800618 @Override
619 public void onPause() {
620 isLoaderInitialized = false;
Paul Westbrook6ead20d2012-03-19 14:48:14 -0700621
622 enableNotifications();
Paul Westbrook94e440d2012-02-24 11:03:47 -0800623 }
624
Vikram Aggarwala55b36c2012-01-13 11:45:02 -0800625 @Override
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800626 public void onResume() {
Paul Westbrook6ead20d2012-03-19 14:48:14 -0700627 // Register the receiver that will prevent the status receiver from
628 // displaying its notification icon as long as we're running.
629 // The SupressNotificationReceiver will block the broadcast if we're looking at the folder
630 // that the notification was received for.
631 disableNotifications();
632
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800633 if (mActionBarView != null) {
634 mActionBarView.onResume();
635 }
Paul Westbrook6ead20d2012-03-19 14:48:14 -0700636
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800637 }
638
639 @Override
640 public void onSaveInstanceState(Bundle outState) {
Vikram Aggarwal6c511582012-02-27 10:59:47 -0800641 if (mAccount != null) {
642 LogUtils.d(LOG_TAG, "Saving the account now");
643 outState.putParcelable(SAVED_ACCOUNT, mAccount);
644 }
Vikram Aggarwal80aeac52012-02-07 15:27:20 -0800645 if (mConvListContext != null) {
646 outState.putBundle(SAVED_LIST_CONTEXT, mConvListContext.toBundle());
Vikram Aggarwal8b152632012-02-03 14:58:45 -0800647 }
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800648 }
649
650 @Override
Mindy Pereira68f2e222012-03-07 10:36:54 -0800651 public void onSearchRequested(String query) {
652 Intent intent = new Intent();
653 intent.setAction(Intent.ACTION_SEARCH);
654 intent.putExtra(ConversationListContext.EXTRA_SEARCH_QUERY, query);
655 intent.putExtra(Utils.EXTRA_ACCOUNT, mAccount);
656 intent.setComponent(mActivity.getComponentName());
657 mActivity.startActivity(intent);
Vikram Aggarwala55b36c2012-01-13 11:45:02 -0800658 }
659
660 @Override
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800661 public void onStartDragMode() {
662 // TODO(viki): Auto-generated method stub
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800663 }
664
665 @Override
666 public void onStop() {
667 // TODO(viki): Auto-generated method stub
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800668 }
669
670 @Override
671 public void onStopDragMode() {
672 // TODO(viki): Auto-generated method stub
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800673 }
674
Vikram Aggarwalfa131a22012-02-02 13:56:22 -0800675 /**
Mindy Pereira161f50d2012-02-28 15:47:19 -0800676 * {@inheritDoc} Subclasses must override this to listen to mode changes
677 * from the ViewMode. Subclasses <b>must</b> call the parent's
678 * onViewModeChanged since the parent will handle common state changes.
Vikram Aggarwalfa131a22012-02-02 13:56:22 -0800679 */
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800680 @Override
Vikram Aggarwalfa131a22012-02-02 13:56:22 -0800681 public void onViewModeChanged(int newMode) {
682 // Perform any mode specific work here.
Mindy Pereira161f50d2012-02-28 15:47:19 -0800683 // reset the action bar icon based on the mode. Why don't the individual
684 // controllers do
Vikram Aggarwalfa131a22012-02-02 13:56:22 -0800685 // this themselves?
Vikram Aggarwala55b36c2012-01-13 11:45:02 -0800686
Vikram Aggarwale9a81032012-02-22 13:15:35 -0800687 // In conversation list mode, clean up the conversation.
Vikram Aggarwalfa131a22012-02-02 13:56:22 -0800688 if (newMode == ViewMode.CONVERSATION_LIST) {
689 // Clean up the conversation here.
690 }
691
Mindy Pereira161f50d2012-02-28 15:47:19 -0800692 // We don't want to invalidate the options menu when switching to
693 // conversation
Vikram Aggarwalfa131a22012-02-02 13:56:22 -0800694 // mode, as it will happen when the conversation finishes loading.
695 if (newMode != ViewMode.CONVERSATION) {
696 mActivity.invalidateOptionsMenu();
697 }
Vikram Aggarwala55b36c2012-01-13 11:45:02 -0800698 }
699
700 @Override
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800701 public void onWindowFocusChanged(boolean hasFocus) {
702 // TODO(viki): Auto-generated method stub
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800703 }
704
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800705 /**
706 * @param savedState
707 */
708 protected void restoreListContext(Bundle savedState) {
Vikram Aggarwal8b152632012-02-03 14:58:45 -0800709 Bundle listContextBundle = savedState.getBundle(SAVED_LIST_CONTEXT);
710 if (listContextBundle != null) {
Vikram Aggarwal80aeac52012-02-07 15:27:20 -0800711 mConvListContext = ConversationListContext.forBundle(listContextBundle);
Mindy Pereiradac00542012-03-01 10:50:33 -0800712 mFolder = mConvListContext.folder;
Vikram Aggarwal8b152632012-02-03 14:58:45 -0800713 }
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800714 }
715
716 /**
Mindy Pereira161f50d2012-02-28 15:47:19 -0800717 * Restore the state from the previous bundle. Subclasses should call this
718 * method from the parent class, since it performs important UI
719 * initialization.
720 *
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800721 * @param savedState
722 */
723 protected void restoreState(Bundle savedState) {
Mindy Pereira68f2e222012-03-07 10:36:54 -0800724 final Intent intent = mActivity.getIntent();
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800725 if (savedState != null) {
726 restoreListContext(savedState);
Vikram Aggarwal6c511582012-02-27 10:59:47 -0800727 mAccount = savedState.getParcelable(SAVED_ACCOUNT);
Mindy Pereirab849dfb2012-03-07 18:13:15 -0800728 mActionBarView.setAccount(mAccount);
Andy Huangf9a73482012-03-13 15:54:02 -0700729 restartOptionalLoader(LOADER_ACCOUNT_SETTINGS, null /* args */);
Mindy Pereira68f2e222012-03-07 10:36:54 -0800730 } else if (intent != null) {
731 if (Intent.ACTION_VIEW.equals(intent.getAction())) {
Mindy Pereirad8542ad2012-02-29 13:54:17 -0800732 if (intent.hasExtra(Utils.EXTRA_ACCOUNT)) {
733 mAccount = ((Account) intent.getParcelableExtra(Utils.EXTRA_ACCOUNT));
Mindy Pereirab849dfb2012-03-07 18:13:15 -0800734 mActionBarView.setAccount(mAccount);
Andy Huangf9a73482012-03-13 15:54:02 -0700735 restartOptionalLoader(LOADER_ACCOUNT_SETTINGS, null /* args */);
Mindy Pereira68f2e222012-03-07 10:36:54 -0800736 mActivity.invalidateOptionsMenu();
Mindy Pereirad8542ad2012-02-29 13:54:17 -0800737 }
738 if (intent.hasExtra(Utils.EXTRA_FOLDER)) {
739 // Open the folder.
740 LogUtils.d(LOG_TAG, "SHOW THE FOLDER at %s",
741 intent.getParcelableExtra(Utils.EXTRA_FOLDER));
742 onFolderChanged((Folder) intent.getParcelableExtra(Utils.EXTRA_FOLDER));
743 }
Mindy Pereira161f50d2012-02-28 15:47:19 -0800744 if (intent.hasExtra(Utils.EXTRA_CONVERSATION)) {
745 // Open the conversation.
746 LogUtils.d(LOG_TAG, "SHOW THE CONVERSATION at %s",
747 intent.getParcelableExtra(Utils.EXTRA_CONVERSATION));
Mindy Pereira5040f1a2012-03-20 10:14:06 -0700748 setCurrentConversation((Conversation) intent
Mindy Pereira161f50d2012-02-28 15:47:19 -0800749 .getParcelableExtra(Utils.EXTRA_CONVERSATION));
Mindy Pereira5040f1a2012-03-20 10:14:06 -0700750 showConversation(this.mCurrentConversation);
Mindy Pereira161f50d2012-02-28 15:47:19 -0800751 }
Mindy Pereira68f2e222012-03-07 10:36:54 -0800752 } else if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
753 mViewMode.enterSearchResultsListMode();
754 mAccount = ((Account) intent.getParcelableExtra(Utils.EXTRA_ACCOUNT));
Mindy Pereirab849dfb2012-03-07 18:13:15 -0800755 mActionBarView.setAccount(mAccount);
Mindy Pereirae0828392012-03-08 10:38:40 -0800756 fetchSearchFolder(intent);
Mindy Pereira161f50d2012-02-28 15:47:19 -0800757 }
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800758 }
Mindy Pereira967ede62012-03-22 09:29:09 -0700759
760 /**
761 * Restore the state of selected conversations. This needs to be done after the correct mode
762 * is set and the action bar is fully initialized. If not, several key pieces of state
763 * information will be missing, and the split views may not be initialized correctly.
764 * @param savedState
765 */
766 restoreSelectedConversations(intent.getExtras());
Mindy Pereira68f2e222012-03-07 10:36:54 -0800767 // Create the accounts loader; this loads the account switch spinner.
Vikram Aggarwalfa4b47e2012-03-09 13:02:46 -0800768 mActivity.getLoaderManager().initLoader(LOADER_ACCOUNT_CURSOR, null, this);
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800769 }
770
Mindy Pereira967ede62012-03-22 09:29:09 -0700771 private void restoreSelectedConversations(Bundle savedState) {
772 if (savedState == null) {
773 onSetEmpty();
774 return;
775 }
776 mSelectedSet = savedState.getParcelable(SAVED_CONVERSATIONS);
777 if (mSelectedSet == null) {
778 mSelectedSet = new ConversationSelectionSet();
779 }
780 if (mSelectedSet.isEmpty()) {
781 onSetEmpty();
782 return;
783 }
784 // We have some selected conversations. Perform all the actions needed.
785 onSetPopulated(mSelectedSet);
786 }
787
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800788 @Override
789 public void setSubject(String subject) {
790 // Do something useful with the subject. This requires changing the
791 // conversation view's subject text.
792 }
793
Vikram Aggarwalec5cbf72012-03-08 15:10:35 -0800794 /**
795 * Children can override this method, but they must call super.showConversation().
796 * {@inheritDoc}
797 */
798 @Override
799 public void showConversation(Conversation conversation) {
Vikram Aggarwalec5cbf72012-03-08 15:10:35 -0800800 }
801
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -0800802 @Override
Mindy Pereira9b875682012-02-15 18:10:54 -0800803 public void onConversationSelected(Conversation conversation) {
Mindy Pereira5040f1a2012-03-20 10:14:06 -0700804 setCurrentConversation(conversation);
Mindy Pereira9b875682012-02-15 18:10:54 -0800805 showConversation(mCurrentConversation);
Mindy Pereira68f2e222012-03-07 10:36:54 -0800806 if (mConvListContext != null && mConvListContext.isSearchResult()) {
807 mViewMode.enterSearchResultsConversationMode();
808 } else {
809 mViewMode.enterConversationMode();
810 }
Vikram Aggarwal7d602882012-02-07 15:01:20 -0800811 }
Mindy Pereira555140c2012-02-15 14:55:29 -0800812
Mindy Pereira5040f1a2012-03-20 10:14:06 -0700813 public void setCurrentConversation(Conversation conversation) {
814 mCurrentConversation = conversation;
815 }
816
Vikram Aggarwala9b93f32012-02-23 14:51:58 -0800817 /**
818 * {@inheritDoc}
819 */
Mindy Pereira555140c2012-02-15 14:55:29 -0800820 @Override
Vikram Aggarwal7dedb952012-02-16 16:10:23 -0800821 public Loader<Cursor> onCreateLoader(int id, Bundle args) {
822 // Create a loader to listen in on account changes.
Vikram Aggarwalfa4b47e2012-03-09 13:02:46 -0800823 switch (id) {
824 case LOADER_ACCOUNT_CURSOR:
Paul Westbrookc2074c42012-03-22 15:26:58 -0700825 return new CursorLoader(mContext, MailAppProvider.getAccountsUri(),
Vikram Aggarwalfa4b47e2012-03-09 13:02:46 -0800826 UIProvider.ACCOUNTS_PROJECTION, null, null, null);
827 case LOADER_FOLDER_CURSOR:
Mindy Pereiraab486362012-03-21 18:18:53 -0700828 return new CursorLoader(mContext, mFolder.uri, UIProvider.FOLDERS_PROJECTION, null,
829 null, null);
Vikram Aggarwalfa4b47e2012-03-09 13:02:46 -0800830 case LOADER_ACCOUNT_SETTINGS:
831 if (mAccount.settingsQueryUri != null) {
832 return new CursorLoader(mContext, mAccount.settingsQueryUri,
833 UIProvider.SETTINGS_PROJECTION, null, null, null);
834 }
Paul Westbrookea4ee202012-03-12 14:12:33 -0700835 break;
Vikram Aggarwalfa4b47e2012-03-09 13:02:46 -0800836 case LOADER_RECENT_FOLDERS:
Paul Westbrookea4ee202012-03-12 14:12:33 -0700837 if (mAccount.recentFolderListUri != null) {
838 return new CursorLoader(mContext, mAccount.recentFolderListUri,
839 UIProvider.FOLDERS_PROJECTION, null, null, null);
840 }
841 break;
Mindy Pereiraab486362012-03-21 18:18:53 -0700842 case LOADER_ACCOUNT_INBOX:
843 Settings settings = getSettings();
844 Uri inboxUri;
845 if (settings != null) {
846 inboxUri = settings.defaultInbox;
847 } else {
848 inboxUri = mAccount.folderListUri;
849 }
850 return new CursorLoader(mContext, inboxUri, UIProvider.FOLDERS_PROJECTION, null,
851 null, null);
852 case LOADER_SEARCH:
853 return Folder.forSearchResults(mAccount,
854 args.getString(ConversationListContext.EXTRA_SEARCH_QUERY),
855 mActivity.getActivityContext());
Vikram Aggarwalfa4b47e2012-03-09 13:02:46 -0800856 default:
857 LogUtils.wtf(LOG_TAG, "Loader returned unexpected id: " + id);
Mindy Pereira5cb0c3e2012-02-22 15:22:47 -0800858 }
859 return null;
Vikram Aggarwal7dedb952012-02-16 16:10:23 -0800860 }
861
Andy Huangf9a73482012-03-13 15:54:02 -0700862 /**
863 * {@link LoaderManager} currently has a bug in
864 * {@link LoaderManager#restartLoader(int, Bundle, android.app.LoaderManager.LoaderCallbacks)}
865 * where, if a previous onCreateLoader returned a null loader, this method will NPE. Work around
866 * this bug by destroying any loaders that may have been created as null (essentially because
867 * they are optional loads, and may not apply to a particular account).
868 * <p>
869 * A simple null check before restarting a loader will not work, because that would not
870 * give the controller a chance to invalidate UI corresponding the prior loader result.
871 *
872 * @param id loader ID to safely restart
873 * @param args arguments to pass to the restarted loader
874 */
875 private void restartOptionalLoader(int id, Bundle args) {
876 final LoaderManager lm = mActivity.getLoaderManager();
877 lm.destroyLoader(id);
878 lm.restartLoader(id, args, this);
879 }
880
Paul Westbrook23b74b92012-02-29 11:36:12 -0800881 private boolean accountsUpdated(Cursor accountCursor) {
882 // Check to see if the current account hasn't been set, or the account cursor is empty
883 if (mAccount == null || !accountCursor.moveToFirst()) {
Vikram Aggarwal6c511582012-02-27 10:59:47 -0800884 return true;
Paul Westbrook23b74b92012-02-29 11:36:12 -0800885 }
886
887 // Check to see if the number of accounts are different, from the number we saw on the last
888 // updated
889 if (mCurrentAccountUris.size() != accountCursor.getCount()) {
890 return true;
891 }
892
893 // Check to see if the account list is different or if the current account is not found in
894 // the cursor.
895 boolean foundCurrentAccount = false;
Vikram Aggarwal7dedb952012-02-16 16:10:23 -0800896 do {
Paul Westbrook23b74b92012-02-29 11:36:12 -0800897 final Uri accountUri =
898 Uri.parse(accountCursor.getString(UIProvider.ACCOUNT_URI_COLUMN));
899 if (!foundCurrentAccount && mAccount.uri.equals(accountUri)) {
900 foundCurrentAccount = true;
901 }
902
903 if (!mCurrentAccountUris.contains(accountUri)) {
904 return true;
905 }
Vikram Aggarwal7dedb952012-02-16 16:10:23 -0800906 } while (accountCursor.moveToNext());
Paul Westbrook23b74b92012-02-29 11:36:12 -0800907
908 // As long as we found the current account, the list hasn't been updated
909 return !foundCurrentAccount;
Vikram Aggarwal7dedb952012-02-16 16:10:23 -0800910 }
911
912 /**
Mindy Pereira161f50d2012-02-28 15:47:19 -0800913 * Update the accounts on the device. This currently loads the first account
914 * in the list.
915 *
Vikram Aggarwal7dedb952012-02-16 16:10:23 -0800916 * @param loader
Vikram Aggarwal6c511582012-02-27 10:59:47 -0800917 * @param accounts cursor into the AccountCache
Vikram Aggarwal7dedb952012-02-16 16:10:23 -0800918 * @return true if the update was successful, false otherwise
919 */
Paul Westbrook23b74b92012-02-29 11:36:12 -0800920 private boolean updateAccounts(Loader<Cursor> loader, Cursor accounts) {
Vikram Aggarwal7dedb952012-02-16 16:10:23 -0800921 if (accounts == null || !accounts.moveToFirst()) {
922 return false;
923 }
Paul Westbrook23b74b92012-02-29 11:36:12 -0800924
Vikram Aggarwala9b93f32012-02-23 14:51:58 -0800925 final Account[] allAccounts = Account.getAllAccounts(accounts);
Paul Westbrook23b74b92012-02-29 11:36:12 -0800926
927 // Save the uris for the accounts
928 mCurrentAccountUris.clear();
929 for (Account account : allAccounts) {
930 mCurrentAccountUris.add(account.uri);
931 }
932
Andy Huang0d647352012-03-21 21:48:16 -0700933 // 1. current account is already set and is in allAccounts -> no-op
934 // 2. current account is set and is not in allAccounts -> pick first (acct was deleted?)
935 // 3. saved pref has an account -> pick that one
936 // 4. otherwise just pick first
937
938 Account newAccount = null;
939
940 if (mAccount != null) {
941 if (!mCurrentAccountUris.contains(mAccount.uri)) {
942 newAccount = allAccounts[0];
943 } else {
944 newAccount = mAccount;
945 }
Paul Westbrook23b74b92012-02-29 11:36:12 -0800946 } else {
Paul Westbrookc2074c42012-03-22 15:26:58 -0700947 final String lastAccountUri = MailAppProvider.getInstance()
Andy Huang0d647352012-03-21 21:48:16 -0700948 .getLastViewedAccount();
949 if (lastAccountUri != null) {
950 for (int i = 0; i < allAccounts.length; i++) {
951 final Account acct = allAccounts[i];
952 if (lastAccountUri.equals(acct.uri.toString())) {
953 newAccount = acct;
954 break;
955 }
956 }
957 }
958 if (newAccount == null) {
959 newAccount = allAccounts[0];
960 }
Paul Westbrook23b74b92012-02-29 11:36:12 -0800961 }
Andy Huang0d647352012-03-21 21:48:16 -0700962
Paul Westbrook23b74b92012-02-29 11:36:12 -0800963 onAccountChanged(newAccount);
964
Vikram Aggarwala9b93f32012-02-23 14:51:58 -0800965 mActionBarView.setAccounts(allAccounts);
966 return (allAccounts.length > 0);
Vikram Aggarwal7dedb952012-02-16 16:10:23 -0800967 }
968
Paul Westbrook6ead20d2012-03-19 14:48:14 -0700969 private void disableNotifications() {
970 mNewEmailReceiver.activate(mContext, this);
971 }
972
973 private void enableNotifications() {
974 mNewEmailReceiver.deactivate();
975 }
976
977 private void disableNotificationsOnAccountChange(Account account) {
978 // If the new mail suppression receiver is activated for a different account, we want to
979 // activate it for the new account.
980 if (mNewEmailReceiver.activated() &&
981 !mNewEmailReceiver.notificationsDisabledForAccount(account)) {
982 // Deactivate the current receiver, otherwise multiple receivers may be registered.
983 mNewEmailReceiver.deactivate();
984 mNewEmailReceiver.activate(mContext, this);
985 }
986 }
987
Vikram Aggarwala9b93f32012-02-23 14:51:58 -0800988 /**
989 * {@inheritDoc}
990 */
Vikram Aggarwal7dedb952012-02-16 16:10:23 -0800991 @Override
992 public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
Mindy Pereira5cb0c3e2012-02-22 15:22:47 -0800993 // We want to reinitialize only if we haven't ever been initialized, or
994 // if the current account has vanished.
Paul Westbrooke3e84292012-03-05 16:19:30 -0800995 if (data == null) {
Vikram Aggarwalfa4b47e2012-03-09 13:02:46 -0800996 LogUtils.e(LOG_TAG, "Received null cursor from loader id: %d", loader.getId());
Paul Westbrooke3e84292012-03-05 16:19:30 -0800997 }
Vikram Aggarwalfa4b47e2012-03-09 13:02:46 -0800998 switch (loader.getId()) {
999 case LOADER_ACCOUNT_CURSOR:
1000 final boolean accountListUpdated = accountsUpdated(data);
1001 if (!isLoaderInitialized || accountListUpdated) {
1002 isLoaderInitialized = updateAccounts(loader, data);
1003 }
1004 break;
1005 case LOADER_FOLDER_CURSOR:
Vikram Aggarwalfa4b47e2012-03-09 13:02:46 -08001006 // Check status of the cursor.
Mindy Pereira5cb0c3e2012-02-22 15:22:47 -08001007 data.moveToFirst();
1008 Folder folder = new Folder(data);
Mindy Pereira11dd5ef2012-03-10 15:10:18 -08001009 if (folder.isSyncInProgress()) {
1010 mActionBarView.onRefreshStarted();
Paul Westbrookc808fac2012-02-22 16:42:18 -08001011 } else {
Mindy Pereira11dd5ef2012-03-10 15:10:18 -08001012 // Stop the spinner here.
1013 mActionBarView.onRefreshStopped(folder.lastSyncResult);
1014 }
1015 if (mConversationListFragment != null) {
1016 mConversationListFragment.onFolderUpdated(folder);
Mindy Pereira5cb0c3e2012-02-22 15:22:47 -08001017 }
1018 LogUtils.v(LOG_TAG, "FOLDER STATUS = " + folder.syncStatus);
Vikram Aggarwalfa4b47e2012-03-09 13:02:46 -08001019 break;
1020 case LOADER_ACCOUNT_SETTINGS:
Mindy Pereirab4a43282012-03-23 16:20:03 -07001021 // An account may actually have no settings if it is one of the
1022 // special combined accounts.
1023 Settings settings = null;
1024 if (data.moveToFirst()) {
1025 settings = new Settings(data);
1026 }
1027 onSettingsChanged(settings);
Vikram Aggarwalfa4b47e2012-03-09 13:02:46 -08001028 break;
1029 case LOADER_RECENT_FOLDERS:
Vikram Aggarwalfa4b47e2012-03-09 13:02:46 -08001030 mRecentFolderList.loadFromUiProvider(data);
1031 break;
Mindy Pereiraab486362012-03-21 18:18:53 -07001032 case LOADER_ACCOUNT_INBOX:
Mindy Pereirab4a43282012-03-23 16:20:03 -07001033 if (data.moveToFirst()) {
1034 // Just want to get the inbox, don't care about updates to it
1035 // as this will be tracked by the folder change listener.
1036 mActivity.getLoaderManager().destroyLoader(LOADER_ACCOUNT_INBOX);
1037 Folder inbox = new Folder(data);
1038 onFolderChanged(inbox);
1039 }
Mindy Pereiraab486362012-03-21 18:18:53 -07001040 break;
1041 case LOADER_SEARCH:
1042 data.moveToFirst();
1043 Folder search = new Folder(data);
1044 setFolder(search);
1045 mConvListContext = ConversationListContext.forSearchQuery(mAccount, mFolder,
1046 search.uri.getQueryParameter(UIProvider.SearchQueryParameters.QUERY));
1047 showConversationList(mConvListContext);
1048 mActivity.invalidateOptionsMenu();
1049 break;
Vikram Aggarwal7dedb952012-02-16 16:10:23 -08001050 }
1051 }
1052
Vikram Aggarwala9b93f32012-02-23 14:51:58 -08001053 /**
1054 * {@inheritDoc}
1055 */
Vikram Aggarwal7dedb952012-02-16 16:10:23 -08001056 @Override
1057 public void onLoaderReset(Loader<Cursor> loader) {
Andy Huangf9a73482012-03-13 15:54:02 -07001058 switch (loader.getId()) {
1059 case LOADER_ACCOUNT_SETTINGS:
1060 onSettingsChanged(null);
1061 break;
1062 }
Vikram Aggarwal7dedb952012-02-16 16:10:23 -08001063 }
Mindy Pereira674afa42012-02-17 14:05:24 -08001064
Mindy Pereira674afa42012-02-17 14:05:24 -08001065 @Override
1066 public void onTouchEvent(MotionEvent event) {
1067 if (event.getAction() == MotionEvent.ACTION_DOWN) {
1068 int mode = mViewMode.getMode();
1069 if (mode == ViewMode.CONVERSATION_LIST) {
1070 mConversationListFragment.onTouchEvent(event);
1071 } else if (mode == ViewMode.CONVERSATION) {
1072 mConversationViewFragment.onTouchEvent(event);
1073 }
1074 }
1075 }
Mindy Pereira5cb0c3e2012-02-22 15:22:47 -08001076
Mindy Pereirafbe40192012-03-20 10:40:45 -07001077 protected abstract class DestructiveActionListener implements ActionCompleteListener {
1078 protected final int mAction;
1079
1080 /**
1081 * Create a listener object. action is one of four constants: R.id.y_button (archive),
1082 * R.id.delete , R.id.mute, and R.id.report_spam.
1083 * @param action
1084 */
1085 public DestructiveActionListener(int action) {
1086 mAction = action;
1087 }
1088
1089 public void performConversationAction(Collection<Conversation> single) {
1090 switch (mAction) {
Mindy Pereira0692baf2012-03-23 17:34:31 -07001091 case R.id.archive:
Mindy Pereirafbe40192012-03-20 10:40:45 -07001092 LogUtils.d(LOG_TAG, "Archiving conversation " + mCurrentConversation);
1093 Conversation.archive(mContext, single);
1094 break;
1095 case R.id.delete:
1096 LogUtils.d(LOG_TAG, "Deleting conversation " + mCurrentConversation);
1097 Conversation.delete(mContext, single);
1098 break;
1099 case R.id.mute:
1100 LogUtils.d(LOG_TAG, "Muting conversation " + mCurrentConversation);
1101 if (mFolder.supportsCapability(FolderCapabilities.DESTRUCTIVE_MUTE))
1102 mCurrentConversation.localDeleteOnUpdate = true;
1103 Conversation.mute(mContext, single);
1104 break;
1105 case R.id.report_spam:
1106 LogUtils.d(LOG_TAG, "reporting spam conversation " + mCurrentConversation);
1107 Conversation.reportSpam(mContext, single);
1108 break;
1109 }
1110 }
1111
1112 public Conversation getNextConversation() {
1113 Conversation next = null;
1114 int pref = getAutoAdvanceSetting(mActivity);
Mindy Pereira967ede62012-03-22 09:29:09 -07001115 Cursor c = mConversationListCursor;
Mindy Pereirafbe40192012-03-20 10:40:45 -07001116 if (c != null) {
1117 c.moveToPosition(mCurrentConversation.position);
1118 }
1119 switch (pref) {
1120 case AutoAdvance.NEWER:
1121 if (c.moveToPrevious()) {
1122 next = new Conversation(c);
1123 }
1124 break;
1125 case AutoAdvance.OLDER:
1126 if (c.moveToNext()) {
1127 next = new Conversation(c);
1128 }
1129 break;
1130 }
1131 return next;
1132 }
1133
1134 @Override
1135 public abstract void onActionComplete();
1136 }
Mindy Pereirae5f4dc02012-03-21 16:08:53 -07001137
1138 // Called from the FolderSelectionDialog after a user is done changing
1139 // folders.
1140 @Override
Mindy Pereira6778f462012-03-23 18:01:55 -07001141 public void onFolderChangesCommit(ArrayList<Folder> folderChangeList) {
Mindy Pereirae5f4dc02012-03-21 16:08:53 -07001142 // Get currently active folder info and compare it to the list
1143 // these conversations have been given; if they no longer contain
1144 // the selected folder, delete them from the list.
1145 HashSet<String> folderUris = new HashSet<String>();
Mindy Pereira6778f462012-03-23 18:01:55 -07001146 if (folderChangeList != null && !folderChangeList.isEmpty()) {
1147 for (Folder f : folderChangeList) {
1148 folderUris.add(f.uri.toString());
1149 }
Mindy Pereirae5f4dc02012-03-21 16:08:53 -07001150 }
Mindy Pereirac9d59182012-03-22 16:06:46 -07001151 final boolean destructiveChange = !folderUris.contains(mFolder.uri.toString());
Mindy Pereirae5f4dc02012-03-21 16:08:53 -07001152 DestructiveActionListener listener = getFolderDestructiveActionListener();
Mindy Pereira6778f462012-03-23 18:01:55 -07001153 StringBuilder foldersUrisString = new StringBuilder();
1154 boolean first = true;
1155 for (Folder f : folderChangeList) {
1156 if (first) {
1157 first = false;
1158 } else {
1159 foldersUrisString.append(',');
1160 }
1161 foldersUrisString.append(f.uri.toString());
1162 }
1163 updateCurrentConversation(ConversationColumns.FOLDER_LIST, foldersUrisString.toString());
1164 updateCurrentConversation(ConversationColumns.RAW_FOLDERS,
1165 Folder.getSerializedFolderString(folderChangeList));
1166 // TODO: (mindyp): set ConversationColumns.RAW_FOLDERS like in
1167 // SelectedConversationsActionMenu
Mindy Pereirae5f4dc02012-03-21 16:08:53 -07001168 if (destructiveChange) {
1169 mCurrentConversation.localDeleteOnUpdate = true;
Mindy Pereirac9d59182012-03-22 16:06:46 -07001170 requestDelete(listener);
1171 } else if (mConversationListFragment != null) {
1172 mConversationListFragment.requestListRefresh();
Mindy Pereirae5f4dc02012-03-21 16:08:53 -07001173 }
1174 }
1175
1176 protected abstract DestructiveActionListener getFolderDestructiveActionListener();
Mindy Pereira967ede62012-03-22 09:29:09 -07001177
1178 @Override
1179 public void onRefreshRequired() {
1180 // Refresh the query in the background
1181 getConversationListCursor().refresh();
1182 }
1183
1184 @Override
1185 public void onRefreshReady() {
1186 ArrayList<Integer> deletedRows = mConversationListCursor.getRefreshDeletions();
1187 // If we have any deletions from the server, animate them away
1188 if (!deletedRows.isEmpty() && mConversationListFragment != null) {
1189 AnimatedAdapter adapter = mConversationListFragment.getAnimatedAdapter();
1190 if (adapter != null) {
1191 mConversationListFragment.getAnimatedAdapter().delete(deletedRows,
1192 this);
1193 }
1194 } else {
1195 // Swap cursors
1196 getConversationListCursor().sync();
Marc Blankbec51152012-03-22 19:27:34 -07001197 refreshAdapter();
Mindy Pereira967ede62012-03-22 09:29:09 -07001198 }
1199 }
1200
Marc Blankbec51152012-03-22 19:27:34 -07001201 @Override
1202 public void onDataSetChanged() {
1203 refreshAdapter();
1204 }
1205
1206 private void refreshAdapter() {
Mindy Pereira967ede62012-03-22 09:29:09 -07001207 if (mConversationListFragment != null) {
1208 AnimatedAdapter adapter = mConversationListFragment.getAnimatedAdapter();
1209 if (adapter != null) {
1210 adapter.notifyDataSetChanged();
1211 }
1212 }
1213 }
1214
1215 @Override
1216 public void onSetEmpty() {
1217 mSelectedConversationsActionMenu = null;
1218 }
1219
1220 @Override
1221 public void onSetPopulated(ConversationSelectionSet set) {
1222 mSelectedConversationsActionMenu = new SelectedConversationsActionMenu(mActivity,
1223 mSelectedSet, mConversationListFragment.getAnimatedAdapter(), this,
1224 mConversationListFragment, mAccount, mFolder);
1225 mSelectedConversationsActionMenu.activate();
1226 }
1227
1228
1229 @Override
1230 public void onSetChanged(ConversationSelectionSet set) {
1231 // Do nothing. We don't care about changes to the set.
1232 }
1233
1234 @Override
1235 public ConversationSelectionSet getSelectedSet() {
1236 return mSelectedSet;
1237 }
1238
1239 @Override
1240 public void onActionComplete() {
1241 if (getConversationListCursor().isRefreshReady()) {
Marc Blankbec51152012-03-22 19:27:34 -07001242 refreshAdapter();
Mindy Pereira967ede62012-03-22 09:29:09 -07001243 }
1244 }
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -08001245}