blob: b07324cffd72f8a20095c46153a474987bb12fd1 [file] [log] [blame]
Mindy Pereira68f2e222012-03-07 10:36:54 -08001/*
2 * Copyright (C) 2012 Google Inc.
3 * Licensed to The Android Open Source Project.
Vikram Aggarwal5e5ac742011-12-19 08:14:16 -08004 *
Mindy Pereira68f2e222012-03-07 10:36:54 -08005 * 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
Vikram Aggarwal5e5ac742011-12-19 08:14:16 -08008 *
Mindy Pereira68f2e222012-03-07 10:36:54 -08009 * http://www.apache.org/licenses/LICENSE-2.0
Vikram Aggarwal5e5ac742011-12-19 08:14:16 -080010 *
Mindy Pereira68f2e222012-03-07 10:36:54 -080011 * 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 */
Vikram Aggarwal5e5ac742011-12-19 08:14:16 -080017
Vikram Aggarwal1ddcf0f2012-01-13 11:45:02 -080018package com.android.mail.ui;
Vikram Aggarwal5e5ac742011-12-19 08:14:16 -080019
20import android.app.ActionBar;
Mindy Pereira68f2e222012-03-07 10:36:54 -080021import android.app.SearchManager;
22import android.app.SearchableInfo;
Scott Banachowski53a615d2012-12-19 15:31:20 -080023import android.content.ContentResolver;
Mindy Pereira68f2e222012-03-07 10:36:54 -080024import android.content.Context;
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -070025import android.content.res.Resources;
Vikram Aggarwal12a7c042012-03-28 16:48:08 -070026import android.database.Cursor;
Vikram Aggarwala51ef9f2013-04-02 13:25:35 -070027import android.net.Uri;
28import android.os.AsyncTask;
Paul Westbrook8c887ef2013-04-24 00:29:28 -070029import android.os.Build;
Vikram Aggarwal5e5ac742011-12-19 08:14:16 -080030import android.os.Bundle;
Mindy Pereira9b623802012-03-07 17:15:49 -080031import android.os.Handler;
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -070032import android.os.Message;
Mindy Pereira0531c9f2012-06-25 14:12:59 -070033import android.text.TextUtils;
Mindy Pereira68f2e222012-03-07 10:36:54 -080034import android.util.AttributeSet;
Vikram Aggarwal5e5ac742011-12-19 08:14:16 -080035import android.view.Menu;
Mindy Pereira68f2e222012-03-07 10:36:54 -080036import android.view.MenuItem;
37import android.view.View;
Scott Kennedyb905d082013-05-07 16:44:34 -070038import android.view.ViewConfiguration;
Mindy Pereira68f2e222012-03-07 10:36:54 -080039import android.widget.LinearLayout;
40import android.widget.SearchView;
41import android.widget.SearchView.OnQueryTextListener;
Vikram Aggarwal12a7c042012-03-28 16:48:08 -070042import android.widget.SearchView.OnSuggestionListener;
Paul Westbrook8c887ef2013-04-24 00:29:28 -070043import android.widget.TextView;
Vikram Aggarwala9b93f32012-02-23 14:51:58 -080044
Alice Yang760d9562013-04-02 13:13:24 -070045import com.android.mail.ConversationListContext;
46import com.android.mail.R;
Scott Kennedya85831d2013-04-12 15:54:22 -070047import com.android.mail.preferences.MailPrefs;
Alice Yang760d9562013-04-02 13:13:24 -070048import com.android.mail.providers.Account;
49import com.android.mail.providers.AccountObserver;
Alice Yang760d9562013-04-02 13:13:24 -070050import com.android.mail.providers.Conversation;
51import com.android.mail.providers.Folder;
52import com.android.mail.providers.FolderObserver;
53import com.android.mail.providers.SearchRecentSuggestionsProvider;
54import com.android.mail.providers.UIProvider;
55import com.android.mail.providers.UIProvider.AccountCapabilities;
56import com.android.mail.providers.UIProvider.FolderCapabilities;
Scott Kennedy7ee089e2013-03-25 17:05:44 -040057import com.android.mail.providers.UIProvider.FolderType;
Alice Yang760d9562013-04-02 13:13:24 -070058import com.android.mail.utils.LogTag;
59import com.android.mail.utils.LogUtils;
60import com.android.mail.utils.Utils;
61
Vikram Aggarwal5e5ac742011-12-19 08:14:16 -080062/**
Andy Huang6681e542012-06-14 14:36:45 -070063 * View to manage the various states of the Mail Action Bar.
64 * <p>
65 * This also happens to be the custom view we supply to ActionBar.
Mindy Pereira68f2e222012-03-07 10:36:54 -080066 *
Vikram Aggarwal5e5ac742011-12-19 08:14:16 -080067 */
mindyp6fb1b622012-09-05 09:27:08 -070068public class MailActionBarView extends LinearLayout implements ViewMode.ModeChangeListener,
Paul Westbrook8c887ef2013-04-24 00:29:28 -070069 OnQueryTextListener, OnSuggestionListener, MenuItem.OnActionExpandListener,
70 View.OnClickListener {
Andy Huangaf65e732013-01-28 20:45:40 -080071
Mindy Pereiraedb135e2012-06-15 08:34:09 -070072 protected ActionBar mActionBar;
Vikram Aggarwal7c401b72012-08-13 16:43:47 -070073 protected ControllableActivity mActivity;
Mindy Pereiraedb135e2012-06-15 08:34:09 -070074 protected ActivityController mController;
Mindy Pereira68f2e222012-03-07 10:36:54 -080075 /**
Tony Mantlerbd091502013-09-16 13:59:47 -070076 * The current mode of the ActionBar and Activity
Mindy Pereira68f2e222012-03-07 10:36:54 -080077 */
Tony Mantlerbd091502013-09-16 13:59:47 -070078 private ViewMode mViewModeController;
Mindy Pereira68f2e222012-03-07 10:36:54 -080079
80 private MenuItem mSearch;
Mindy Pereira68f2e222012-03-07 10:36:54 -080081 /**
82 * The account currently being shown
83 */
84 private Account mAccount;
Marc Blankedcab012012-04-04 12:42:38 -070085 /**
86 * The folder currently being shown
87 */
88 private Folder mFolder;
Mindy Pereira68f2e222012-03-07 10:36:54 -080089
Mindy Pereira68f2e222012-03-07 10:36:54 -080090 private SearchView mSearchWidget;
91 private MenuItem mHelpItem;
Paul Westbrook517743e2012-03-22 10:40:46 -070092 private MenuItem mSendFeedbackItem;
Marc Blankedcab012012-04-04 12:42:38 -070093 private MenuItem mFolderSettingsItem;
Scott Kennedy7ee089e2013-03-25 17:05:44 -040094 private MenuItem mEmptyTrashItem;
95 private MenuItem mEmptySpamItem;
Paul Westbrook8c887ef2013-04-24 00:29:28 -070096 private boolean mUseLegacyTitle;
97 private View mLegacyTitleContainer;
98 private TextView mLegacyTitle;
99 private TextView mLegacySubTitle;
100
Vikram Aggarwalf642b812013-03-15 16:30:21 -0700101 /** True if the current device is a tablet, false otherwise. */
Vikram Aggarwalad8b99b2013-04-07 16:37:28 -0700102 protected final boolean mIsOnTablet;
Yorke Leef807ba72012-09-20 17:18:05 -0700103 private Conversation mCurrentConversation;
Mindy Pereira9b623802012-03-07 17:15:49 -0800104
Paul Westbrookb334c902012-06-25 11:42:46 -0700105 public static final String LOG_TAG = LogTag.getLogTag();
Andy Huang0d647352012-03-21 21:48:16 -0700106
Vikram Aggarwal50ff0e52013-03-14 13:58:02 -0700107 private FolderObserver mFolderObserver;
Mindy Pereira68f2e222012-03-07 10:36:54 -0800108
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700109 /** A handler that changes the subtitle when it receives a message. */
110 private final class SubtitleHandler extends Handler {
111 /** Message sent to display the account email address in the subtitle. */
112 private static final int EMAIL = 0;
113
114 @Override
115 public void handleMessage(Message message) {
116 assert (message.what == EMAIL);
117 final String subtitleText;
Vikram Aggarwalb335fb32013-04-22 17:16:15 -0700118 if (mAccount != null) {
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700119 // Display the account name (email address).
120 subtitleText = mAccount.name;
121 } else {
Vikram Aggarwalb335fb32013-04-22 17:16:15 -0700122 subtitleText = null;
123 LogUtils.wtf(LOG_TAG, "MABV.handleMessage() has a null account!");
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700124 }
Paul Westbrook8c887ef2013-04-24 00:29:28 -0700125 setSubtitle(subtitleText);
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700126 super.handleMessage(message);
127 }
128 }
129
130 /** Changes the subtitle to display the account name */
131 private final SubtitleHandler mHandler = new SubtitleHandler();
132 /** Unread count for the current folder. */
133 private int mUnreadCount = 0;
134 /** We show the email address after this delay: 5 seconds currently */
135 private static final int ACCOUNT_DELAY_MS = 5 * 1000;
136 /** At what point do we stop showing the unread count: 999+ currently */
137 private final int UNREAD_LIMIT;
138
Vikram Aggarwala51ef9f2013-04-02 13:25:35 -0700139 /** Updates the resolver and tells it the most recent account. */
140 private final class UpdateProvider extends AsyncTask<Bundle, Void, Void> {
141 final Uri mAccount;
142 final ContentResolver mResolver;
143 public UpdateProvider(Uri account, ContentResolver resolver) {
144 mAccount = account;
145 mResolver = resolver;
146 }
147
148 @Override
149 protected Void doInBackground(Bundle... params) {
150 mResolver.call(mAccount, UIProvider.AccountCallMethods.SET_CURRENT_ACCOUNT,
151 mAccount.toString(), params[0]);
152 return null;
153 }
154 }
155
Vikram Aggarwal7c401b72012-08-13 16:43:47 -0700156 private final AccountObserver mAccountObserver = new AccountObserver() {
157 @Override
158 public void onChanged(Account newAccount) {
Scott Banachowski53a615d2012-12-19 15:31:20 -0800159 updateAccount(newAccount);
Vikram Aggarwal7c401b72012-08-13 16:43:47 -0700160 }
161 };
Andy Huangaf65e732013-01-28 20:45:40 -0800162
Andy Huang6681e542012-06-14 14:36:45 -0700163 public MailActionBarView(Context context) {
Mindy Pereira68f2e222012-03-07 10:36:54 -0800164 this(context, null);
165 }
166
Andy Huang6681e542012-06-14 14:36:45 -0700167 public MailActionBarView(Context context, AttributeSet attrs) {
Mindy Pereira68f2e222012-03-07 10:36:54 -0800168 this(context, attrs, 0);
169 }
170
Andy Huang6681e542012-06-14 14:36:45 -0700171 public MailActionBarView(Context context, AttributeSet attrs, int defStyle) {
Mindy Pereira68f2e222012-03-07 10:36:54 -0800172 super(context, attrs, defStyle);
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700173 final Resources r = getResources();
174 mIsOnTablet = Utils.useTabletUI(r);
175 UNREAD_LIMIT = r.getInteger(R.integer.maxUnreadCount);
Andy Huang5895f7b2012-06-01 17:07:20 -0700176 }
177
Paul Westbrook8c887ef2013-04-24 00:29:28 -0700178 private void initializeTitleViews() {
179 mLegacyTitleContainer = findViewById(R.id.legacy_title_container);
180 if (mLegacyTitleContainer != null) {
181 // Determine if this device is running on MR1.1 or later
182 final boolean runningMR11OrLater = actionBarSupportsNewMethods(mActionBar);
183 if (runningMR11OrLater || !mController.isDrawerEnabled()) {
184 // We don't need the legacy view, just hide it
185 mLegacyTitleContainer.setVisibility(View.GONE);
186 mUseLegacyTitle = false;
187 } else {
188 mUseLegacyTitle = true;
189 // We need to show the legacy title/subtitle. Set the click listener
190 mLegacyTitleContainer.setOnClickListener(this);
191
192 mLegacyTitle = (TextView)mLegacyTitleContainer.findViewById(R.id.legacy_title);
193 mLegacySubTitle =
194 (TextView)mLegacyTitleContainer.findViewById(R.id.legacy_subtitle);
195 }
196 }
Mindy Pereira68f2e222012-03-07 10:36:54 -0800197 }
198
Andy Huang313ac132013-03-04 23:40:58 -0800199 public void expandSearch() {
200 if (mSearch != null) {
201 mSearch.expandActionView();
202 }
203 }
204
Vikram Aggarwalb17cbc02012-04-06 15:41:46 -0700205 /**
Vikram Aggarwal0dda5732012-04-06 11:20:16 -0700206 * Close the search view if it is expanded.
Vikram Aggarwalb17cbc02012-04-06 15:41:46 -0700207 */
208 public void collapseSearch() {
209 if (mSearch != null) {
210 mSearch.collapseActionView();
211 }
212 }
213
Mindy Pereiraedb135e2012-06-15 08:34:09 -0700214 /**
215 * Get the search menu item.
216 */
217 protected MenuItem getSearch() {
218 return mSearch;
219 }
220
Mindy Pereira68f2e222012-03-07 10:36:54 -0800221 public boolean onCreateOptionsMenu(Menu menu) {
222 // If the mode is valid, then set the initial menu
Tony Mantlerbd091502013-09-16 13:59:47 -0700223 if (getMode() == ViewMode.UNKNOWN) {
Mindy Pereira68f2e222012-03-07 10:36:54 -0800224 return false;
225 }
Mindy Pereira9b623802012-03-07 17:15:49 -0800226 mSearch = menu.findItem(R.id.search);
227 if (mSearch != null) {
228 mSearchWidget = (SearchView) mSearch.getActionView();
Vikram Aggarwal0dda5732012-04-06 11:20:16 -0700229 mSearch.setOnActionExpandListener(this);
Mindy Pereira68f2e222012-03-07 10:36:54 -0800230 SearchManager searchManager = (SearchManager) mActivity.getActivityContext()
231 .getSystemService(Context.SEARCH_SERVICE);
Mindy Pereirafb178492012-03-07 18:56:42 -0800232 if (searchManager != null && mSearchWidget != null) {
Mindy Pereira68f2e222012-03-07 10:36:54 -0800233 SearchableInfo info = searchManager.getSearchableInfo(mActivity.getComponentName());
234 mSearchWidget.setSearchableInfo(info);
235 mSearchWidget.setOnQueryTextListener(this);
Vikram Aggarwal12a7c042012-03-28 16:48:08 -0700236 mSearchWidget.setOnSuggestionListener(this);
Vikram Aggarwal0dda5732012-04-06 11:20:16 -0700237 mSearchWidget.setIconifiedByDefault(true);
Mindy Pereira68f2e222012-03-07 10:36:54 -0800238 }
239 }
240 mHelpItem = menu.findItem(R.id.help_info_menu_item);
Paul Westbrook517743e2012-03-22 10:40:46 -0700241 mSendFeedbackItem = menu.findItem(R.id.feedback_menu_item);
Marc Blankedcab012012-04-04 12:42:38 -0700242 mFolderSettingsItem = menu.findItem(R.id.folder_options);
Scott Kennedy7ee089e2013-03-25 17:05:44 -0400243 mEmptyTrashItem = menu.findItem(R.id.empty_trash);
244 mEmptySpamItem = menu.findItem(R.id.empty_spam);
Mindy Pereira68f2e222012-03-07 10:36:54 -0800245 return true;
246 }
247
248 public int getOptionsMenuId() {
Tony Mantlerbd091502013-09-16 13:59:47 -0700249 switch (getMode()) {
Tony Mantlere29b97e2013-08-01 16:37:54 -0700250 case ViewMode.UNKNOWN:
251 return R.menu.conversation_list_menu;
252 case ViewMode.CONVERSATION:
253 return R.menu.conversation_actions;
254 case ViewMode.CONVERSATION_LIST:
255 return R.menu.conversation_list_menu;
256 case ViewMode.SEARCH_RESULTS_LIST:
257 return R.menu.conversation_list_search_results_actions;
258 case ViewMode.SEARCH_RESULTS_CONVERSATION:
259 return R.menu.conversation_actions;
260 case ViewMode.WAITING_FOR_ACCOUNT_INITIALIZATION:
261 return R.menu.wait_mode_actions;
262 }
263 LogUtils.wtf(LOG_TAG, "Menu requested for unknown view mode");
264 return R.menu.conversation_list_menu;
Mindy Pereira68f2e222012-03-07 10:36:54 -0800265 }
266
Vikram Aggarwal7c401b72012-08-13 16:43:47 -0700267 public void initialize(ControllableActivity activity, ActivityController callback,
Vikram Aggarwaldac89fa2013-03-05 16:43:09 -0800268 ActionBar actionBar) {
Mindy Pereira68f2e222012-03-07 10:36:54 -0800269 mActionBar = actionBar;
Vikram Aggarwala7997952012-03-09 10:30:13 -0800270 mController = callback;
Mindy Pereira68f2e222012-03-07 10:36:54 -0800271 mActivity = activity;
Paul Westbrook8c887ef2013-04-24 00:29:28 -0700272 initializeTitleViews();
273
Vikram Aggarwal50ff0e52013-03-14 13:58:02 -0700274 mFolderObserver = new FolderObserver() {
275 @Override
276 public void onChanged(Folder newFolder) {
277 onFolderUpdated(newFolder);
278 }
279 };
Vikram Aggarwal26b0bfd2013-03-29 13:05:08 -0700280 // Return values are purposely discarded. Initialization happens quite early, and we don't
281 // have a valid folder, or a valid list of accounts.
Vikram Aggarwal50ff0e52013-03-14 13:58:02 -0700282 mFolderObserver.initialize(mController);
Scott Banachowski53a615d2012-12-19 15:31:20 -0800283 updateAccount(mAccountObserver.initialize(activity.getAccountController()));
284 }
285
286 private void updateAccount(Account account) {
Vikram Aggarwalf18b1172013-04-17 12:36:23 -0700287 final boolean accountChanged = mAccount == null || !mAccount.uri.equals(account.uri);
Scott Banachowski53a615d2012-12-19 15:31:20 -0800288 mAccount = account;
Vikram Aggarwalf18b1172013-04-17 12:36:23 -0700289 if (mAccount != null && accountChanged) {
Vikram Aggarwalbc462ca2013-03-15 10:41:03 -0700290 final ContentResolver resolver = mActivity.getActivityContext().getContentResolver();
291 final Bundle bundle = new Bundle(1);
Scott Banachowski53a615d2012-12-19 15:31:20 -0800292 bundle.putParcelable(UIProvider.SetCurrentAccountColumns.ACCOUNT, account);
Vikram Aggarwala51ef9f2013-04-02 13:25:35 -0700293 final UpdateProvider updater = new UpdateProvider(mAccount.uri, resolver);
294 updater.execute(bundle);
Vikram Aggarwalf18b1172013-04-17 12:36:23 -0700295 setFolderAndAccount(false /* folderChanged */);
Scott Banachowski53a615d2012-12-19 15:31:20 -0800296 }
Vikram Aggarwalabd24d82012-04-26 13:23:14 -0700297 }
Andy Huange303b9b2012-04-19 16:31:09 -0700298
Vikram Aggarwalabd24d82012-04-26 13:23:14 -0700299 /**
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700300 * Called by the owner of the ActionBar to change the current folder.
Vikram Aggarwal5e5ac742011-12-19 08:14:16 -0800301 */
Mindy Pereira68f2e222012-03-07 10:36:54 -0800302 public void setFolder(Folder folder) {
Marc Blankedcab012012-04-04 12:42:38 -0700303 mFolder = folder;
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700304 setFolderAndAccount(true);
Mindy Pereira68f2e222012-03-07 10:36:54 -0800305 }
Vikram Aggarwal5e5ac742011-12-19 08:14:16 -0800306
Mindy Pereira641de652012-08-02 15:21:50 -0700307 public void onDestroy() {
308 if (mFolderObserver != null) {
Vikram Aggarwal50ff0e52013-03-14 13:58:02 -0700309 mFolderObserver.unregisterAndDestroy();
Mindy Pereira641de652012-08-02 15:21:50 -0700310 mFolderObserver = null;
311 }
Vikram Aggarwal7c401b72012-08-13 16:43:47 -0700312 mAccountObserver.unregisterAndDestroy();
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700313 mHandler.removeMessages(SubtitleHandler.EMAIL);
Mindy Pereira68f2e222012-03-07 10:36:54 -0800314 }
315
Vikram Aggarwala7997952012-03-09 10:30:13 -0800316 @Override
Mindy Pereira68f2e222012-03-07 10:36:54 -0800317 public void onViewModeChanged(int newMode) {
Mindy Pereira68f2e222012-03-07 10:36:54 -0800318 mActivity.invalidateOptionsMenu();
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700319 mHandler.removeMessages(SubtitleHandler.EMAIL);
Vikram Aggarwal1a3f3fc2012-07-18 14:49:08 -0700320 // Check if we are either on a phone, or in Conversation mode on tablet. For these, the
321 // recent folders is enabled.
Tony Mantlerbd091502013-09-16 13:59:47 -0700322 switch (getMode()) {
Andy Huangd736a382012-08-29 13:08:58 -0700323 case ViewMode.UNKNOWN:
Andy Huangd736a382012-08-29 13:08:58 -0700324 break;
325 case ViewMode.CONVERSATION_LIST:
326 showNavList();
327 break;
Vikram Aggarwalad8b99b2013-04-07 16:37:28 -0700328 case ViewMode.SEARCH_RESULTS_CONVERSATION:
Vikram Aggarwale875b662013-04-10 15:56:46 -0700329 mActionBar.setDisplayHomeAsUpEnabled(true);
330 setEmptyMode();
331 break;
Andy Huangd736a382012-08-29 13:08:58 -0700332 case ViewMode.CONVERSATION:
Andrew Sapperstein6c570db2013-08-06 17:21:36 -0700333 case ViewMode.AD:
Vikram Aggarwal49499e62013-02-08 16:39:08 -0800334 closeSearchField();
Andy Huangd736a382012-08-29 13:08:58 -0700335 mActionBar.setDisplayHomeAsUpEnabled(true);
Vikram Aggarwalad8b99b2013-04-07 16:37:28 -0700336 setEmptyMode();
Andy Huangd736a382012-08-29 13:08:58 -0700337 break;
Andy Huangd736a382012-08-29 13:08:58 -0700338 case ViewMode.WAITING_FOR_ACCOUNT_INITIALIZATION:
339 // We want the user to be able to switch accounts while waiting for an account
340 // to sync.
341 showNavList();
342 break;
343 }
Mindy Pereira68f2e222012-03-07 10:36:54 -0800344 }
Vikram Aggarwal5e5ac742011-12-19 08:14:16 -0800345
Vikram Aggarwal49499e62013-02-08 16:39:08 -0800346 /**
347 * Close the search query entry field to avoid keyboard events, and to restore the actionbar
348 * to non-search mode.
349 */
Vikram Aggarwaldac89fa2013-03-05 16:43:09 -0800350 private void closeSearchField() {
Vikram Aggarwal49499e62013-02-08 16:39:08 -0800351 if (mSearch == null) {
352 return;
353 }
354 mSearch.collapseActionView();
355 }
356
Mindy Pereiraedb135e2012-06-15 08:34:09 -0700357 protected int getMode() {
Tony Mantlerbd091502013-09-16 13:59:47 -0700358 if (mViewModeController != null) {
359 return mViewModeController.getMode();
360 } else {
361 return ViewMode.UNKNOWN;
362 }
Mindy Pereiraedb135e2012-06-15 08:34:09 -0700363 }
364
Mindy Pereirab849dfb2012-03-07 18:13:15 -0800365 public boolean onPrepareOptionsMenu(Menu menu) {
Mindy Pereira68f2e222012-03-07 10:36:54 -0800366 // We start out with every option enabled. Based on the current view, we disable actions
367 // that are possible.
Vikram Aggarwal95b28ab2012-04-26 15:53:09 -0700368 LogUtils.d(LOG_TAG, "ActionBarView.onPrepareOptionsMenu().");
Vikram Aggarwal5e5ac742011-12-19 08:14:16 -0800369
Mindy Pereira68f2e222012-03-07 10:36:54 -0800370 if (mHelpItem != null) {
371 mHelpItem.setVisible(mAccount != null
372 && mAccount.supportsCapability(AccountCapabilities.HELP_CONTENT));
373 }
Paul Westbrook517743e2012-03-22 10:40:46 -0700374 if (mSendFeedbackItem != null) {
375 mSendFeedbackItem.setVisible(mAccount != null
376 && mAccount.supportsCapability(AccountCapabilities.SEND_FEEDBACK));
377 }
Tony Mantlerb3281fb2013-08-20 14:30:29 -0700378 if (mController.shouldHideMenuItems()) {
379 // Shortcut: hide all remaining menu items if the drawer is shown
380 final int size = menu.size();
381
382 for (int i = 0; i < size; i++) {
383 final MenuItem item = menu.getItem(i);
384 final int id = item.getItemId();
385 if (id != R.id.settings
386 && id != R.id.feedback_menu_item
387 && id != R.id.help_info_menu_item) {
388 item.setVisible(false);
389 }
390 }
391 return false;
392 }
Marc Blankedcab012012-04-04 12:42:38 -0700393 if (mFolderSettingsItem != null) {
394 mFolderSettingsItem.setVisible(mFolder != null
395 && mFolder.supportsCapability(FolderCapabilities.SUPPORTS_SETTINGS));
396 }
Scott Kennedy7ee089e2013-03-25 17:05:44 -0400397 if (mEmptyTrashItem != null) {
398 mEmptyTrashItem.setVisible(mAccount != null && mFolder != null
399 && mAccount.supportsCapability(AccountCapabilities.EMPTY_TRASH)
Andrew Sappersteined5b52d2013-04-30 13:40:18 -0700400 && mFolder.isTrash() && mFolder.totalCount > 0);
Scott Kennedy7ee089e2013-03-25 17:05:44 -0400401 }
402 if (mEmptySpamItem != null) {
403 mEmptySpamItem.setVisible(mAccount != null && mFolder != null
404 && mAccount.supportsCapability(AccountCapabilities.EMPTY_SPAM)
Andrew Sappersteined5b52d2013-04-30 13:40:18 -0700405 && mFolder.isType(FolderType.SPAM) && mFolder.totalCount > 0);
Scott Kennedy7ee089e2013-03-25 17:05:44 -0400406 }
Mindy Pereira62c71792012-07-18 09:14:29 -0700407
Tony Mantlerbd091502013-09-16 13:59:47 -0700408 switch (getMode()) {
Yorke Leef807ba72012-09-20 17:18:05 -0700409 case ViewMode.CONVERSATION:
410 case ViewMode.SEARCH_RESULTS_CONVERSATION:
411 // We update the ActionBar options when we are entering conversation view because
412 // waiting for the AbstractConversationViewFragment to do it causes duplicate icons
413 // to show up during the time between the conversation is selected and the fragment
414 // is added.
415 setConversationModeOptions(menu);
Scott Kennedyb905d082013-05-07 16:44:34 -0700416 // We want to use the user's preferred menu items here
417 final Resources resources = getResources();
418 final int maxItems = resources.getInteger(R.integer.actionbar_max_items);
419 final int hiddenItems = resources.getInteger(
420 R.integer.actionbar_hidden_non_cab_items_no_physical_button);
421 final int totalItems = maxItems
422 - (ViewConfiguration.get(getContext()).hasPermanentMenuKey()
423 ? 0 : hiddenItems);
424 reorderMenu(getContext(), mAccount, menu, totalItems);
Yorke Leef807ba72012-09-20 17:18:05 -0700425 break;
Mindy Pereira68f2e222012-03-07 10:36:54 -0800426 case ViewMode.CONVERSATION_LIST:
Vikram Aggarwalad8b99b2013-04-07 16:37:28 -0700427 // Show compose and search based on the account
Mindy Pereira68f2e222012-03-07 10:36:54 -0800428 // The only option that needs to be disabled is search
Mindy Pereira863e4412012-03-23 12:21:38 -0700429 Utils.setMenuItemVisibility(menu, R.id.search,
Mindy Pereira9b623802012-03-07 17:15:49 -0800430 mAccount.supportsCapability(AccountCapabilities.FOLDER_SERVER_SEARCH));
Mindy Pereira68f2e222012-03-07 10:36:54 -0800431 break;
Vikram Aggarwalad8b99b2013-04-07 16:37:28 -0700432 case ViewMode.SEARCH_RESULTS_LIST:
Vikram Aggarwal4bfad142013-05-07 17:12:46 -0700433 // Hide compose and search
434 Utils.setMenuItemVisibility(menu, R.id.compose, false);
Vikram Aggarwalad8b99b2013-04-07 16:37:28 -0700435 Utils.setMenuItemVisibility(menu, R.id.search, false);
436 break;
Mindy Pereira68f2e222012-03-07 10:36:54 -0800437 }
Scott Kennedya85831d2013-04-12 15:54:22 -0700438
439 return false;
440 }
441
Scott Kennedy26f2b302013-04-22 17:47:17 -0700442 /**
443 * Reorders the specified {@link Menu}, taking into account the user's Archive/Delete
444 * preference.
445 */
Scott Kennedyb905d082013-05-07 16:44:34 -0700446 public static void reorderMenu(final Context context, final Account account, final Menu menu,
447 final int maxItems) {
Scott Kennedyd4796d72013-05-03 19:49:32 -0700448 final String removalAction = MailPrefs.get(context).getRemovalAction(
449 account.supportsCapability(AccountCapabilities.ARCHIVE));
Andrew Sapperstein43a1cc12013-05-03 10:21:02 -0700450 final boolean showArchive = MailPrefs.RemovalActions.ARCHIVE.equals(removalAction) ||
451 MailPrefs.RemovalActions.ARCHIVE_AND_DELETE.equals(removalAction);
452 final boolean showDelete = MailPrefs.RemovalActions.DELETE.equals(removalAction) ||
453 MailPrefs.RemovalActions.ARCHIVE_AND_DELETE.equals(removalAction);
Scott Kennedya85831d2013-04-12 15:54:22 -0700454
Scott Kennedyb905d082013-05-07 16:44:34 -0700455 // Do a first pass to extract necessary information on what is safe to display
Scott Kennedyccd3ce22013-04-26 10:34:29 -0700456 boolean archiveVisibleEnabled = false;
457 boolean deleteVisibleEnabled = false;
458 for (int i = 0; i < menu.size(); i++) {
459 final MenuItem menuItem = menu.getItem(i);
460 final int itemId = menuItem.getItemId();
461 final boolean visible = menuItem.isVisible();
462 final boolean enabled = menuItem.isEnabled();
463
464 if (itemId == R.id.archive || itemId == R.id.remove_folder) {
465 archiveVisibleEnabled |= (visible & enabled);
466 } else if (itemId == R.id.delete || itemId == R.id.discard_drafts) {
467 deleteVisibleEnabled |= (visible & enabled);
468 }
469 }
470
Scott Kennedyb905d082013-05-07 16:44:34 -0700471 int actionItems = 0;
472
Scott Kennedya85831d2013-04-12 15:54:22 -0700473 for (int i = 0; i < menu.size(); i++) {
474 final MenuItem menuItem = menu.getItem(i);
Scott Kennedy26f2b302013-04-22 17:47:17 -0700475 final int itemId = menuItem.getItemId();
Scott Kennedya85831d2013-04-12 15:54:22 -0700476
Scott Kennedyb905d082013-05-07 16:44:34 -0700477 // We only want to promote it if it's visible and has an icon
478 if (menuItem.isVisible() && menuItem.getIcon() != null) {
Scott Kennedy2b9d80e2013-07-30 23:03:45 -0700479 if (itemId == R.id.archive || itemId == R.id.remove_folder) {
480 /*
481 * If this is disabled, and we want to show both archive and delete, we will
482 * hide archive (rather than showing it disabled), and take up one of our
483 * spaces. If we only want to show archive, we'll hide it, but not take up
484 * a space.
485 */
486 if (!menuItem.isEnabled() && showArchive) {
487 menuItem.setVisible(false);
Andrew Sapperstein43a1cc12013-05-03 10:21:02 -0700488
Scott Kennedy2b9d80e2013-07-30 23:03:45 -0700489 if (showDelete) {
490 actionItems++;
Scott Kennedyb905d082013-05-07 16:44:34 -0700491 }
Scott Kennedy2b9d80e2013-07-30 23:03:45 -0700492 } else {
Scott Kennedyb905d082013-05-07 16:44:34 -0700493 /*
494 * We show this if the following are all true:
495 * 1. The user wants to display archive, or delete is not visible
496 * 2. We have room for it
497 */
498 if ((showArchive || !deleteVisibleEnabled) && actionItems < maxItems) {
499 menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
500 actionItems++;
501 }
Scott Kennedy2b9d80e2013-07-30 23:03:45 -0700502 }
503 } else if (itemId == R.id.delete || itemId == R.id.discard_drafts) {
504 /*
505 * We show this if the following are all true:
506 * 1. The user wants to display delete, or archive is not visible
507 * 2. We have room for it
508 */
509 if ((showDelete || !archiveVisibleEnabled) && actionItems < maxItems) {
510 menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
Scott Kennedyb905d082013-05-07 16:44:34 -0700511 actionItems++;
Scott Kennedy2b9d80e2013-07-30 23:03:45 -0700512 }
Andrew Sapperstein6c570db2013-08-06 17:21:36 -0700513 } else if (itemId == R.id.change_folders) {
Scott Kennedy2b9d80e2013-07-30 23:03:45 -0700514 final boolean showChangeFolder = account
515 .supportsCapability(AccountCapabilities.MULTIPLE_FOLDERS_PER_CONV);
516 menuItem.setVisible(showChangeFolder);
517
518 if (showChangeFolder && actionItems < maxItems) {
519 menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
520 actionItems++;
521 }
522 } else if (itemId == R.id.search) {
523 menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS
524 | MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
525 actionItems++;
526 } else {
527 if (actionItems < maxItems) {
528 menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
529 actionItems++;
530 }
Scott Kennedyb905d082013-05-07 16:44:34 -0700531 }
Scott Kennedyef91f8b2013-05-03 19:42:53 -0700532 }
Scott Kennedya85831d2013-04-12 15:54:22 -0700533 }
Mindy Pereira68f2e222012-03-07 10:36:54 -0800534 }
Vikram Aggarwal5e5ac742011-12-19 08:14:16 -0800535
Vikram Aggarwalabd24d82012-04-26 13:23:14 -0700536 /**
Vikram Aggarwalad8b99b2013-04-07 16:37:28 -0700537 * Put the ActionBar in List navigation mode.
Vikram Aggarwalabd24d82012-04-26 13:23:14 -0700538 */
Mindy Pereira72a9f392012-03-21 09:41:09 -0700539 private void showNavList() {
Paul Westbrook8c887ef2013-04-24 00:29:28 -0700540 setTitleModeFlags(getActionBarTitleModeFlag());
Vikram Aggarwalb642b312013-04-24 18:41:40 -0700541 setFolderAndAccount(false);
Mindy Pereira72a9f392012-03-21 09:41:09 -0700542 }
543
Paul Westbrook8c887ef2013-04-24 00:29:28 -0700544 private void setSubtitle(CharSequence subtitle) {
Andy Huangc499d712013-06-12 17:37:57 -0700545 if (!TextUtils.equals(subtitle, mActionBar.getSubtitle())) {
546 mActionBar.setSubtitle(subtitle);
547 }
Paul Westbrook8c887ef2013-04-24 00:29:28 -0700548 if (mLegacySubTitle != null) {
549 mLegacySubTitle.setText(subtitle);
550 }
551 }
552
Paul Westbrook8c887ef2013-04-24 00:29:28 -0700553 private void setTitle(CharSequence title) {
Andy Huangc499d712013-06-12 17:37:57 -0700554 if (!TextUtils.equals(title, mActionBar.getTitle())) {
555 mActionBar.setTitle(title);
556 }
Paul Westbrook8c887ef2013-04-24 00:29:28 -0700557 if (mLegacyTitle != null) {
558 mLegacyTitle.setText(title);
559 }
560 }
561
562 private int getActionBarTitleModeFlag() {
563 return mUseLegacyTitle ? ActionBar.DISPLAY_SHOW_CUSTOM : ActionBar.DISPLAY_SHOW_TITLE;
mindyp6fb1b622012-09-05 09:27:08 -0700564 }
565
566 /**
Vikram Aggarwalfd7eaba2013-04-11 12:31:46 -0700567 * Set the actionbar mode to empty: no title, no subtitle, no custom view.
mindyp6fb1b622012-09-05 09:27:08 -0700568 */
569 protected void setEmptyMode() {
Vikram Aggarwalfd7eaba2013-04-11 12:31:46 -0700570 // Disable title/subtitle and the custom view by setting the bitmask to all off.
571 setTitleModeFlags(0);
Vikram Aggarwalabd24d82012-04-26 13:23:14 -0700572 }
573
Vikram Aggarwalad8b99b2013-04-07 16:37:28 -0700574 /**
575 * Removes the back button from being shown
576 */
Mindy Pereira68f2e222012-03-07 10:36:54 -0800577 public void removeBackButton() {
578 if (mActionBar == null) {
579 return;
580 }
Vikram Aggarwal649b9ea2012-08-27 12:15:20 -0700581 // Remove the back button but continue showing an icon.
582 final int mask = ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME;
583 mActionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME, mask);
Mindy Pereira68f2e222012-03-07 10:36:54 -0800584 mActivity.getActionBar().setHomeButtonEnabled(false);
585 }
Mindy Pereirabc57bf12012-02-29 14:39:09 -0800586
Mindy Pereira68f2e222012-03-07 10:36:54 -0800587 public void setBackButton() {
Andy Huang12b3ee42013-04-24 22:49:43 -0700588 if (mActionBar == null) {
Mindy Pereira68f2e222012-03-07 10:36:54 -0800589 return;
590 }
Vikram Aggarwal649b9ea2012-08-27 12:15:20 -0700591 // Show home as up, and show an icon.
592 final int mask = ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME;
593 mActionBar.setDisplayOptions(mask, mask);
Mindy Pereira68f2e222012-03-07 10:36:54 -0800594 mActivity.getActionBar().setHomeButtonEnabled(true);
595 }
Vikram Aggarwal5e5ac742011-12-19 08:14:16 -0800596
Mindy Pereira68f2e222012-03-07 10:36:54 -0800597 @Override
598 public boolean onQueryTextSubmit(String query) {
Mindy Pereira4ce59942012-03-08 09:57:35 -0800599 if (mSearch != null) {
600 mSearch.collapseActionView();
601 mSearchWidget.setQuery("", false);
602 }
Andy Huang313ac132013-03-04 23:40:58 -0800603 mController.executeSearch(query.trim());
Mindy Pereira68f2e222012-03-07 10:36:54 -0800604 return true;
605 }
Vikram Aggarwal5e5ac742011-12-19 08:14:16 -0800606
Mindy Pereira68f2e222012-03-07 10:36:54 -0800607 @Override
608 public boolean onQueryTextChange(String newText) {
Mindy Pereira68f2e222012-03-07 10:36:54 -0800609 return false;
610 }
Mindy Pereira9b623802012-03-07 17:15:49 -0800611
Vikram Aggarwal12a7c042012-03-28 16:48:08 -0700612 // Next two methods are called when search suggestions are clicked.
613 @Override
614 public boolean onSuggestionSelect(int position) {
615 return onSuggestionClick(position);
616 }
617
618 @Override
619 public boolean onSuggestionClick(int position) {
620 final Cursor c = mSearchWidget.getSuggestionsAdapter().getCursor();
621 final boolean haveValidQuery = (c != null) && c.moveToPosition(position);
622 if (!haveValidQuery) {
623 LogUtils.d(LOG_TAG, "onSuggestionClick: Couldn't get a search query");
Mindy Pereiraf8fcb802012-06-25 11:43:06 -0700624 // We haven't handled this query, but the default behavior will
625 // leave EXTRA_ACCOUNT un-populated, leading to a crash. So claim
626 // that we have handled the event.
Vikram Aggarwal12a7c042012-03-28 16:48:08 -0700627 return true;
628 }
Mindy Pereirab466bcf2012-06-14 15:52:26 -0700629 collapseSearch();
Mindy Pereira1e180dd2012-08-16 10:24:10 -0700630 // what is in the text field
Mindy Pereiraca97b312012-06-25 14:00:20 -0700631 String queryText = mSearchWidget.getQuery().toString();
Mindy Pereira1e180dd2012-08-16 10:24:10 -0700632 // What the suggested query is
Mindy Pereiraf8fcb802012-06-25 11:43:06 -0700633 String query = c.getString(c.getColumnIndex(SearchManager.SUGGEST_COLUMN_QUERY));
mindyp0fc8fa52012-09-04 10:43:23 -0700634 // If the text the user typed in is a prefix of what is in the search
635 // widget suggestion query, just take the search widget suggestion
636 // query. Otherwise, it is a suffix and we want to remove matching
637 // prefix portions.
638 if (!TextUtils.isEmpty(queryText) && query.indexOf(queryText) != 0) {
Mindy Pereira5e1403c2012-07-18 13:22:11 -0700639 final int queryTokenIndex = queryText
640 .lastIndexOf(SearchRecentSuggestionsProvider.QUERY_TOKEN_SEPARATOR);
641 if (queryTokenIndex > -1) {
642 queryText = queryText.substring(0, queryTokenIndex);
Mindy Pereiraca97b312012-06-25 14:00:20 -0700643 }
644 // Since we auto-complete on each token in a query, if the query the
645 // user typed up until the last token is a substring of the
646 // suggestion they click, make sure we don't double include the
647 // query text. For example:
648 // user types john, that matches john palo alto
649 // User types john p, that matches john john palo alto
650 // Remove the first john
Mindy Pereira5e1403c2012-07-18 13:22:11 -0700651 // Only do this if we have multiple query tokens.
652 if (queryTokenIndex > -1 && !TextUtils.isEmpty(query) && query.contains(queryText)
Mindy Pereiraca97b312012-06-25 14:00:20 -0700653 && queryText.length() < query.length()) {
654 int start = query.indexOf(queryText);
655 query = query.substring(0, start) + query.substring(start + queryText.length());
656 }
657 }
Andy Huang313ac132013-03-04 23:40:58 -0800658 mController.executeSearch(query.trim());
Vikram Aggarwal12a7c042012-03-28 16:48:08 -0700659 return true;
660 }
Mindy Pereiraa46a57f2012-04-03 17:19:31 -0700661
Vikram Aggarwalf642b812013-03-15 16:30:21 -0700662 /**
663 * Uses the current state to update the current folder {@link #mFolder} and the current
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700664 * account {@link #mAccount} shown in the actionbar. Also updates the actionbar subtitle to
665 * momentarily display the unread count if it has changed.
Vikram Aggarwalf18b1172013-04-17 12:36:23 -0700666 * @param folderChanged true if folder changed in terms of URI
Vikram Aggarwalf642b812013-03-15 16:30:21 -0700667 */
Vikram Aggarwalf18b1172013-04-17 12:36:23 -0700668 private void setFolderAndAccount(final boolean folderChanged) {
Vikram Aggarwal5fce9922013-04-01 21:17:42 -0700669 // Very little can be done if the actionbar or activity is null.
670 if (mActionBar == null || mActivity == null) {
671 return;
672 }
Tony Mantlerbd091502013-09-16 13:59:47 -0700673 if (ViewMode.isWaitingForSync(getMode())) {
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700674 // Account is not synced: clear title and update the subtitle.
Paul Westbrook8c887ef2013-04-24 00:29:28 -0700675 setTitle("");
Vikram Aggarwal9dcd4832013-05-09 16:08:59 -0700676 removeUnreadCount(true);
Vikram Aggarwalf642b812013-03-15 16:30:21 -0700677 return;
678 }
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700679 // Check if we should be changing the actionbar at all, and back off if not.
Tony Mantlerbd091502013-09-16 13:59:47 -0700680 final boolean isShowingFolder = mIsOnTablet || ViewMode.isListMode(getMode());
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700681 if (!isShowingFolder) {
Paul Westbrook91024952013-04-21 15:59:06 -0700682 // It isn't necessary to set the title in this case, as the title view will
683 // be hidden
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700684 return;
Vikram Aggarwalf642b812013-03-15 16:30:21 -0700685 }
Vikram Aggarwal26b0bfd2013-03-29 13:05:08 -0700686 if (mFolder == null) {
Paul Westbrook91024952013-04-21 15:59:06 -0700687 // Clear the action bar title. We don't want the app name to be shown while
688 // waiting for the folder query to finish
Paul Westbrook8c887ef2013-04-24 00:29:28 -0700689 setTitle("");
Vikram Aggarwal26b0bfd2013-03-29 13:05:08 -0700690 return;
691 }
Paul Westbrook8c887ef2013-04-24 00:29:28 -0700692 setTitle(mFolder.name);
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700693
Vikram Aggarwalf18b1172013-04-17 12:36:23 -0700694 final int folderUnreadCount = mFolder.isUnreadCountHidden() ? 0 : mFolder.unreadCount;
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700695 // The user shouldn't see "999+ unread messages", and then a short while later: "999+
696 // unread messages". So we set our unread count just past the limit. This way we can
697 // change the subtitle the first time around but not for subsequent changes as far as the
698 // unread count remains over the limit.
Vikram Aggarwalf18b1172013-04-17 12:36:23 -0700699 final int toDisplay = (folderUnreadCount > UNREAD_LIMIT)
700 ? (UNREAD_LIMIT + 1) : folderUnreadCount;
Vikram Aggarwalc590ba82013-05-02 11:28:05 -0700701 if ((mUnreadCount != toDisplay || folderChanged) && toDisplay != 0) {
Vikram Aggarwal9dcd4832013-05-09 16:08:59 -0700702 setSubtitle(Utils.getUnreadMessageString(mActivity.getApplicationContext(), toDisplay));
Vikram Aggarwal26b0bfd2013-03-29 13:05:08 -0700703 }
Vikram Aggarwal055b24b2013-05-10 15:14:20 -0700704 // Schedule a removal of unread count for the future, if there isn't one already. If the
705 // unread count dropped to zero, remove it and show the account name right away.
706 removeUnreadCount(toDisplay == 0);
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700707 // Remember the new value for the next run
Vikram Aggarwalf18b1172013-04-17 12:36:23 -0700708 mUnreadCount = toDisplay;
709 }
710
711 /**
712 * Remove the unread count and show the account name, if required.
Vikram Aggarwal9dcd4832013-05-09 16:08:59 -0700713 * @param now true if you want the change to happen immediately. False if you want to enforce
714 * it happens later.
Vikram Aggarwalf18b1172013-04-17 12:36:23 -0700715 */
Vikram Aggarwal9dcd4832013-05-09 16:08:59 -0700716 private void removeUnreadCount(boolean now) {
717 if (now) {
718 // Remove all previous messages which might change the subtitle
719 mHandler.removeMessages(SubtitleHandler.EMAIL);
720 // Update the subtitle: clear it or show account name.
721 mHandler.sendEmptyMessage(SubtitleHandler.EMAIL);
722 } else {
723 if (!mHandler.hasMessages(SubtitleHandler.EMAIL)) {
724 // In a short while, show the account name in its place.
725 mHandler.sendEmptyMessageDelayed(SubtitleHandler.EMAIL, ACCOUNT_DELAY_MS);
726 }
727 }
Vikram Aggarwalbc462ca2013-03-15 10:41:03 -0700728 }
729
Mindy Pereiraa46a57f2012-04-03 17:19:31 -0700730 /**
731 * Notify that the folder has changed.
732 */
733 public void onFolderUpdated(Folder folder) {
Vikram Aggarwal26b0bfd2013-03-29 13:05:08 -0700734 if (folder == null) {
735 return;
736 }
Vikram Aggarwale875b662013-04-10 15:56:46 -0700737 /** True if we are changing folders. */
Scott Kennedy259df5b2013-07-11 13:24:01 -0700738 final boolean changingFolders = (mFolder == null || !mFolder.equals(folder));
Vikram Aggarwalc18c3382013-04-05 15:29:13 -0700739 mFolder = folder;
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700740 setFolderAndAccount(changingFolders);
Vikram Aggarwal49499e62013-02-08 16:39:08 -0800741 final ConversationListContext listContext = mController == null ? null :
742 mController.getCurrentListContext();
Vikram Aggarwale875b662013-04-10 15:56:46 -0700743 if (changingFolders && !ConversationListContext.isSearchResult(listContext)) {
Vikram Aggarwal49499e62013-02-08 16:39:08 -0800744 closeSearchField();
745 }
Mindy Pereiraa46a57f2012-04-03 17:19:31 -0700746 }
Vikram Aggarwal0dda5732012-04-06 11:20:16 -0700747
748 @Override
749 public boolean onMenuItemActionExpand(MenuItem item) {
750 // Do nothing. Required as part of the interface, we ar only interested in
751 // onMenuItemActionCollapse(MenuItem).
752 // Have to return true here. Unlike other callbacks, the return value here is whether
753 // we want to suppress the action (rather than consume the action). We don't want to
754 // suppress the action.
755 return true;
756 }
757
758 @Override
759 public boolean onMenuItemActionCollapse(MenuItem item) {
Mindy Pereirab466bcf2012-06-14 15:52:26 -0700760 // Work around b/6664203 by manually forcing this view to be VISIBLE
761 // upon ActionView collapse. DISPLAY_SHOW_CUSTOM will still control its final
762 // visibility.
Andy Huang5895f7b2012-06-01 17:07:20 -0700763 setVisibility(VISIBLE);
Mindy Pereirab466bcf2012-06-14 15:52:26 -0700764 // Have to return true here. Unlike other callbacks, the return value
765 // here is whether we want to suppress the action (rather than consume the action). We
766 // don't want to suppress the action.
Vikram Aggarwal0dda5732012-04-06 11:20:16 -0700767 return true;
768 }
Vikram Aggarwal37263972012-04-17 15:51:14 -0700769
Vikram Aggarwalfd7eaba2013-04-11 12:31:46 -0700770 /**
771 * Sets the actionbar mode: Pass it an integer which contains each of these values, perhaps
Paul Westbrook8c887ef2013-04-24 00:29:28 -0700772 * OR'd together: {@link ActionBar#DISPLAY_SHOW_CUSTOM} and
773 * {@link ActionBar#DISPLAY_SHOW_TITLE}. To disable all, pass a zero.
Vikram Aggarwalfd7eaba2013-04-11 12:31:46 -0700774 * @param enabledFlags
775 */
Andy Huangaf65e732013-01-28 20:45:40 -0800776 private void setTitleModeFlags(int enabledFlags) {
Paul Westbrook8c887ef2013-04-24 00:29:28 -0700777 final int mask = ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_CUSTOM;
Andy Huangaf65e732013-01-28 20:45:40 -0800778 mActionBar.setDisplayOptions(enabledFlags, mask);
779 }
780
Yorke Leef807ba72012-09-20 17:18:05 -0700781 public void setCurrentConversation(Conversation conversation) {
782 mCurrentConversation = conversation;
783 }
784
785 //We need to do this here instead of in the fragment
786 public void setConversationModeOptions(Menu menu) {
787 if (mCurrentConversation == null) {
788 return;
789 }
790 final boolean showMarkImportant = !mCurrentConversation.isImportant();
791 Utils.setMenuItemVisibility(menu, R.id.mark_important, showMarkImportant
792 && mAccount.supportsCapability(UIProvider.AccountCapabilities.MARK_IMPORTANT));
793 Utils.setMenuItemVisibility(menu, R.id.mark_not_important, !showMarkImportant
794 && mAccount.supportsCapability(UIProvider.AccountCapabilities.MARK_IMPORTANT));
795 final boolean showDelete = mFolder != null &&
796 mFolder.supportsCapability(UIProvider.FolderCapabilities.DELETE);
797 Utils.setMenuItemVisibility(menu, R.id.delete, showDelete);
798 // We only want to show the discard drafts menu item if we are not showing the delete menu
799 // item, and the current folder is a draft folder and the account supports discarding
800 // drafts for a conversation
801 final boolean showDiscardDrafts = !showDelete && mFolder != null && mFolder.isDraft() &&
802 mAccount.supportsCapability(AccountCapabilities.DISCARD_CONVERSATION_DRAFTS);
803 Utils.setMenuItemVisibility(menu, R.id.discard_drafts, showDiscardDrafts);
804 final boolean archiveVisible = mAccount.supportsCapability(AccountCapabilities.ARCHIVE)
805 && mFolder != null && mFolder.supportsCapability(FolderCapabilities.ARCHIVE)
806 && !mFolder.isTrash();
807 Utils.setMenuItemVisibility(menu, R.id.archive, archiveVisible);
808 Utils.setMenuItemVisibility(menu, R.id.remove_folder, !archiveVisible && mFolder != null
809 && mFolder.supportsCapability(FolderCapabilities.CAN_ACCEPT_MOVED_MESSAGES)
Tony Mantler53b34252013-09-25 16:42:25 -0700810 && !mFolder.isProviderFolder()
811 && mAccount.supportsCapability(AccountCapabilities.ARCHIVE));
Scott Kennedy28f3d5c2013-04-08 16:53:50 -0700812 Utils.setMenuItemVisibility(menu, R.id.move_to, mFolder != null
813 && mFolder.supportsCapability(FolderCapabilities.ALLOWS_REMOVE_CONVERSATION));
Scott Kennedydd2ec682013-06-03 19:16:13 -0700814 Utils.setMenuItemVisibility(menu, R.id.move_to_inbox, mFolder != null
815 && mFolder.supportsCapability(FolderCapabilities.ALLOWS_MOVE_TO_INBOX));
816
Yorke Leef807ba72012-09-20 17:18:05 -0700817 final MenuItem removeFolder = menu.findItem(R.id.remove_folder);
Alice Yang40698e62013-03-14 11:31:53 -0700818 if (mFolder != null && removeFolder != null) {
Yorke Leef807ba72012-09-20 17:18:05 -0700819 removeFolder.setTitle(mActivity.getApplicationContext().getString(
820 R.string.remove_folder, mFolder.name));
821 }
822 Utils.setMenuItemVisibility(menu, R.id.report_spam,
823 mAccount.supportsCapability(AccountCapabilities.REPORT_SPAM) && mFolder != null
824 && mFolder.supportsCapability(FolderCapabilities.REPORT_SPAM)
825 && !mCurrentConversation.spam);
826 Utils.setMenuItemVisibility(menu, R.id.mark_not_spam,
827 mAccount.supportsCapability(AccountCapabilities.REPORT_SPAM) && mFolder != null
828 && mFolder.supportsCapability(FolderCapabilities.MARK_NOT_SPAM)
829 && mCurrentConversation.spam);
830 Utils.setMenuItemVisibility(menu, R.id.report_phishing,
831 mAccount.supportsCapability(AccountCapabilities.REPORT_PHISHING) && mFolder != null
832 && mFolder.supportsCapability(FolderCapabilities.REPORT_PHISHING)
833 && !mCurrentConversation.phishing);
834 Utils.setMenuItemVisibility(menu, R.id.mute,
835 mAccount.supportsCapability(AccountCapabilities.MUTE) && mFolder != null
836 && mFolder.supportsCapability(FolderCapabilities.DESTRUCTIVE_MUTE)
837 && !mCurrentConversation.muted);
838 }
Paul Westbrook8c887ef2013-04-24 00:29:28 -0700839
840 private static boolean actionBarSupportsNewMethods(ActionBar bar) {
841 // TODO(pwestbro) switch this to
842 // (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) when we switch to the
843 // latest SDK
844 if (Build.VERSION.SDK_INT > 17) {
845 return true;
846 }
847 if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) {
848 return false;
849 }
850 boolean supportsNewApi = false;
851 try {
852 if (bar != null) {
853 supportsNewApi = (ActionBar.class.getField("DISPLAY_TITLE_MULTIPLE_LINES") != null);
854 }
855 } catch (NoSuchFieldException e) {
856 // stay false
857 }
858 return supportsNewApi;
859 }
860
861 @Override
862 public void onClick (View v) {
863 if (v.getId() == R.id.legacy_title_container) {
864 mController.onUpPressed();
865 }
866 }
Tony Mantlerbd091502013-09-16 13:59:47 -0700867
868 public ViewMode getViewModeController() {
869 return mViewModeController;
870 }
871
872 public void setViewModeController(ViewMode viewModeController) {
873 mViewModeController = viewModeController;
874 mViewModeController.addListener(this);
875 }
Vikram Aggarwal5e5ac742011-12-19 08:14:16 -0800876}