blob: c967530512eb181ef4e25af6df082a09cf5b5843 [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 /**
76 * The current mode of the ActionBar. This references constants in {@link ViewMode}
77 */
78 private int mMode = ViewMode.UNKNOWN;
79
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;
Mindy Pereira9b623802012-03-07 17:15:49 -080093 private MenuItem mRefreshItem;
Marc Blankedcab012012-04-04 12:42:38 -070094 private MenuItem mFolderSettingsItem;
Scott Kennedy7ee089e2013-03-25 17:05:44 -040095 private MenuItem mEmptyTrashItem;
96 private MenuItem mEmptySpamItem;
Mindy Pereira9b623802012-03-07 17:15:49 -080097 private View mRefreshActionView;
Paul Westbrook8c887ef2013-04-24 00:29:28 -070098 private boolean mUseLegacyTitle;
99 private View mLegacyTitleContainer;
100 private TextView mLegacyTitle;
101 private TextView mLegacySubTitle;
102
Vikram Aggarwalf642b812013-03-15 16:30:21 -0700103 /** True if the current device is a tablet, false otherwise. */
Vikram Aggarwalad8b99b2013-04-07 16:37:28 -0700104 protected final boolean mIsOnTablet;
Mindy Pereira9b623802012-03-07 17:15:49 -0800105 private boolean mRefreshInProgress;
Yorke Leef807ba72012-09-20 17:18:05 -0700106 private Conversation mCurrentConversation;
Mindy Pereira9b623802012-03-07 17:15:49 -0800107
Paul Westbrookb334c902012-06-25 11:42:46 -0700108 public static final String LOG_TAG = LogTag.getLogTag();
Andy Huang0d647352012-03-21 21:48:16 -0700109
Mindy Pereira9b623802012-03-07 17:15:49 -0800110 private final Runnable mInvalidateMenu = new Runnable() {
111 @Override
112 public void run() {
113 mActivity.invalidateOptionsMenu();
114 }
115 };
Vikram Aggarwal50ff0e52013-03-14 13:58:02 -0700116 private FolderObserver mFolderObserver;
Mindy Pereira68f2e222012-03-07 10:36:54 -0800117
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700118 /** A handler that changes the subtitle when it receives a message. */
119 private final class SubtitleHandler extends Handler {
120 /** Message sent to display the account email address in the subtitle. */
121 private static final int EMAIL = 0;
122
123 @Override
124 public void handleMessage(Message message) {
125 assert (message.what == EMAIL);
126 final String subtitleText;
Vikram Aggarwalb335fb32013-04-22 17:16:15 -0700127 if (mAccount != null) {
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700128 // Display the account name (email address).
129 subtitleText = mAccount.name;
130 } else {
Vikram Aggarwalb335fb32013-04-22 17:16:15 -0700131 subtitleText = null;
132 LogUtils.wtf(LOG_TAG, "MABV.handleMessage() has a null account!");
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700133 }
Paul Westbrook8c887ef2013-04-24 00:29:28 -0700134 setSubtitle(subtitleText);
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700135 super.handleMessage(message);
136 }
137 }
138
139 /** Changes the subtitle to display the account name */
140 private final SubtitleHandler mHandler = new SubtitleHandler();
141 /** Unread count for the current folder. */
142 private int mUnreadCount = 0;
143 /** We show the email address after this delay: 5 seconds currently */
144 private static final int ACCOUNT_DELAY_MS = 5 * 1000;
145 /** At what point do we stop showing the unread count: 999+ currently */
146 private final int UNREAD_LIMIT;
147
Vikram Aggarwala51ef9f2013-04-02 13:25:35 -0700148 /** Updates the resolver and tells it the most recent account. */
149 private final class UpdateProvider extends AsyncTask<Bundle, Void, Void> {
150 final Uri mAccount;
151 final ContentResolver mResolver;
152 public UpdateProvider(Uri account, ContentResolver resolver) {
153 mAccount = account;
154 mResolver = resolver;
155 }
156
157 @Override
158 protected Void doInBackground(Bundle... params) {
159 mResolver.call(mAccount, UIProvider.AccountCallMethods.SET_CURRENT_ACCOUNT,
160 mAccount.toString(), params[0]);
161 return null;
162 }
163 }
164
Vikram Aggarwal7c401b72012-08-13 16:43:47 -0700165 private final AccountObserver mAccountObserver = new AccountObserver() {
166 @Override
167 public void onChanged(Account newAccount) {
Scott Banachowski53a615d2012-12-19 15:31:20 -0800168 updateAccount(newAccount);
Vikram Aggarwal7c401b72012-08-13 16:43:47 -0700169 }
170 };
Andy Huangaf65e732013-01-28 20:45:40 -0800171
Andy Huang6681e542012-06-14 14:36:45 -0700172 public MailActionBarView(Context context) {
Mindy Pereira68f2e222012-03-07 10:36:54 -0800173 this(context, null);
174 }
175
Andy Huang6681e542012-06-14 14:36:45 -0700176 public MailActionBarView(Context context, AttributeSet attrs) {
Mindy Pereira68f2e222012-03-07 10:36:54 -0800177 this(context, attrs, 0);
178 }
179
Andy Huang6681e542012-06-14 14:36:45 -0700180 public MailActionBarView(Context context, AttributeSet attrs, int defStyle) {
Mindy Pereira68f2e222012-03-07 10:36:54 -0800181 super(context, attrs, defStyle);
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700182 final Resources r = getResources();
183 mIsOnTablet = Utils.useTabletUI(r);
184 UNREAD_LIMIT = r.getInteger(R.integer.maxUnreadCount);
Andy Huang5895f7b2012-06-01 17:07:20 -0700185 }
186
Paul Westbrook8c887ef2013-04-24 00:29:28 -0700187 private void initializeTitleViews() {
188 mLegacyTitleContainer = findViewById(R.id.legacy_title_container);
189 if (mLegacyTitleContainer != null) {
190 // Determine if this device is running on MR1.1 or later
191 final boolean runningMR11OrLater = actionBarSupportsNewMethods(mActionBar);
192 if (runningMR11OrLater || !mController.isDrawerEnabled()) {
193 // We don't need the legacy view, just hide it
194 mLegacyTitleContainer.setVisibility(View.GONE);
195 mUseLegacyTitle = false;
196 } else {
197 mUseLegacyTitle = true;
198 // We need to show the legacy title/subtitle. Set the click listener
199 mLegacyTitleContainer.setOnClickListener(this);
200
201 mLegacyTitle = (TextView)mLegacyTitleContainer.findViewById(R.id.legacy_title);
202 mLegacySubTitle =
203 (TextView)mLegacyTitleContainer.findViewById(R.id.legacy_subtitle);
204 }
205 }
Mindy Pereira68f2e222012-03-07 10:36:54 -0800206 }
207
Andy Huang313ac132013-03-04 23:40:58 -0800208 public void expandSearch() {
209 if (mSearch != null) {
210 mSearch.expandActionView();
211 }
212 }
213
Vikram Aggarwalb17cbc02012-04-06 15:41:46 -0700214 /**
Vikram Aggarwal0dda5732012-04-06 11:20:16 -0700215 * Close the search view if it is expanded.
Vikram Aggarwalb17cbc02012-04-06 15:41:46 -0700216 */
217 public void collapseSearch() {
218 if (mSearch != null) {
219 mSearch.collapseActionView();
220 }
221 }
222
Mindy Pereiraedb135e2012-06-15 08:34:09 -0700223 /**
224 * Get the search menu item.
225 */
226 protected MenuItem getSearch() {
227 return mSearch;
228 }
229
Mindy Pereira68f2e222012-03-07 10:36:54 -0800230 public boolean onCreateOptionsMenu(Menu menu) {
231 // If the mode is valid, then set the initial menu
232 if (mMode == ViewMode.UNKNOWN) {
233 return false;
234 }
Mindy Pereira9b623802012-03-07 17:15:49 -0800235 mSearch = menu.findItem(R.id.search);
236 if (mSearch != null) {
237 mSearchWidget = (SearchView) mSearch.getActionView();
Vikram Aggarwal0dda5732012-04-06 11:20:16 -0700238 mSearch.setOnActionExpandListener(this);
Mindy Pereira68f2e222012-03-07 10:36:54 -0800239 SearchManager searchManager = (SearchManager) mActivity.getActivityContext()
240 .getSystemService(Context.SEARCH_SERVICE);
Mindy Pereirafb178492012-03-07 18:56:42 -0800241 if (searchManager != null && mSearchWidget != null) {
Mindy Pereira68f2e222012-03-07 10:36:54 -0800242 SearchableInfo info = searchManager.getSearchableInfo(mActivity.getComponentName());
243 mSearchWidget.setSearchableInfo(info);
244 mSearchWidget.setOnQueryTextListener(this);
Vikram Aggarwal12a7c042012-03-28 16:48:08 -0700245 mSearchWidget.setOnSuggestionListener(this);
Vikram Aggarwal0dda5732012-04-06 11:20:16 -0700246 mSearchWidget.setIconifiedByDefault(true);
Mindy Pereira68f2e222012-03-07 10:36:54 -0800247 }
248 }
249 mHelpItem = menu.findItem(R.id.help_info_menu_item);
Paul Westbrook517743e2012-03-22 10:40:46 -0700250 mSendFeedbackItem = menu.findItem(R.id.feedback_menu_item);
Mindy Pereira9b623802012-03-07 17:15:49 -0800251 mRefreshItem = menu.findItem(R.id.refresh);
Marc Blankedcab012012-04-04 12:42:38 -0700252 mFolderSettingsItem = menu.findItem(R.id.folder_options);
Scott Kennedy7ee089e2013-03-25 17:05:44 -0400253 mEmptyTrashItem = menu.findItem(R.id.empty_trash);
254 mEmptySpamItem = menu.findItem(R.id.empty_spam);
Mindy Pereira68f2e222012-03-07 10:36:54 -0800255 return true;
256 }
257
258 public int getOptionsMenuId() {
259 // Relies on the ordering of the view modes, since they are integer constants.
260 final int[] modeMenu = {
261 // 0: UNKNOWN
262 R.menu.conversation_list_menu,
263 // 1: CONVERSATION
264 R.menu.conversation_actions,
265 // 2: CONVERSATION_LIST
266 R.menu.conversation_list_menu,
267 // 3: FOLDER_LIST
268 R.menu.folder_list_menu,
269 // 4: SEARCH_RESULTS_LIST
270 R.menu.conversation_list_search_results_actions,
271 // 5: SEARCH_RESULTS_CONVERSATION
Scott Kennedyb905d082013-05-07 16:44:34 -0700272 R.menu.conversation_actions,
Paul Westbrook2d50bcd2012-04-10 11:53:47 -0700273 // 6: WAITING_FOR_ACCOUNT_INITIALIZATION
274 R.menu.wait_mode_actions
Mindy Pereira68f2e222012-03-07 10:36:54 -0800275 };
276 return modeMenu[mMode];
277 }
278
Vikram Aggarwal7c401b72012-08-13 16:43:47 -0700279 public void initialize(ControllableActivity activity, ActivityController callback,
Vikram Aggarwaldac89fa2013-03-05 16:43:09 -0800280 ActionBar actionBar) {
Mindy Pereira68f2e222012-03-07 10:36:54 -0800281 mActionBar = actionBar;
Vikram Aggarwala7997952012-03-09 10:30:13 -0800282 mController = callback;
Mindy Pereira68f2e222012-03-07 10:36:54 -0800283 mActivity = activity;
Paul Westbrook8c887ef2013-04-24 00:29:28 -0700284 initializeTitleViews();
285
Vikram Aggarwal50ff0e52013-03-14 13:58:02 -0700286 mFolderObserver = new FolderObserver() {
287 @Override
288 public void onChanged(Folder newFolder) {
289 onFolderUpdated(newFolder);
290 }
291 };
Vikram Aggarwal26b0bfd2013-03-29 13:05:08 -0700292 // Return values are purposely discarded. Initialization happens quite early, and we don't
293 // have a valid folder, or a valid list of accounts.
Vikram Aggarwal50ff0e52013-03-14 13:58:02 -0700294 mFolderObserver.initialize(mController);
Scott Banachowski53a615d2012-12-19 15:31:20 -0800295 updateAccount(mAccountObserver.initialize(activity.getAccountController()));
296 }
297
298 private void updateAccount(Account account) {
Vikram Aggarwalf18b1172013-04-17 12:36:23 -0700299 final boolean accountChanged = mAccount == null || !mAccount.uri.equals(account.uri);
Scott Banachowski53a615d2012-12-19 15:31:20 -0800300 mAccount = account;
Vikram Aggarwalf18b1172013-04-17 12:36:23 -0700301 if (mAccount != null && accountChanged) {
Vikram Aggarwalbc462ca2013-03-15 10:41:03 -0700302 final ContentResolver resolver = mActivity.getActivityContext().getContentResolver();
303 final Bundle bundle = new Bundle(1);
Scott Banachowski53a615d2012-12-19 15:31:20 -0800304 bundle.putParcelable(UIProvider.SetCurrentAccountColumns.ACCOUNT, account);
Vikram Aggarwala51ef9f2013-04-02 13:25:35 -0700305 final UpdateProvider updater = new UpdateProvider(mAccount.uri, resolver);
306 updater.execute(bundle);
Vikram Aggarwalf18b1172013-04-17 12:36:23 -0700307 setFolderAndAccount(false /* folderChanged */);
Scott Banachowski53a615d2012-12-19 15:31:20 -0800308 }
Vikram Aggarwalabd24d82012-04-26 13:23:14 -0700309 }
Andy Huange303b9b2012-04-19 16:31:09 -0700310
Vikram Aggarwalabd24d82012-04-26 13:23:14 -0700311 /**
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700312 * Called by the owner of the ActionBar to change the current folder.
Vikram Aggarwal5e5ac742011-12-19 08:14:16 -0800313 */
Mindy Pereira68f2e222012-03-07 10:36:54 -0800314 public void setFolder(Folder folder) {
Mindy Pereira641de652012-08-02 15:21:50 -0700315 setRefreshInProgress(false);
Marc Blankedcab012012-04-04 12:42:38 -0700316 mFolder = folder;
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700317 setFolderAndAccount(true);
Vikram Aggarwal1dd77c92012-08-21 09:27:40 -0700318 mActivity.invalidateOptionsMenu();
Mindy Pereira68f2e222012-03-07 10:36:54 -0800319 }
Vikram Aggarwal5e5ac742011-12-19 08:14:16 -0800320
Mindy Pereira641de652012-08-02 15:21:50 -0700321 public void onDestroy() {
322 if (mFolderObserver != null) {
Vikram Aggarwal50ff0e52013-03-14 13:58:02 -0700323 mFolderObserver.unregisterAndDestroy();
Mindy Pereira641de652012-08-02 15:21:50 -0700324 mFolderObserver = null;
325 }
Vikram Aggarwal7c401b72012-08-13 16:43:47 -0700326 mAccountObserver.unregisterAndDestroy();
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700327 mHandler.removeMessages(SubtitleHandler.EMAIL);
Mindy Pereira68f2e222012-03-07 10:36:54 -0800328 }
329
Vikram Aggarwala7997952012-03-09 10:30:13 -0800330 @Override
Mindy Pereira68f2e222012-03-07 10:36:54 -0800331 public void onViewModeChanged(int newMode) {
332 mMode = newMode;
Mindy Pereira68f2e222012-03-07 10:36:54 -0800333 mActivity.invalidateOptionsMenu();
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700334 mHandler.removeMessages(SubtitleHandler.EMAIL);
Vikram Aggarwal1a3f3fc2012-07-18 14:49:08 -0700335 // Check if we are either on a phone, or in Conversation mode on tablet. For these, the
336 // recent folders is enabled.
Andy Huangd736a382012-08-29 13:08:58 -0700337 switch (mMode) {
338 case ViewMode.UNKNOWN:
Andy Huangd736a382012-08-29 13:08:58 -0700339 break;
340 case ViewMode.CONVERSATION_LIST:
341 showNavList();
342 break;
Vikram Aggarwalad8b99b2013-04-07 16:37:28 -0700343 case ViewMode.SEARCH_RESULTS_CONVERSATION:
Vikram Aggarwale875b662013-04-10 15:56:46 -0700344 mActionBar.setDisplayHomeAsUpEnabled(true);
345 setEmptyMode();
346 break;
Andy Huangd736a382012-08-29 13:08:58 -0700347 case ViewMode.CONVERSATION:
Vikram Aggarwal49499e62013-02-08 16:39:08 -0800348 closeSearchField();
Andy Huangd736a382012-08-29 13:08:58 -0700349 mActionBar.setDisplayHomeAsUpEnabled(true);
Vikram Aggarwalad8b99b2013-04-07 16:37:28 -0700350 setEmptyMode();
Andy Huangd736a382012-08-29 13:08:58 -0700351 break;
352 case ViewMode.FOLDER_LIST:
Vikram Aggarwal49499e62013-02-08 16:39:08 -0800353 closeSearchField();
Andy Huangd736a382012-08-29 13:08:58 -0700354 mActionBar.setDisplayHomeAsUpEnabled(true);
Andy Huangaf65e732013-01-28 20:45:40 -0800355 setFoldersMode();
Andy Huangd736a382012-08-29 13:08:58 -0700356 break;
357 case ViewMode.WAITING_FOR_ACCOUNT_INITIALIZATION:
358 // We want the user to be able to switch accounts while waiting for an account
359 // to sync.
360 showNavList();
361 break;
362 }
Mindy Pereira68f2e222012-03-07 10:36:54 -0800363 }
Vikram Aggarwal5e5ac742011-12-19 08:14:16 -0800364
Vikram Aggarwal49499e62013-02-08 16:39:08 -0800365 /**
366 * Close the search query entry field to avoid keyboard events, and to restore the actionbar
367 * to non-search mode.
368 */
Vikram Aggarwaldac89fa2013-03-05 16:43:09 -0800369 private void closeSearchField() {
Vikram Aggarwal49499e62013-02-08 16:39:08 -0800370 if (mSearch == null) {
371 return;
372 }
373 mSearch.collapseActionView();
374 }
375
Mindy Pereiraedb135e2012-06-15 08:34:09 -0700376 protected int getMode() {
377 return mMode;
378 }
379
Mindy Pereirab849dfb2012-03-07 18:13:15 -0800380 public boolean onPrepareOptionsMenu(Menu menu) {
Mindy Pereira68f2e222012-03-07 10:36:54 -0800381 // We start out with every option enabled. Based on the current view, we disable actions
382 // that are possible.
Vikram Aggarwal95b28ab2012-04-26 15:53:09 -0700383 LogUtils.d(LOG_TAG, "ActionBarView.onPrepareOptionsMenu().");
Vikram Aggarwal5e5ac742011-12-19 08:14:16 -0800384
Andrew Sapperstein5747e152013-05-13 14:13:08 -0700385 if (mController.shouldHideMenuItems()) {
386 setMenuItemsToHiddenForOpenDrawer(menu);
387 return false;
388 }
Andy Huangd736a382012-08-29 13:08:58 -0700389 // TODO: move refresh stuff into setRefreshInProgress. can just setActionView without
390 // invalidating.
Mindy Pereira9b623802012-03-07 17:15:49 -0800391 if (mRefreshInProgress) {
392 if (mRefreshItem != null) {
393 if (mRefreshActionView == null) {
394 mRefreshItem.setActionView(R.layout.action_bar_indeterminate_progress);
395 mRefreshActionView = mRefreshItem.getActionView();
396 } else {
397 mRefreshItem.setActionView(mRefreshActionView);
398 }
399 }
400 } else {
401 if (mRefreshItem != null) {
402 mRefreshItem.setActionView(null);
403 }
404 }
Mindy Pereira68f2e222012-03-07 10:36:54 -0800405 if (mHelpItem != null) {
406 mHelpItem.setVisible(mAccount != null
407 && mAccount.supportsCapability(AccountCapabilities.HELP_CONTENT));
408 }
Paul Westbrook517743e2012-03-22 10:40:46 -0700409 if (mSendFeedbackItem != null) {
410 mSendFeedbackItem.setVisible(mAccount != null
411 && mAccount.supportsCapability(AccountCapabilities.SEND_FEEDBACK));
412 }
Marc Blankedcab012012-04-04 12:42:38 -0700413 if (mFolderSettingsItem != null) {
414 mFolderSettingsItem.setVisible(mFolder != null
415 && mFolder.supportsCapability(FolderCapabilities.SUPPORTS_SETTINGS));
416 }
Scott Kennedy7ee089e2013-03-25 17:05:44 -0400417 if (mEmptyTrashItem != null) {
418 mEmptyTrashItem.setVisible(mAccount != null && mFolder != null
419 && mAccount.supportsCapability(AccountCapabilities.EMPTY_TRASH)
Andrew Sappersteined5b52d2013-04-30 13:40:18 -0700420 && mFolder.isTrash() && mFolder.totalCount > 0);
Scott Kennedy7ee089e2013-03-25 17:05:44 -0400421 }
422 if (mEmptySpamItem != null) {
423 mEmptySpamItem.setVisible(mAccount != null && mFolder != null
424 && mAccount.supportsCapability(AccountCapabilities.EMPTY_SPAM)
Andrew Sappersteined5b52d2013-04-30 13:40:18 -0700425 && mFolder.isType(FolderType.SPAM) && mFolder.totalCount > 0);
Scott Kennedy7ee089e2013-03-25 17:05:44 -0400426 }
Mindy Pereira62c71792012-07-18 09:14:29 -0700427
Mindy Pereira9b623802012-03-07 17:15:49 -0800428 switch (mMode) {
Yorke Leef807ba72012-09-20 17:18:05 -0700429 case ViewMode.CONVERSATION:
430 case ViewMode.SEARCH_RESULTS_CONVERSATION:
431 // We update the ActionBar options when we are entering conversation view because
432 // waiting for the AbstractConversationViewFragment to do it causes duplicate icons
433 // to show up during the time between the conversation is selected and the fragment
434 // is added.
435 setConversationModeOptions(menu);
Scott Kennedyb905d082013-05-07 16:44:34 -0700436 // We want to use the user's preferred menu items here
437 final Resources resources = getResources();
438 final int maxItems = resources.getInteger(R.integer.actionbar_max_items);
439 final int hiddenItems = resources.getInteger(
440 R.integer.actionbar_hidden_non_cab_items_no_physical_button);
441 final int totalItems = maxItems
442 - (ViewConfiguration.get(getContext()).hasPermanentMenuKey()
443 ? 0 : hiddenItems);
444 reorderMenu(getContext(), mAccount, menu, totalItems);
Yorke Leef807ba72012-09-20 17:18:05 -0700445 break;
Mindy Pereira68f2e222012-03-07 10:36:54 -0800446 case ViewMode.CONVERSATION_LIST:
Vikram Aggarwalad8b99b2013-04-07 16:37:28 -0700447 // Show compose and search based on the account
Mindy Pereira68f2e222012-03-07 10:36:54 -0800448 // The only option that needs to be disabled is search
Mindy Pereira863e4412012-03-23 12:21:38 -0700449 Utils.setMenuItemVisibility(menu, R.id.search,
Mindy Pereira9b623802012-03-07 17:15:49 -0800450 mAccount.supportsCapability(AccountCapabilities.FOLDER_SERVER_SEARCH));
Mindy Pereira68f2e222012-03-07 10:36:54 -0800451 break;
Vikram Aggarwalad8b99b2013-04-07 16:37:28 -0700452 case ViewMode.SEARCH_RESULTS_LIST:
Vikram Aggarwal4bfad142013-05-07 17:12:46 -0700453 // Hide compose and search
454 Utils.setMenuItemVisibility(menu, R.id.compose, false);
Vikram Aggarwalad8b99b2013-04-07 16:37:28 -0700455 Utils.setMenuItemVisibility(menu, R.id.search, false);
456 break;
Mindy Pereira68f2e222012-03-07 10:36:54 -0800457 }
Scott Kennedya85831d2013-04-12 15:54:22 -0700458
459 return false;
460 }
461
Scott Kennedy26f2b302013-04-22 17:47:17 -0700462 /**
463 * Reorders the specified {@link Menu}, taking into account the user's Archive/Delete
464 * preference.
465 */
Scott Kennedyb905d082013-05-07 16:44:34 -0700466 public static void reorderMenu(final Context context, final Account account, final Menu menu,
467 final int maxItems) {
Scott Kennedyd4796d72013-05-03 19:49:32 -0700468 final String removalAction = MailPrefs.get(context).getRemovalAction(
469 account.supportsCapability(AccountCapabilities.ARCHIVE));
Andrew Sapperstein43a1cc12013-05-03 10:21:02 -0700470 final boolean showArchive = MailPrefs.RemovalActions.ARCHIVE.equals(removalAction) ||
471 MailPrefs.RemovalActions.ARCHIVE_AND_DELETE.equals(removalAction);
472 final boolean showDelete = MailPrefs.RemovalActions.DELETE.equals(removalAction) ||
473 MailPrefs.RemovalActions.ARCHIVE_AND_DELETE.equals(removalAction);
Scott Kennedya85831d2013-04-12 15:54:22 -0700474
Scott Kennedyb905d082013-05-07 16:44:34 -0700475 // Do a first pass to extract necessary information on what is safe to display
Scott Kennedyccd3ce22013-04-26 10:34:29 -0700476 boolean archiveVisibleEnabled = false;
477 boolean deleteVisibleEnabled = false;
478 for (int i = 0; i < menu.size(); i++) {
479 final MenuItem menuItem = menu.getItem(i);
480 final int itemId = menuItem.getItemId();
481 final boolean visible = menuItem.isVisible();
482 final boolean enabled = menuItem.isEnabled();
483
484 if (itemId == R.id.archive || itemId == R.id.remove_folder) {
485 archiveVisibleEnabled |= (visible & enabled);
486 } else if (itemId == R.id.delete || itemId == R.id.discard_drafts) {
487 deleteVisibleEnabled |= (visible & enabled);
488 }
489 }
490
Scott Kennedyb905d082013-05-07 16:44:34 -0700491 int actionItems = 0;
492
Scott Kennedya85831d2013-04-12 15:54:22 -0700493 for (int i = 0; i < menu.size(); i++) {
494 final MenuItem menuItem = menu.getItem(i);
Scott Kennedy26f2b302013-04-22 17:47:17 -0700495 final int itemId = menuItem.getItemId();
Scott Kennedya85831d2013-04-12 15:54:22 -0700496
Scott Kennedyb905d082013-05-07 16:44:34 -0700497 // We only want to promote it if it's visible and has an icon
498 if (menuItem.isVisible() && menuItem.getIcon() != null) {
499 switch (itemId) {
500 case R.id.archive:
501 case R.id.remove_folder:
502 /*
503 * If this is disabled, and we want to show both archive and delete, we will
504 * hide archive (rather than showing it disabled), and take up one of our
505 * spaces. If we only want to show archive, we'll hide it, but not take up
506 * a space.
507 */
508 if (!menuItem.isEnabled() && showArchive) {
509 menuItem.setVisible(false);
Andrew Sapperstein43a1cc12013-05-03 10:21:02 -0700510
Scott Kennedyb905d082013-05-07 16:44:34 -0700511 if (showDelete) {
512 actionItems++;
513 }
Andrew Sapperstein43a1cc12013-05-03 10:21:02 -0700514
Scott Kennedyb905d082013-05-07 16:44:34 -0700515 break;
516 }
Scott Kennedyef91f8b2013-05-03 19:42:53 -0700517
Scott Kennedyb905d082013-05-07 16:44:34 -0700518 /*
519 * We show this if the following are all true:
520 * 1. The user wants to display archive, or delete is not visible
521 * 2. We have room for it
522 */
523 if ((showArchive || !deleteVisibleEnabled) && actionItems < maxItems) {
524 menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
525 actionItems++;
526 }
527 break;
528 case R.id.delete:
529 case R.id.discard_drafts:
530 /*
531 * We show this if the following are all true:
532 * 1. The user wants to display delete, or archive is not visible
533 * 2. We have room for it
534 */
535 if ((showDelete || !archiveVisibleEnabled) && actionItems < maxItems) {
536 menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
537 actionItems++;
538 }
539 break;
540 case R.id.change_folder:
541 final boolean showChangeFolder = account
542 .supportsCapability(AccountCapabilities.MULTIPLE_FOLDERS_PER_CONV);
543 menuItem.setVisible(showChangeFolder);
544
545 if (showChangeFolder && actionItems < maxItems) {
546 menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
547 actionItems++;
548 }
549 break;
550 case R.id.search:
551 menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS
552 | MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
553 actionItems++;
554 break;
555 default:
556 if (actionItems < maxItems) {
557 menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
558 actionItems++;
559 }
560 break;
561 }
Scott Kennedyef91f8b2013-05-03 19:42:53 -0700562 }
Scott Kennedya85831d2013-04-12 15:54:22 -0700563 }
Mindy Pereira68f2e222012-03-07 10:36:54 -0800564 }
Vikram Aggarwal5e5ac742011-12-19 08:14:16 -0800565
Vikram Aggarwalabd24d82012-04-26 13:23:14 -0700566 /**
Andrew Sapperstein5747e152013-05-13 14:13:08 -0700567 * Hides menu items while the drawer is open.
568 */
569 private void setMenuItemsToHiddenForOpenDrawer(Menu menu) {
570 final int size = menu.size();
571
572 for (int i = 0; i < size; i++) {
573 final MenuItem item = menu.getItem(i);
574
575 final int id = item.getItemId();
576 item.setVisible(id == R.id.settings ||
577 id == R.id.feedback_menu_item ||
578 id == R.id.help_info_menu_item);
579 }
580 }
581
582 /**
Vikram Aggarwalad8b99b2013-04-07 16:37:28 -0700583 * Put the ActionBar in List navigation mode.
Vikram Aggarwalabd24d82012-04-26 13:23:14 -0700584 */
Mindy Pereira72a9f392012-03-21 09:41:09 -0700585 private void showNavList() {
Paul Westbrook8c887ef2013-04-24 00:29:28 -0700586 setTitleModeFlags(getActionBarTitleModeFlag());
Vikram Aggarwalb642b312013-04-24 18:41:40 -0700587 setFolderAndAccount(false);
Mindy Pereira72a9f392012-03-21 09:41:09 -0700588 }
589
Vikram Aggarwalabd24d82012-04-26 13:23:14 -0700590 /**
Vikram Aggarwalad8b99b2013-04-07 16:37:28 -0700591 * Put the ActionBar in standard mode, and show the word "Folders" along with the account name
592 * (if user has multiple accounts)
Vikram Aggarwalabd24d82012-04-26 13:23:14 -0700593 */
Andy Huangaf65e732013-01-28 20:45:40 -0800594 private void setFoldersMode() {
Paul Westbrook8c887ef2013-04-24 00:29:28 -0700595 setTitle(R.string.folders);
Paul Westbrook8c887ef2013-04-24 00:29:28 -0700596 setSubtitle(mAccount.name);
Vikram Aggarwalb642b312013-04-24 18:41:40 -0700597 setTitleModeFlags(ActionBar.DISPLAY_SHOW_TITLE);
Paul Westbrook8c887ef2013-04-24 00:29:28 -0700598 }
599
600 private void setSubtitle(CharSequence subtitle) {
601 mActionBar.setSubtitle(subtitle);
602 if (mLegacySubTitle != null) {
603 mLegacySubTitle.setText(subtitle);
604 }
605 }
606
607 private void setTitle(int res) {
608 mActionBar.setTitle(res);
609 if (mLegacyTitle != null) {
610 mLegacyTitle.setText(res);
611 }
612 }
613
614 private void setTitle(CharSequence title) {
615 mActionBar.setTitle(title);
616 if (mLegacyTitle != null) {
617 mLegacyTitle.setText(title);
618 }
619 }
620
621 private int getActionBarTitleModeFlag() {
622 return mUseLegacyTitle ? ActionBar.DISPLAY_SHOW_CUSTOM : ActionBar.DISPLAY_SHOW_TITLE;
mindyp6fb1b622012-09-05 09:27:08 -0700623 }
624
625 /**
Vikram Aggarwalfd7eaba2013-04-11 12:31:46 -0700626 * Set the actionbar mode to empty: no title, no subtitle, no custom view.
mindyp6fb1b622012-09-05 09:27:08 -0700627 */
628 protected void setEmptyMode() {
Vikram Aggarwalfd7eaba2013-04-11 12:31:46 -0700629 // Disable title/subtitle and the custom view by setting the bitmask to all off.
630 setTitleModeFlags(0);
Vikram Aggarwalabd24d82012-04-26 13:23:14 -0700631 }
632
Vikram Aggarwalad8b99b2013-04-07 16:37:28 -0700633 /**
634 * Removes the back button from being shown
635 */
Mindy Pereira68f2e222012-03-07 10:36:54 -0800636 public void removeBackButton() {
637 if (mActionBar == null) {
638 return;
639 }
Vikram Aggarwal649b9ea2012-08-27 12:15:20 -0700640 // Remove the back button but continue showing an icon.
641 final int mask = ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME;
642 mActionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME, mask);
Mindy Pereira68f2e222012-03-07 10:36:54 -0800643 mActivity.getActionBar().setHomeButtonEnabled(false);
644 }
Mindy Pereirabc57bf12012-02-29 14:39:09 -0800645
Mindy Pereira68f2e222012-03-07 10:36:54 -0800646 public void setBackButton() {
Andy Huang12b3ee42013-04-24 22:49:43 -0700647 if (mActionBar == null) {
Mindy Pereira68f2e222012-03-07 10:36:54 -0800648 return;
649 }
Vikram Aggarwal649b9ea2012-08-27 12:15:20 -0700650 // Show home as up, and show an icon.
651 final int mask = ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME;
652 mActionBar.setDisplayOptions(mask, mask);
Mindy Pereira68f2e222012-03-07 10:36:54 -0800653 mActivity.getActionBar().setHomeButtonEnabled(true);
654 }
Vikram Aggarwal5e5ac742011-12-19 08:14:16 -0800655
Mindy Pereira68f2e222012-03-07 10:36:54 -0800656 @Override
657 public boolean onQueryTextSubmit(String query) {
Mindy Pereira4ce59942012-03-08 09:57:35 -0800658 if (mSearch != null) {
659 mSearch.collapseActionView();
660 mSearchWidget.setQuery("", false);
661 }
Andy Huang313ac132013-03-04 23:40:58 -0800662 mController.executeSearch(query.trim());
Mindy Pereira68f2e222012-03-07 10:36:54 -0800663 return true;
664 }
Vikram Aggarwal5e5ac742011-12-19 08:14:16 -0800665
Mindy Pereira68f2e222012-03-07 10:36:54 -0800666 @Override
667 public boolean onQueryTextChange(String newText) {
Mindy Pereira68f2e222012-03-07 10:36:54 -0800668 return false;
669 }
Mindy Pereira9b623802012-03-07 17:15:49 -0800670
Mindy Pereira641de652012-08-02 15:21:50 -0700671 private boolean setRefreshInProgress(boolean inProgress) {
Mindy Pereira9b623802012-03-07 17:15:49 -0800672 if (inProgress != mRefreshInProgress) {
673 mRefreshInProgress = inProgress;
674 if (mSearch == null || !mSearch.isActionViewExpanded()) {
675 mHandler.post(mInvalidateMenu);
676 }
677 return true;
678 }
679 return false;
680 }
681
Mindy Pereira641de652012-08-02 15:21:50 -0700682 private void onRefreshStarted() {
Mindy Pereira9b623802012-03-07 17:15:49 -0800683 setRefreshInProgress(true);
684 }
685
Vikram Aggarwal41b9e8f2012-09-25 10:15:04 -0700686 private void onRefreshStopped() {
Paul Westbrook4f393ec2012-08-10 09:19:50 -0700687 setRefreshInProgress(false);
Mindy Pereira9b623802012-03-07 17:15:49 -0800688 }
Mindy Pereirafd0c2972012-03-27 13:50:39 -0700689
Vikram Aggarwal12a7c042012-03-28 16:48:08 -0700690 // Next two methods are called when search suggestions are clicked.
691 @Override
692 public boolean onSuggestionSelect(int position) {
693 return onSuggestionClick(position);
694 }
695
696 @Override
697 public boolean onSuggestionClick(int position) {
698 final Cursor c = mSearchWidget.getSuggestionsAdapter().getCursor();
699 final boolean haveValidQuery = (c != null) && c.moveToPosition(position);
700 if (!haveValidQuery) {
701 LogUtils.d(LOG_TAG, "onSuggestionClick: Couldn't get a search query");
Mindy Pereiraf8fcb802012-06-25 11:43:06 -0700702 // We haven't handled this query, but the default behavior will
703 // leave EXTRA_ACCOUNT un-populated, leading to a crash. So claim
704 // that we have handled the event.
Vikram Aggarwal12a7c042012-03-28 16:48:08 -0700705 return true;
706 }
Mindy Pereirab466bcf2012-06-14 15:52:26 -0700707 collapseSearch();
Mindy Pereira1e180dd2012-08-16 10:24:10 -0700708 // what is in the text field
Mindy Pereiraca97b312012-06-25 14:00:20 -0700709 String queryText = mSearchWidget.getQuery().toString();
Mindy Pereira1e180dd2012-08-16 10:24:10 -0700710 // What the suggested query is
Mindy Pereiraf8fcb802012-06-25 11:43:06 -0700711 String query = c.getString(c.getColumnIndex(SearchManager.SUGGEST_COLUMN_QUERY));
mindyp0fc8fa52012-09-04 10:43:23 -0700712 // If the text the user typed in is a prefix of what is in the search
713 // widget suggestion query, just take the search widget suggestion
714 // query. Otherwise, it is a suffix and we want to remove matching
715 // prefix portions.
716 if (!TextUtils.isEmpty(queryText) && query.indexOf(queryText) != 0) {
Mindy Pereira5e1403c2012-07-18 13:22:11 -0700717 final int queryTokenIndex = queryText
718 .lastIndexOf(SearchRecentSuggestionsProvider.QUERY_TOKEN_SEPARATOR);
719 if (queryTokenIndex > -1) {
720 queryText = queryText.substring(0, queryTokenIndex);
Mindy Pereiraca97b312012-06-25 14:00:20 -0700721 }
722 // Since we auto-complete on each token in a query, if the query the
723 // user typed up until the last token is a substring of the
724 // suggestion they click, make sure we don't double include the
725 // query text. For example:
726 // user types john, that matches john palo alto
727 // User types john p, that matches john john palo alto
728 // Remove the first john
Mindy Pereira5e1403c2012-07-18 13:22:11 -0700729 // Only do this if we have multiple query tokens.
730 if (queryTokenIndex > -1 && !TextUtils.isEmpty(query) && query.contains(queryText)
Mindy Pereiraca97b312012-06-25 14:00:20 -0700731 && queryText.length() < query.length()) {
732 int start = query.indexOf(queryText);
733 query = query.substring(0, start) + query.substring(start + queryText.length());
734 }
735 }
Andy Huang313ac132013-03-04 23:40:58 -0800736 mController.executeSearch(query.trim());
Vikram Aggarwal12a7c042012-03-28 16:48:08 -0700737 return true;
738 }
Mindy Pereiraa46a57f2012-04-03 17:19:31 -0700739
Vikram Aggarwalf642b812013-03-15 16:30:21 -0700740 /**
741 * Uses the current state to update the current folder {@link #mFolder} and the current
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700742 * account {@link #mAccount} shown in the actionbar. Also updates the actionbar subtitle to
743 * momentarily display the unread count if it has changed.
Vikram Aggarwalf18b1172013-04-17 12:36:23 -0700744 * @param folderChanged true if folder changed in terms of URI
Vikram Aggarwalf642b812013-03-15 16:30:21 -0700745 */
Vikram Aggarwalf18b1172013-04-17 12:36:23 -0700746 private void setFolderAndAccount(final boolean folderChanged) {
Vikram Aggarwal5fce9922013-04-01 21:17:42 -0700747 // Very little can be done if the actionbar or activity is null.
748 if (mActionBar == null || mActivity == null) {
749 return;
750 }
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700751 if (ViewMode.isWaitingForSync(mMode)) {
752 // Account is not synced: clear title and update the subtitle.
Paul Westbrook8c887ef2013-04-24 00:29:28 -0700753 setTitle("");
Vikram Aggarwal9dcd4832013-05-09 16:08:59 -0700754 removeUnreadCount(true);
Vikram Aggarwalf642b812013-03-15 16:30:21 -0700755 return;
756 }
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700757 // Check if we should be changing the actionbar at all, and back off if not.
758 final boolean isShowingFolder = mIsOnTablet || ViewMode.isListMode(mMode);
759 if (!isShowingFolder) {
Paul Westbrook91024952013-04-21 15:59:06 -0700760 // It isn't necessary to set the title in this case, as the title view will
761 // be hidden
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700762 return;
Vikram Aggarwalf642b812013-03-15 16:30:21 -0700763 }
Vikram Aggarwal26b0bfd2013-03-29 13:05:08 -0700764 if (mFolder == null) {
Paul Westbrook91024952013-04-21 15:59:06 -0700765 // Clear the action bar title. We don't want the app name to be shown while
766 // waiting for the folder query to finish
Paul Westbrook8c887ef2013-04-24 00:29:28 -0700767 setTitle("");
Vikram Aggarwal26b0bfd2013-03-29 13:05:08 -0700768 return;
769 }
Paul Westbrook8c887ef2013-04-24 00:29:28 -0700770 setTitle(mFolder.name);
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700771
Vikram Aggarwalf18b1172013-04-17 12:36:23 -0700772 final int folderUnreadCount = mFolder.isUnreadCountHidden() ? 0 : mFolder.unreadCount;
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700773 // The user shouldn't see "999+ unread messages", and then a short while later: "999+
774 // unread messages". So we set our unread count just past the limit. This way we can
775 // change the subtitle the first time around but not for subsequent changes as far as the
776 // unread count remains over the limit.
Vikram Aggarwalf18b1172013-04-17 12:36:23 -0700777 final int toDisplay = (folderUnreadCount > UNREAD_LIMIT)
778 ? (UNREAD_LIMIT + 1) : folderUnreadCount;
Vikram Aggarwalc590ba82013-05-02 11:28:05 -0700779 if ((mUnreadCount != toDisplay || folderChanged) && toDisplay != 0) {
Vikram Aggarwal9dcd4832013-05-09 16:08:59 -0700780 setSubtitle(Utils.getUnreadMessageString(mActivity.getApplicationContext(), toDisplay));
Vikram Aggarwal26b0bfd2013-03-29 13:05:08 -0700781 }
Vikram Aggarwal055b24b2013-05-10 15:14:20 -0700782 // Schedule a removal of unread count for the future, if there isn't one already. If the
783 // unread count dropped to zero, remove it and show the account name right away.
784 removeUnreadCount(toDisplay == 0);
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700785 // Remember the new value for the next run
Vikram Aggarwalf18b1172013-04-17 12:36:23 -0700786 mUnreadCount = toDisplay;
787 }
788
789 /**
790 * Remove the unread count and show the account name, if required.
Vikram Aggarwal9dcd4832013-05-09 16:08:59 -0700791 * @param now true if you want the change to happen immediately. False if you want to enforce
792 * it happens later.
Vikram Aggarwalf18b1172013-04-17 12:36:23 -0700793 */
Vikram Aggarwal9dcd4832013-05-09 16:08:59 -0700794 private void removeUnreadCount(boolean now) {
795 if (now) {
796 // Remove all previous messages which might change the subtitle
797 mHandler.removeMessages(SubtitleHandler.EMAIL);
798 // Update the subtitle: clear it or show account name.
799 mHandler.sendEmptyMessage(SubtitleHandler.EMAIL);
800 } else {
801 if (!mHandler.hasMessages(SubtitleHandler.EMAIL)) {
802 // In a short while, show the account name in its place.
803 mHandler.sendEmptyMessageDelayed(SubtitleHandler.EMAIL, ACCOUNT_DELAY_MS);
804 }
805 }
Vikram Aggarwalbc462ca2013-03-15 10:41:03 -0700806 }
807
Mindy Pereiraa46a57f2012-04-03 17:19:31 -0700808 /**
809 * Notify that the folder has changed.
810 */
811 public void onFolderUpdated(Folder folder) {
Vikram Aggarwal26b0bfd2013-03-29 13:05:08 -0700812 if (folder == null) {
813 return;
814 }
Vikram Aggarwale875b662013-04-10 15:56:46 -0700815 /** True if we are changing folders. */
816 final boolean changingFolders = (mFolder == null || !mFolder.uri.equals(folder.uri));
Vikram Aggarwalc18c3382013-04-05 15:29:13 -0700817 mFolder = folder;
Vikram Aggarwal75d1bb12013-04-12 17:17:00 -0700818 setFolderAndAccount(changingFolders);
Mindy Pereira641de652012-08-02 15:21:50 -0700819 if (folder.isSyncInProgress()) {
820 onRefreshStarted();
821 } else {
822 // Stop the spinner here.
Vikram Aggarwal41b9e8f2012-09-25 10:15:04 -0700823 onRefreshStopped();
Mindy Pereira641de652012-08-02 15:21:50 -0700824 }
Vikram Aggarwal49499e62013-02-08 16:39:08 -0800825 final ConversationListContext listContext = mController == null ? null :
826 mController.getCurrentListContext();
Vikram Aggarwale875b662013-04-10 15:56:46 -0700827 if (changingFolders && !ConversationListContext.isSearchResult(listContext)) {
Vikram Aggarwal49499e62013-02-08 16:39:08 -0800828 closeSearchField();
829 }
Mindy Pereiraa46a57f2012-04-03 17:19:31 -0700830 }
Vikram Aggarwal0dda5732012-04-06 11:20:16 -0700831
832 @Override
833 public boolean onMenuItemActionExpand(MenuItem item) {
834 // Do nothing. Required as part of the interface, we ar only interested in
835 // onMenuItemActionCollapse(MenuItem).
836 // Have to return true here. Unlike other callbacks, the return value here is whether
837 // we want to suppress the action (rather than consume the action). We don't want to
838 // suppress the action.
839 return true;
840 }
841
842 @Override
843 public boolean onMenuItemActionCollapse(MenuItem item) {
Mindy Pereirab466bcf2012-06-14 15:52:26 -0700844 // Work around b/6664203 by manually forcing this view to be VISIBLE
845 // upon ActionView collapse. DISPLAY_SHOW_CUSTOM will still control its final
846 // visibility.
Andy Huang5895f7b2012-06-01 17:07:20 -0700847 setVisibility(VISIBLE);
Mindy Pereirab466bcf2012-06-14 15:52:26 -0700848 // Have to return true here. Unlike other callbacks, the return value
849 // here is whether we want to suppress the action (rather than consume the action). We
850 // don't want to suppress the action.
Vikram Aggarwal0dda5732012-04-06 11:20:16 -0700851 return true;
852 }
Vikram Aggarwal37263972012-04-17 15:51:14 -0700853
Vikram Aggarwalfd7eaba2013-04-11 12:31:46 -0700854 /**
855 * Sets the actionbar mode: Pass it an integer which contains each of these values, perhaps
Paul Westbrook8c887ef2013-04-24 00:29:28 -0700856 * OR'd together: {@link ActionBar#DISPLAY_SHOW_CUSTOM} and
857 * {@link ActionBar#DISPLAY_SHOW_TITLE}. To disable all, pass a zero.
Vikram Aggarwalfd7eaba2013-04-11 12:31:46 -0700858 * @param enabledFlags
859 */
Andy Huangaf65e732013-01-28 20:45:40 -0800860 private void setTitleModeFlags(int enabledFlags) {
Paul Westbrook8c887ef2013-04-24 00:29:28 -0700861 final int mask = ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_CUSTOM;
Andy Huangaf65e732013-01-28 20:45:40 -0800862 mActionBar.setDisplayOptions(enabledFlags, mask);
863 }
864
Yorke Leef807ba72012-09-20 17:18:05 -0700865 public void setCurrentConversation(Conversation conversation) {
866 mCurrentConversation = conversation;
867 }
868
869 //We need to do this here instead of in the fragment
870 public void setConversationModeOptions(Menu menu) {
871 if (mCurrentConversation == null) {
872 return;
873 }
874 final boolean showMarkImportant = !mCurrentConversation.isImportant();
875 Utils.setMenuItemVisibility(menu, R.id.mark_important, showMarkImportant
876 && mAccount.supportsCapability(UIProvider.AccountCapabilities.MARK_IMPORTANT));
877 Utils.setMenuItemVisibility(menu, R.id.mark_not_important, !showMarkImportant
878 && mAccount.supportsCapability(UIProvider.AccountCapabilities.MARK_IMPORTANT));
879 final boolean showDelete = mFolder != null &&
880 mFolder.supportsCapability(UIProvider.FolderCapabilities.DELETE);
881 Utils.setMenuItemVisibility(menu, R.id.delete, showDelete);
882 // We only want to show the discard drafts menu item if we are not showing the delete menu
883 // item, and the current folder is a draft folder and the account supports discarding
884 // drafts for a conversation
885 final boolean showDiscardDrafts = !showDelete && mFolder != null && mFolder.isDraft() &&
886 mAccount.supportsCapability(AccountCapabilities.DISCARD_CONVERSATION_DRAFTS);
887 Utils.setMenuItemVisibility(menu, R.id.discard_drafts, showDiscardDrafts);
888 final boolean archiveVisible = mAccount.supportsCapability(AccountCapabilities.ARCHIVE)
889 && mFolder != null && mFolder.supportsCapability(FolderCapabilities.ARCHIVE)
890 && !mFolder.isTrash();
891 Utils.setMenuItemVisibility(menu, R.id.archive, archiveVisible);
892 Utils.setMenuItemVisibility(menu, R.id.remove_folder, !archiveVisible && mFolder != null
893 && mFolder.supportsCapability(FolderCapabilities.CAN_ACCEPT_MOVED_MESSAGES)
894 && !mFolder.isProviderFolder());
Scott Kennedy28f3d5c2013-04-08 16:53:50 -0700895 Utils.setMenuItemVisibility(menu, R.id.move_to, mFolder != null
896 && mFolder.supportsCapability(FolderCapabilities.ALLOWS_REMOVE_CONVERSATION));
Scott Kennedydd2ec682013-06-03 19:16:13 -0700897 Utils.setMenuItemVisibility(menu, R.id.move_to_inbox, mFolder != null
898 && mFolder.supportsCapability(FolderCapabilities.ALLOWS_MOVE_TO_INBOX));
899
Yorke Leef807ba72012-09-20 17:18:05 -0700900 final MenuItem removeFolder = menu.findItem(R.id.remove_folder);
Alice Yang40698e62013-03-14 11:31:53 -0700901 if (mFolder != null && removeFolder != null) {
Yorke Leef807ba72012-09-20 17:18:05 -0700902 removeFolder.setTitle(mActivity.getApplicationContext().getString(
903 R.string.remove_folder, mFolder.name));
904 }
905 Utils.setMenuItemVisibility(menu, R.id.report_spam,
906 mAccount.supportsCapability(AccountCapabilities.REPORT_SPAM) && mFolder != null
907 && mFolder.supportsCapability(FolderCapabilities.REPORT_SPAM)
908 && !mCurrentConversation.spam);
909 Utils.setMenuItemVisibility(menu, R.id.mark_not_spam,
910 mAccount.supportsCapability(AccountCapabilities.REPORT_SPAM) && mFolder != null
911 && mFolder.supportsCapability(FolderCapabilities.MARK_NOT_SPAM)
912 && mCurrentConversation.spam);
913 Utils.setMenuItemVisibility(menu, R.id.report_phishing,
914 mAccount.supportsCapability(AccountCapabilities.REPORT_PHISHING) && mFolder != null
915 && mFolder.supportsCapability(FolderCapabilities.REPORT_PHISHING)
916 && !mCurrentConversation.phishing);
917 Utils.setMenuItemVisibility(menu, R.id.mute,
918 mAccount.supportsCapability(AccountCapabilities.MUTE) && mFolder != null
919 && mFolder.supportsCapability(FolderCapabilities.DESTRUCTIVE_MUTE)
920 && !mCurrentConversation.muted);
921 }
Paul Westbrook8c887ef2013-04-24 00:29:28 -0700922
923 private static boolean actionBarSupportsNewMethods(ActionBar bar) {
924 // TODO(pwestbro) switch this to
925 // (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) when we switch to the
926 // latest SDK
927 if (Build.VERSION.SDK_INT > 17) {
928 return true;
929 }
930 if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) {
931 return false;
932 }
933 boolean supportsNewApi = false;
934 try {
935 if (bar != null) {
936 supportsNewApi = (ActionBar.class.getField("DISPLAY_TITLE_MULTIPLE_LINES") != null);
937 }
938 } catch (NoSuchFieldException e) {
939 // stay false
940 }
941 return supportsNewApi;
942 }
943
944 @Override
945 public void onClick (View v) {
946 if (v.getId() == R.id.legacy_title_container) {
947 mController.onUpPressed();
948 }
949 }
Vikram Aggarwal5e5ac742011-12-19 08:14:16 -0800950}