Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2012 Google Inc. |
| 3 | * Licensed to The Android Open Source Project. |
Vikram Aggarwal | 5e5ac74 | 2011-12-19 08:14:16 -0800 | [diff] [blame] | 4 | * |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 5 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | * you may not use this file except in compliance with the License. |
| 7 | * You may obtain a copy of the License at |
Vikram Aggarwal | 5e5ac74 | 2011-12-19 08:14:16 -0800 | [diff] [blame] | 8 | * |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 9 | * http://www.apache.org/licenses/LICENSE-2.0 |
Vikram Aggarwal | 5e5ac74 | 2011-12-19 08:14:16 -0800 | [diff] [blame] | 10 | * |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 11 | * Unless required by applicable law or agreed to in writing, software |
| 12 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | * See the License for the specific language governing permissions and |
| 15 | * limitations under the License. |
| 16 | */ |
Vikram Aggarwal | 5e5ac74 | 2011-12-19 08:14:16 -0800 | [diff] [blame] | 17 | |
Vikram Aggarwal | 1ddcf0f | 2012-01-13 11:45:02 -0800 | [diff] [blame] | 18 | package com.android.mail.ui; |
Vikram Aggarwal | 5e5ac74 | 2011-12-19 08:14:16 -0800 | [diff] [blame] | 19 | |
| 20 | import android.app.ActionBar; |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 21 | import android.app.SearchManager; |
| 22 | import android.app.SearchableInfo; |
Scott Banachowski | 53a615d | 2012-12-19 15:31:20 -0800 | [diff] [blame] | 23 | import android.content.ContentResolver; |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 24 | import android.content.Context; |
Vikram Aggarwal | bcb16b9 | 2013-01-28 18:05:03 -0800 | [diff] [blame] | 25 | import android.content.res.Resources; |
Vikram Aggarwal | 12a7c04 | 2012-03-28 16:48:08 -0700 | [diff] [blame] | 26 | import android.database.Cursor; |
Vikram Aggarwal | a51ef9f | 2013-04-02 13:25:35 -0700 | [diff] [blame] | 27 | import android.net.Uri; |
| 28 | import android.os.AsyncTask; |
Vikram Aggarwal | 5e5ac74 | 2011-12-19 08:14:16 -0800 | [diff] [blame] | 29 | import android.os.Bundle; |
Mindy Pereira | 9b62380 | 2012-03-07 17:15:49 -0800 | [diff] [blame] | 30 | import android.os.Handler; |
Alice Yang | 48706db | 2013-02-01 17:56:52 -0800 | [diff] [blame] | 31 | import android.text.SpannableString; |
| 32 | import android.text.SpannableStringBuilder; |
Mindy Pereira | 0531c9f | 2012-06-25 14:12:59 -0700 | [diff] [blame] | 33 | import android.text.TextUtils; |
Alice Yang | 48706db | 2013-02-01 17:56:52 -0800 | [diff] [blame] | 34 | import android.text.style.TextAppearanceSpan; |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 35 | import android.util.AttributeSet; |
Vikram Aggarwal | 5e5ac74 | 2011-12-19 08:14:16 -0800 | [diff] [blame] | 36 | import android.view.Menu; |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 37 | import android.view.MenuItem; |
| 38 | import android.view.View; |
| 39 | import android.widget.LinearLayout; |
| 40 | import android.widget.SearchView; |
| 41 | import android.widget.SearchView.OnQueryTextListener; |
Vikram Aggarwal | 12a7c04 | 2012-03-28 16:48:08 -0700 | [diff] [blame] | 42 | import android.widget.SearchView.OnSuggestionListener; |
Vikram Aggarwal | 26b0bfd | 2013-03-29 13:05:08 -0700 | [diff] [blame] | 43 | import android.widget.TextView; |
Vikram Aggarwal | a9b93f3 | 2012-02-23 14:51:58 -0800 | [diff] [blame] | 44 | |
Alice Yang | 760d956 | 2013-04-02 13:13:24 -0700 | [diff] [blame] | 45 | import com.android.mail.ConversationListContext; |
| 46 | import com.android.mail.R; |
| 47 | import com.android.mail.browse.SnippetTextView; |
| 48 | import com.android.mail.providers.Account; |
| 49 | import com.android.mail.providers.AccountObserver; |
| 50 | import com.android.mail.providers.AllAccountObserver; |
| 51 | import com.android.mail.providers.Conversation; |
| 52 | import com.android.mail.providers.Folder; |
| 53 | import com.android.mail.providers.FolderObserver; |
| 54 | import com.android.mail.providers.SearchRecentSuggestionsProvider; |
| 55 | import com.android.mail.providers.UIProvider; |
| 56 | import com.android.mail.providers.UIProvider.AccountCapabilities; |
| 57 | import com.android.mail.providers.UIProvider.FolderCapabilities; |
| 58 | import com.android.mail.utils.LogTag; |
| 59 | import com.android.mail.utils.LogUtils; |
| 60 | import com.android.mail.utils.Utils; |
| 61 | |
Vikram Aggarwal | 5e5ac74 | 2011-12-19 08:14:16 -0800 | [diff] [blame] | 62 | /** |
Andy Huang | 6681e54 | 2012-06-14 14:36:45 -0700 | [diff] [blame] | 63 | * 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 Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 66 | * |
Vikram Aggarwal | 5e5ac74 | 2011-12-19 08:14:16 -0800 | [diff] [blame] | 67 | */ |
mindyp | 6fb1b62 | 2012-09-05 09:27:08 -0700 | [diff] [blame] | 68 | public class MailActionBarView extends LinearLayout implements ViewMode.ModeChangeListener, |
| 69 | OnQueryTextListener, OnSuggestionListener, MenuItem.OnActionExpandListener, |
mindyp | 26760ef | 2012-09-05 14:15:41 -0700 | [diff] [blame] | 70 | SubjectDisplayChanger { |
Andy Huang | af65e73 | 2013-01-28 20:45:40 -0800 | [diff] [blame] | 71 | |
Alice Yang | 15dfaa6 | 2013-02-13 15:37:42 -0800 | [diff] [blame] | 72 | // This is a private setting available starting JB MR1.1. |
Andy Huang | af65e73 | 2013-01-28 20:45:40 -0800 | [diff] [blame] | 73 | private static final int DISPLAY_TITLE_MULTIPLE_LINES = 0x20; |
| 74 | |
Vikram Aggarwal | 19b0e3c | 2013-03-22 15:35:20 -0700 | [diff] [blame] | 75 | /** True if we want the subject in the actionbar */ |
| 76 | public static final boolean SHOW_ACTIONBAR_SUBJECT = true; |
| 77 | |
Mindy Pereira | edb135e | 2012-06-15 08:34:09 -0700 | [diff] [blame] | 78 | protected ActionBar mActionBar; |
Vikram Aggarwal | 7c401b7 | 2012-08-13 16:43:47 -0700 | [diff] [blame] | 79 | protected ControllableActivity mActivity; |
Mindy Pereira | edb135e | 2012-06-15 08:34:09 -0700 | [diff] [blame] | 80 | protected ActivityController mController; |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 81 | /** |
| 82 | * The current mode of the ActionBar. This references constants in {@link ViewMode} |
| 83 | */ |
| 84 | private int mMode = ViewMode.UNKNOWN; |
| 85 | |
| 86 | private MenuItem mSearch; |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 87 | /** |
| 88 | * The account currently being shown |
| 89 | */ |
| 90 | private Account mAccount; |
Marc Blank | edcab01 | 2012-04-04 12:42:38 -0700 | [diff] [blame] | 91 | /** |
| 92 | * The folder currently being shown |
| 93 | */ |
| 94 | private Folder mFolder; |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 95 | |
Andy Huang | 5895f7b | 2012-06-01 17:07:20 -0700 | [diff] [blame] | 96 | private SnippetTextView mSubjectView; |
Vikram Aggarwal | 81064f8 | 2013-04-02 13:25:35 -0700 | [diff] [blame] | 97 | /** The view that shows an unread count. */ |
Vikram Aggarwal | 26b0bfd | 2013-03-29 13:05:08 -0700 | [diff] [blame] | 98 | private TextView mUnreadView; |
| 99 | |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 100 | private SearchView mSearchWidget; |
| 101 | private MenuItem mHelpItem; |
Paul Westbrook | 517743e | 2012-03-22 10:40:46 -0700 | [diff] [blame] | 102 | private MenuItem mSendFeedbackItem; |
Mindy Pereira | 9b62380 | 2012-03-07 17:15:49 -0800 | [diff] [blame] | 103 | private MenuItem mRefreshItem; |
Marc Blank | edcab01 | 2012-04-04 12:42:38 -0700 | [diff] [blame] | 104 | private MenuItem mFolderSettingsItem; |
Mindy Pereira | 9b62380 | 2012-03-07 17:15:49 -0800 | [diff] [blame] | 105 | private View mRefreshActionView; |
Vikram Aggarwal | f642b81 | 2013-03-15 16:30:21 -0700 | [diff] [blame] | 106 | /** True if the current device is a tablet, false otherwise. */ |
| 107 | private boolean mIsOnTablet; |
Mindy Pereira | 9b62380 | 2012-03-07 17:15:49 -0800 | [diff] [blame] | 108 | private boolean mRefreshInProgress; |
Yorke Lee | f807ba7 | 2012-09-20 17:18:05 -0700 | [diff] [blame] | 109 | private Conversation mCurrentConversation; |
Vikram Aggarwal | 6b641ba | 2013-03-15 16:07:24 -0700 | [diff] [blame] | 110 | private AllAccountObserver mAllAccountObserver; |
| 111 | private boolean mHaveMultipleAccounts = false; |
Mindy Pereira | 9b62380 | 2012-03-07 17:15:49 -0800 | [diff] [blame] | 112 | |
Paul Westbrook | b334c90 | 2012-06-25 11:42:46 -0700 | [diff] [blame] | 113 | public static final String LOG_TAG = LogTag.getLogTag(); |
Andy Huang | 0d64735 | 2012-03-21 21:48:16 -0700 | [diff] [blame] | 114 | |
Mindy Pereira | 9b62380 | 2012-03-07 17:15:49 -0800 | [diff] [blame] | 115 | private final Handler mHandler = new Handler(); |
| 116 | private final Runnable mInvalidateMenu = new Runnable() { |
| 117 | @Override |
| 118 | public void run() { |
| 119 | mActivity.invalidateOptionsMenu(); |
| 120 | } |
| 121 | }; |
Andy Huang | 5895f7b | 2012-06-01 17:07:20 -0700 | [diff] [blame] | 122 | private final boolean mShowConversationSubject; |
Vikram Aggarwal | 50ff0e5 | 2013-03-14 13:58:02 -0700 | [diff] [blame] | 123 | private FolderObserver mFolderObserver; |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 124 | |
Vikram Aggarwal | a51ef9f | 2013-04-02 13:25:35 -0700 | [diff] [blame] | 125 | /** Updates the resolver and tells it the most recent account. */ |
| 126 | private final class UpdateProvider extends AsyncTask<Bundle, Void, Void> { |
| 127 | final Uri mAccount; |
| 128 | final ContentResolver mResolver; |
| 129 | public UpdateProvider(Uri account, ContentResolver resolver) { |
| 130 | mAccount = account; |
| 131 | mResolver = resolver; |
| 132 | } |
| 133 | |
| 134 | @Override |
| 135 | protected Void doInBackground(Bundle... params) { |
| 136 | mResolver.call(mAccount, UIProvider.AccountCallMethods.SET_CURRENT_ACCOUNT, |
| 137 | mAccount.toString(), params[0]); |
| 138 | return null; |
| 139 | } |
| 140 | } |
| 141 | |
Vikram Aggarwal | 7c401b7 | 2012-08-13 16:43:47 -0700 | [diff] [blame] | 142 | private final AccountObserver mAccountObserver = new AccountObserver() { |
| 143 | @Override |
| 144 | public void onChanged(Account newAccount) { |
Scott Banachowski | 53a615d | 2012-12-19 15:31:20 -0800 | [diff] [blame] | 145 | updateAccount(newAccount); |
Vikram Aggarwal | 7c401b7 | 2012-08-13 16:43:47 -0700 | [diff] [blame] | 146 | } |
| 147 | }; |
Andy Huang | af65e73 | 2013-01-28 20:45:40 -0800 | [diff] [blame] | 148 | |
Andy Huang | 9da91d3 | 2013-01-30 15:27:00 -0800 | [diff] [blame] | 149 | private final OnLayoutChangeListener mSnippetLayoutListener = new OnLayoutChangeListener() { |
| 150 | @Override |
| 151 | public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, |
| 152 | int oldTop, int oldRight, int oldBottom) { |
| 153 | v.removeOnLayoutChangeListener(this); |
| 154 | // We're in a layout, so try not to do anything layout-related here, and post a |
| 155 | // runnable instead. |
| 156 | v.post(new Runnable() { |
| 157 | @Override |
| 158 | public void run() { |
Alice Yang | 15dfaa6 | 2013-02-13 15:37:42 -0800 | [diff] [blame] | 159 | // Framework only started supporting multi-line action bars in JB MR1.1, |
| 160 | // so on earlier versions, always use our custom action bar. |
| 161 | if (actionBarReportsMultipleLineTitle(mActionBar)) { |
| 162 | // Switch over to title mode when the first fragment asks for a subject |
| 163 | // remainder. We assume that layout has happened by now, so the |
| 164 | // SnippetTextView already has measurements it needs to calculate |
| 165 | // remainders, and it's safe to switch over to TITLE mode to inherit |
| 166 | // standard system behaviors. |
| 167 | setTitleModeFlags(ActionBar.DISPLAY_SHOW_TITLE | |
| 168 | DISPLAY_TITLE_MULTIPLE_LINES); |
Andy Huang | 9da91d3 | 2013-01-30 15:27:00 -0800 | [diff] [blame] | 169 | |
Alice Yang | 15dfaa6 | 2013-02-13 15:37:42 -0800 | [diff] [blame] | 170 | // Work around a bug where the title's container is stuck GONE when a title |
| 171 | // is set while in CUSTOM mode. |
| 172 | mActionBar.setTitle(mActionBar.getTitle()); |
| 173 | } |
Andy Huang | 9da91d3 | 2013-01-30 15:27:00 -0800 | [diff] [blame] | 174 | } |
| 175 | }); |
| 176 | } |
| 177 | }; |
| 178 | |
Alice Yang | 15dfaa6 | 2013-02-13 15:37:42 -0800 | [diff] [blame] | 179 | private static boolean actionBarReportsMultipleLineTitle(ActionBar bar) { |
| 180 | boolean reports = false; |
| 181 | try { |
| 182 | if (bar != null) { |
| 183 | reports = (ActionBar.class.getField("DISPLAY_TITLE_MULTIPLE_LINES") != null); |
| 184 | } |
| 185 | } catch (NoSuchFieldException e) { |
| 186 | // stay false |
| 187 | } |
| 188 | return reports; |
| 189 | } |
| 190 | |
Vikram Aggarwal | dac89fa | 2013-03-05 16:43:09 -0800 | [diff] [blame] | 191 | // Created via view inflation. |
| 192 | @SuppressWarnings("unused") |
Andy Huang | 6681e54 | 2012-06-14 14:36:45 -0700 | [diff] [blame] | 193 | public MailActionBarView(Context context) { |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 194 | this(context, null); |
| 195 | } |
| 196 | |
Andy Huang | 6681e54 | 2012-06-14 14:36:45 -0700 | [diff] [blame] | 197 | public MailActionBarView(Context context, AttributeSet attrs) { |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 198 | this(context, attrs, 0); |
| 199 | } |
| 200 | |
Andy Huang | 6681e54 | 2012-06-14 14:36:45 -0700 | [diff] [blame] | 201 | public MailActionBarView(Context context, AttributeSet attrs, int defStyle) { |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 202 | super(context, attrs, defStyle); |
Vikram Aggarwal | bcb16b9 | 2013-01-28 18:05:03 -0800 | [diff] [blame] | 203 | final Resources r = getResources(); |
Vikram Aggarwal | 26b0bfd | 2013-03-29 13:05:08 -0700 | [diff] [blame] | 204 | mShowConversationSubject = SHOW_ACTIONBAR_SUBJECT |
| 205 | && r.getBoolean(R.bool.show_conversation_subject); |
Vikram Aggarwal | f642b81 | 2013-03-15 16:30:21 -0700 | [diff] [blame] | 206 | mIsOnTablet = Utils.useTabletUI(r); |
Andy Huang | 5895f7b | 2012-06-01 17:07:20 -0700 | [diff] [blame] | 207 | } |
| 208 | |
| 209 | @Override |
| 210 | protected void onFinishInflate() { |
| 211 | super.onFinishInflate(); |
Andy Huang | 5895f7b | 2012-06-01 17:07:20 -0700 | [diff] [blame] | 212 | mSubjectView = (SnippetTextView) findViewById(R.id.conversation_subject); |
Vikram Aggarwal | 26b0bfd | 2013-03-29 13:05:08 -0700 | [diff] [blame] | 213 | mUnreadView = (TextView) findViewById(R.id.unread_count); |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 214 | } |
| 215 | |
Andy Huang | 313ac13 | 2013-03-04 23:40:58 -0800 | [diff] [blame] | 216 | public void expandSearch() { |
| 217 | if (mSearch != null) { |
| 218 | mSearch.expandActionView(); |
| 219 | } |
| 220 | } |
| 221 | |
Vikram Aggarwal | b17cbc0 | 2012-04-06 15:41:46 -0700 | [diff] [blame] | 222 | /** |
Vikram Aggarwal | 0dda573 | 2012-04-06 11:20:16 -0700 | [diff] [blame] | 223 | * Close the search view if it is expanded. |
Vikram Aggarwal | b17cbc0 | 2012-04-06 15:41:46 -0700 | [diff] [blame] | 224 | */ |
| 225 | public void collapseSearch() { |
| 226 | if (mSearch != null) { |
| 227 | mSearch.collapseActionView(); |
| 228 | } |
| 229 | } |
| 230 | |
Mindy Pereira | edb135e | 2012-06-15 08:34:09 -0700 | [diff] [blame] | 231 | /** |
| 232 | * Get the search menu item. |
| 233 | */ |
| 234 | protected MenuItem getSearch() { |
| 235 | return mSearch; |
| 236 | } |
| 237 | |
| 238 | /** |
| 239 | * Get whether to show the conversation subject in the action bar. |
| 240 | */ |
| 241 | protected boolean showConversationSubject() { |
Paul Westbrook | a79acac | 2013-01-31 13:19:41 -0800 | [diff] [blame] | 242 | return ViewMode.isConversationMode(mMode) && mShowConversationSubject; |
Mindy Pereira | edb135e | 2012-06-15 08:34:09 -0700 | [diff] [blame] | 243 | } |
| 244 | |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 245 | public boolean onCreateOptionsMenu(Menu menu) { |
| 246 | // If the mode is valid, then set the initial menu |
| 247 | if (mMode == ViewMode.UNKNOWN) { |
| 248 | return false; |
| 249 | } |
Mindy Pereira | 9b62380 | 2012-03-07 17:15:49 -0800 | [diff] [blame] | 250 | mSearch = menu.findItem(R.id.search); |
| 251 | if (mSearch != null) { |
| 252 | mSearchWidget = (SearchView) mSearch.getActionView(); |
Vikram Aggarwal | 0dda573 | 2012-04-06 11:20:16 -0700 | [diff] [blame] | 253 | mSearch.setOnActionExpandListener(this); |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 254 | SearchManager searchManager = (SearchManager) mActivity.getActivityContext() |
| 255 | .getSystemService(Context.SEARCH_SERVICE); |
Mindy Pereira | fb17849 | 2012-03-07 18:56:42 -0800 | [diff] [blame] | 256 | if (searchManager != null && mSearchWidget != null) { |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 257 | SearchableInfo info = searchManager.getSearchableInfo(mActivity.getComponentName()); |
| 258 | mSearchWidget.setSearchableInfo(info); |
| 259 | mSearchWidget.setOnQueryTextListener(this); |
Vikram Aggarwal | 12a7c04 | 2012-03-28 16:48:08 -0700 | [diff] [blame] | 260 | mSearchWidget.setOnSuggestionListener(this); |
Vikram Aggarwal | 0dda573 | 2012-04-06 11:20:16 -0700 | [diff] [blame] | 261 | mSearchWidget.setIconifiedByDefault(true); |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 262 | } |
| 263 | } |
| 264 | mHelpItem = menu.findItem(R.id.help_info_menu_item); |
Paul Westbrook | 517743e | 2012-03-22 10:40:46 -0700 | [diff] [blame] | 265 | mSendFeedbackItem = menu.findItem(R.id.feedback_menu_item); |
Mindy Pereira | 9b62380 | 2012-03-07 17:15:49 -0800 | [diff] [blame] | 266 | mRefreshItem = menu.findItem(R.id.refresh); |
Marc Blank | edcab01 | 2012-04-04 12:42:38 -0700 | [diff] [blame] | 267 | mFolderSettingsItem = menu.findItem(R.id.folder_options); |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 268 | return true; |
| 269 | } |
| 270 | |
| 271 | public int getOptionsMenuId() { |
| 272 | // Relies on the ordering of the view modes, since they are integer constants. |
| 273 | final int[] modeMenu = { |
| 274 | // 0: UNKNOWN |
| 275 | R.menu.conversation_list_menu, |
| 276 | // 1: CONVERSATION |
| 277 | R.menu.conversation_actions, |
| 278 | // 2: CONVERSATION_LIST |
| 279 | R.menu.conversation_list_menu, |
| 280 | // 3: FOLDER_LIST |
| 281 | R.menu.folder_list_menu, |
| 282 | // 4: SEARCH_RESULTS_LIST |
| 283 | R.menu.conversation_list_search_results_actions, |
| 284 | // 5: SEARCH_RESULTS_CONVERSATION |
Paul Westbrook | 2d50bcd | 2012-04-10 11:53:47 -0700 | [diff] [blame] | 285 | R.menu.conversation_search_results_actions, |
| 286 | // 6: WAITING_FOR_ACCOUNT_INITIALIZATION |
| 287 | R.menu.wait_mode_actions |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 288 | }; |
| 289 | return modeMenu[mMode]; |
| 290 | } |
| 291 | |
Vikram Aggarwal | 7c401b7 | 2012-08-13 16:43:47 -0700 | [diff] [blame] | 292 | public void initialize(ControllableActivity activity, ActivityController callback, |
Vikram Aggarwal | dac89fa | 2013-03-05 16:43:09 -0800 | [diff] [blame] | 293 | ActionBar actionBar) { |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 294 | mActionBar = actionBar; |
Vikram Aggarwal | a799795 | 2012-03-09 10:30:13 -0800 | [diff] [blame] | 295 | mController = callback; |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 296 | mActivity = activity; |
Vikram Aggarwal | 50ff0e5 | 2013-03-14 13:58:02 -0700 | [diff] [blame] | 297 | mFolderObserver = new FolderObserver() { |
| 298 | @Override |
| 299 | public void onChanged(Folder newFolder) { |
| 300 | onFolderUpdated(newFolder); |
| 301 | } |
| 302 | }; |
Vikram Aggarwal | 26b0bfd | 2013-03-29 13:05:08 -0700 | [diff] [blame] | 303 | // Return values are purposely discarded. Initialization happens quite early, and we don't |
| 304 | // have a valid folder, or a valid list of accounts. |
Vikram Aggarwal | 50ff0e5 | 2013-03-14 13:58:02 -0700 | [diff] [blame] | 305 | mFolderObserver.initialize(mController); |
Vikram Aggarwal | 6b641ba | 2013-03-15 16:07:24 -0700 | [diff] [blame] | 306 | mAllAccountObserver = new AllAccountObserver() { |
| 307 | @Override |
| 308 | public void onChanged(Account[] allAccounts) { |
| 309 | mHaveMultipleAccounts = (allAccounts.length > 1); |
| 310 | } |
| 311 | }; |
| 312 | mAllAccountObserver.initialize(mController); |
Scott Banachowski | 53a615d | 2012-12-19 15:31:20 -0800 | [diff] [blame] | 313 | updateAccount(mAccountObserver.initialize(activity.getAccountController())); |
| 314 | } |
| 315 | |
| 316 | private void updateAccount(Account account) { |
| 317 | mAccount = account; |
| 318 | if (mAccount != null) { |
Vikram Aggarwal | bc462ca | 2013-03-15 10:41:03 -0700 | [diff] [blame] | 319 | final ContentResolver resolver = mActivity.getActivityContext().getContentResolver(); |
| 320 | final Bundle bundle = new Bundle(1); |
Scott Banachowski | 53a615d | 2012-12-19 15:31:20 -0800 | [diff] [blame] | 321 | bundle.putParcelable(UIProvider.SetCurrentAccountColumns.ACCOUNT, account); |
Vikram Aggarwal | a51ef9f | 2013-04-02 13:25:35 -0700 | [diff] [blame] | 322 | final UpdateProvider updater = new UpdateProvider(mAccount.uri, resolver); |
| 323 | updater.execute(bundle); |
Vikram Aggarwal | f642b81 | 2013-03-15 16:30:21 -0700 | [diff] [blame] | 324 | setFolderAndAccount(); |
Scott Banachowski | 53a615d | 2012-12-19 15:31:20 -0800 | [diff] [blame] | 325 | } |
Vikram Aggarwal | abd24d8 | 2012-04-26 13:23:14 -0700 | [diff] [blame] | 326 | } |
Andy Huang | e303b9b | 2012-04-19 16:31:09 -0700 | [diff] [blame] | 327 | |
Vikram Aggarwal | abd24d8 | 2012-04-26 13:23:14 -0700 | [diff] [blame] | 328 | /** |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 329 | * Called by the owner of the ActionBar to set the |
| 330 | * folder that is currently being displayed. |
Vikram Aggarwal | 5e5ac74 | 2011-12-19 08:14:16 -0800 | [diff] [blame] | 331 | */ |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 332 | public void setFolder(Folder folder) { |
Mindy Pereira | 641de65 | 2012-08-02 15:21:50 -0700 | [diff] [blame] | 333 | setRefreshInProgress(false); |
Marc Blank | edcab01 | 2012-04-04 12:42:38 -0700 | [diff] [blame] | 334 | mFolder = folder; |
Vikram Aggarwal | f642b81 | 2013-03-15 16:30:21 -0700 | [diff] [blame] | 335 | setFolderAndAccount(); |
Vikram Aggarwal | 1dd77c9 | 2012-08-21 09:27:40 -0700 | [diff] [blame] | 336 | mActivity.invalidateOptionsMenu(); |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 337 | } |
Vikram Aggarwal | 5e5ac74 | 2011-12-19 08:14:16 -0800 | [diff] [blame] | 338 | |
Mindy Pereira | 641de65 | 2012-08-02 15:21:50 -0700 | [diff] [blame] | 339 | public void onDestroy() { |
| 340 | if (mFolderObserver != null) { |
Vikram Aggarwal | 50ff0e5 | 2013-03-14 13:58:02 -0700 | [diff] [blame] | 341 | mFolderObserver.unregisterAndDestroy(); |
Mindy Pereira | 641de65 | 2012-08-02 15:21:50 -0700 | [diff] [blame] | 342 | mFolderObserver = null; |
| 343 | } |
Vikram Aggarwal | 6b641ba | 2013-03-15 16:07:24 -0700 | [diff] [blame] | 344 | if (mAllAccountObserver != null) { |
| 345 | mAllAccountObserver.unregisterAndDestroy(); |
| 346 | mAllAccountObserver = null; |
| 347 | } |
Vikram Aggarwal | 7c401b7 | 2012-08-13 16:43:47 -0700 | [diff] [blame] | 348 | mAccountObserver.unregisterAndDestroy(); |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 349 | } |
| 350 | |
Vikram Aggarwal | a799795 | 2012-03-09 10:30:13 -0800 | [diff] [blame] | 351 | @Override |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 352 | public void onViewModeChanged(int newMode) { |
| 353 | mMode = newMode; |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 354 | mActivity.invalidateOptionsMenu(); |
Vikram Aggarwal | 1a3f3fc | 2012-07-18 14:49:08 -0700 | [diff] [blame] | 355 | // Check if we are either on a phone, or in Conversation mode on tablet. For these, the |
| 356 | // recent folders is enabled. |
Andy Huang | d736a38 | 2012-08-29 13:08:58 -0700 | [diff] [blame] | 357 | switch (mMode) { |
| 358 | case ViewMode.UNKNOWN: |
Vikram Aggarwal | 49499e6 | 2013-02-08 16:39:08 -0800 | [diff] [blame] | 359 | closeSearchField(); |
Andy Huang | d736a38 | 2012-08-29 13:08:58 -0700 | [diff] [blame] | 360 | break; |
| 361 | case ViewMode.CONVERSATION_LIST: |
Vikram Aggarwal | 49499e6 | 2013-02-08 16:39:08 -0800 | [diff] [blame] | 362 | closeSearchField(); |
Andy Huang | d736a38 | 2012-08-29 13:08:58 -0700 | [diff] [blame] | 363 | showNavList(); |
| 364 | break; |
| 365 | case ViewMode.CONVERSATION: |
Vikram Aggarwal | 49499e6 | 2013-02-08 16:39:08 -0800 | [diff] [blame] | 366 | closeSearchField(); |
Andy Huang | d736a38 | 2012-08-29 13:08:58 -0700 | [diff] [blame] | 367 | mActionBar.setDisplayHomeAsUpEnabled(true); |
| 368 | if (!mShowConversationSubject) { |
| 369 | showNavList(); |
| 370 | } else { |
Andy Huang | af65e73 | 2013-01-28 20:45:40 -0800 | [diff] [blame] | 371 | setSnippetMode(); |
Andy Huang | d736a38 | 2012-08-29 13:08:58 -0700 | [diff] [blame] | 372 | } |
| 373 | break; |
| 374 | case ViewMode.FOLDER_LIST: |
Vikram Aggarwal | 49499e6 | 2013-02-08 16:39:08 -0800 | [diff] [blame] | 375 | closeSearchField(); |
Andy Huang | d736a38 | 2012-08-29 13:08:58 -0700 | [diff] [blame] | 376 | mActionBar.setDisplayHomeAsUpEnabled(true); |
Andy Huang | af65e73 | 2013-01-28 20:45:40 -0800 | [diff] [blame] | 377 | setFoldersMode(); |
Andy Huang | d736a38 | 2012-08-29 13:08:58 -0700 | [diff] [blame] | 378 | break; |
| 379 | case ViewMode.WAITING_FOR_ACCOUNT_INITIALIZATION: |
| 380 | // We want the user to be able to switch accounts while waiting for an account |
| 381 | // to sync. |
| 382 | showNavList(); |
| 383 | break; |
| 384 | } |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 385 | } |
Vikram Aggarwal | 5e5ac74 | 2011-12-19 08:14:16 -0800 | [diff] [blame] | 386 | |
Vikram Aggarwal | 49499e6 | 2013-02-08 16:39:08 -0800 | [diff] [blame] | 387 | /** |
| 388 | * Close the search query entry field to avoid keyboard events, and to restore the actionbar |
| 389 | * to non-search mode. |
| 390 | */ |
Vikram Aggarwal | dac89fa | 2013-03-05 16:43:09 -0800 | [diff] [blame] | 391 | private void closeSearchField() { |
Vikram Aggarwal | 49499e6 | 2013-02-08 16:39:08 -0800 | [diff] [blame] | 392 | if (mSearch == null) { |
| 393 | return; |
| 394 | } |
| 395 | mSearch.collapseActionView(); |
| 396 | } |
| 397 | |
Mindy Pereira | edb135e | 2012-06-15 08:34:09 -0700 | [diff] [blame] | 398 | protected int getMode() { |
| 399 | return mMode; |
| 400 | } |
| 401 | |
Mindy Pereira | b849dfb | 2012-03-07 18:13:15 -0800 | [diff] [blame] | 402 | public boolean onPrepareOptionsMenu(Menu menu) { |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 403 | // We start out with every option enabled. Based on the current view, we disable actions |
| 404 | // that are possible. |
Vikram Aggarwal | 95b28ab | 2012-04-26 15:53:09 -0700 | [diff] [blame] | 405 | LogUtils.d(LOG_TAG, "ActionBarView.onPrepareOptionsMenu()."); |
Vikram Aggarwal | 5e5ac74 | 2011-12-19 08:14:16 -0800 | [diff] [blame] | 406 | |
Andy Huang | d736a38 | 2012-08-29 13:08:58 -0700 | [diff] [blame] | 407 | // TODO: move refresh stuff into setRefreshInProgress. can just setActionView without |
| 408 | // invalidating. |
Mindy Pereira | 9b62380 | 2012-03-07 17:15:49 -0800 | [diff] [blame] | 409 | if (mRefreshInProgress) { |
| 410 | if (mRefreshItem != null) { |
| 411 | if (mRefreshActionView == null) { |
| 412 | mRefreshItem.setActionView(R.layout.action_bar_indeterminate_progress); |
| 413 | mRefreshActionView = mRefreshItem.getActionView(); |
| 414 | } else { |
| 415 | mRefreshItem.setActionView(mRefreshActionView); |
| 416 | } |
| 417 | } |
| 418 | } else { |
| 419 | if (mRefreshItem != null) { |
| 420 | mRefreshItem.setActionView(null); |
| 421 | } |
| 422 | } |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 423 | if (mHelpItem != null) { |
| 424 | mHelpItem.setVisible(mAccount != null |
| 425 | && mAccount.supportsCapability(AccountCapabilities.HELP_CONTENT)); |
| 426 | } |
Paul Westbrook | 517743e | 2012-03-22 10:40:46 -0700 | [diff] [blame] | 427 | if (mSendFeedbackItem != null) { |
| 428 | mSendFeedbackItem.setVisible(mAccount != null |
| 429 | && mAccount.supportsCapability(AccountCapabilities.SEND_FEEDBACK)); |
| 430 | } |
Marc Blank | edcab01 | 2012-04-04 12:42:38 -0700 | [diff] [blame] | 431 | if (mFolderSettingsItem != null) { |
| 432 | mFolderSettingsItem.setVisible(mFolder != null |
| 433 | && mFolder.supportsCapability(FolderCapabilities.SUPPORTS_SETTINGS)); |
| 434 | } |
Mindy Pereira | 62c7179 | 2012-07-18 09:14:29 -0700 | [diff] [blame] | 435 | |
Mindy Pereira | 9b62380 | 2012-03-07 17:15:49 -0800 | [diff] [blame] | 436 | switch (mMode) { |
Yorke Lee | f807ba7 | 2012-09-20 17:18:05 -0700 | [diff] [blame] | 437 | case ViewMode.CONVERSATION: |
| 438 | case ViewMode.SEARCH_RESULTS_CONVERSATION: |
| 439 | // We update the ActionBar options when we are entering conversation view because |
| 440 | // waiting for the AbstractConversationViewFragment to do it causes duplicate icons |
| 441 | // to show up during the time between the conversation is selected and the fragment |
| 442 | // is added. |
| 443 | setConversationModeOptions(menu); |
| 444 | break; |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 445 | case ViewMode.CONVERSATION_LIST: |
Mindy Pereira | 30fd47b | 2012-03-09 09:24:00 -0800 | [diff] [blame] | 446 | // Show compose, search, folders, and sync based on the account |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 447 | // The only option that needs to be disabled is search |
Mindy Pereira | 863e441 | 2012-03-23 12:21:38 -0700 | [diff] [blame] | 448 | Utils.setMenuItemVisibility(menu, R.id.search, |
Mindy Pereira | 9b62380 | 2012-03-07 17:15:49 -0800 | [diff] [blame] | 449 | mAccount.supportsCapability(AccountCapabilities.FOLDER_SERVER_SEARCH)); |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 450 | break; |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 451 | } |
mindyp | f5a2442 | 2012-09-04 13:28:11 -0700 | [diff] [blame] | 452 | return false; |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 453 | } |
Vikram Aggarwal | 5e5ac74 | 2011-12-19 08:14:16 -0800 | [diff] [blame] | 454 | |
Vikram Aggarwal | abd24d8 | 2012-04-26 13:23:14 -0700 | [diff] [blame] | 455 | /** |
| 456 | * Put the ActionBar in List navigation mode. This starts the spinner up if it is missing. |
| 457 | */ |
Mindy Pereira | 72a9f39 | 2012-03-21 09:41:09 -0700 | [diff] [blame] | 458 | private void showNavList() { |
Vikram Aggarwal | 26b0bfd | 2013-03-29 13:05:08 -0700 | [diff] [blame] | 459 | setTitleModeFlags(ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_CUSTOM); |
Vikram Aggarwal | 81064f8 | 2013-04-02 13:25:35 -0700 | [diff] [blame] | 460 | if (mUnreadView != null) { |
| 461 | mUnreadView.setVisibility(View.VISIBLE); |
| 462 | } |
Andy Huang | af65e73 | 2013-01-28 20:45:40 -0800 | [diff] [blame] | 463 | mSubjectView.setVisibility(View.GONE); |
Vikram Aggarwal | f642b81 | 2013-03-15 16:30:21 -0700 | [diff] [blame] | 464 | setFolderAndAccount(); |
Mindy Pereira | 72a9f39 | 2012-03-21 09:41:09 -0700 | [diff] [blame] | 465 | } |
| 466 | |
Vikram Aggarwal | abd24d8 | 2012-04-26 13:23:14 -0700 | [diff] [blame] | 467 | /** |
Andy Huang | af65e73 | 2013-01-28 20:45:40 -0800 | [diff] [blame] | 468 | * Set the actionbar mode to "snippet" mode: no list navigation, show what looks like 2-line |
| 469 | * "standard" snippet. Later on, {@link #getUnshownSubject(String)} will seamlessly switch |
| 470 | * back to bog-standard SHOW_TITLE mode once the text remainders can safely be determined. |
Vikram Aggarwal | abd24d8 | 2012-04-26 13:23:14 -0700 | [diff] [blame] | 471 | */ |
Paul Westbrook | a79acac | 2013-01-31 13:19:41 -0800 | [diff] [blame] | 472 | protected void setSnippetMode() { |
Andy Huang | af65e73 | 2013-01-28 20:45:40 -0800 | [diff] [blame] | 473 | setTitleModeFlags(ActionBar.DISPLAY_SHOW_CUSTOM); |
Vikram Aggarwal | 81064f8 | 2013-04-02 13:25:35 -0700 | [diff] [blame] | 474 | if (mUnreadView != null) { |
| 475 | mUnreadView.setVisibility(View.GONE); |
| 476 | } |
Andy Huang | af65e73 | 2013-01-28 20:45:40 -0800 | [diff] [blame] | 477 | mSubjectView.setVisibility(View.VISIBLE); |
Andy Huang | 9da91d3 | 2013-01-30 15:27:00 -0800 | [diff] [blame] | 478 | mSubjectView.addOnLayoutChangeListener(mSnippetLayoutListener); |
Andy Huang | af65e73 | 2013-01-28 20:45:40 -0800 | [diff] [blame] | 479 | } |
| 480 | |
| 481 | private void setFoldersMode() { |
| 482 | setTitleModeFlags(ActionBar.DISPLAY_SHOW_TITLE); |
Vikram Aggarwal | 81064f8 | 2013-04-02 13:25:35 -0700 | [diff] [blame] | 483 | if (mUnreadView != null) { |
| 484 | mUnreadView.setVisibility(View.GONE); |
| 485 | } |
Andy Huang | af65e73 | 2013-01-28 20:45:40 -0800 | [diff] [blame] | 486 | mActionBar.setTitle(R.string.folders); |
Vikram Aggarwal | 6b641ba | 2013-03-15 16:07:24 -0700 | [diff] [blame] | 487 | if (mHaveMultipleAccounts) { |
| 488 | mActionBar.setSubtitle(mAccount.name); |
| 489 | } |
mindyp | 6fb1b62 | 2012-09-05 09:27:08 -0700 | [diff] [blame] | 490 | } |
| 491 | |
| 492 | /** |
| 493 | * Set the actionbar mode to empty: no title, no custom content. |
| 494 | */ |
| 495 | protected void setEmptyMode() { |
Vikram Aggarwal | bc462ca | 2013-03-15 10:41:03 -0700 | [diff] [blame] | 496 | setTitleModeFlags(ActionBar.DISPLAY_SHOW_TITLE); |
Alice Yang | 760d956 | 2013-04-02 13:13:24 -0700 | [diff] [blame] | 497 | if (mSubjectView != null) { |
| 498 | mSubjectView.setVisibility(View.GONE); |
| 499 | } |
| 500 | if (mUnreadView != null) { |
| 501 | mUnreadView.setVisibility(View.GONE); |
| 502 | } |
Vikram Aggarwal | abd24d8 | 2012-04-26 13:23:14 -0700 | [diff] [blame] | 503 | } |
| 504 | |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 505 | public void removeBackButton() { |
| 506 | if (mActionBar == null) { |
| 507 | return; |
| 508 | } |
Vikram Aggarwal | 649b9ea | 2012-08-27 12:15:20 -0700 | [diff] [blame] | 509 | // Remove the back button but continue showing an icon. |
| 510 | final int mask = ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME; |
| 511 | mActionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME, mask); |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 512 | mActivity.getActionBar().setHomeButtonEnabled(false); |
| 513 | } |
Mindy Pereira | bc57bf1 | 2012-02-29 14:39:09 -0800 | [diff] [blame] | 514 | |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 515 | public void setBackButton() { |
| 516 | if (mActionBar == null){ |
| 517 | return; |
| 518 | } |
Vikram Aggarwal | 649b9ea | 2012-08-27 12:15:20 -0700 | [diff] [blame] | 519 | // Show home as up, and show an icon. |
| 520 | final int mask = ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME; |
| 521 | mActionBar.setDisplayOptions(mask, mask); |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 522 | mActivity.getActionBar().setHomeButtonEnabled(true); |
| 523 | } |
Vikram Aggarwal | 5e5ac74 | 2011-12-19 08:14:16 -0800 | [diff] [blame] | 524 | |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 525 | @Override |
| 526 | public boolean onQueryTextSubmit(String query) { |
Mindy Pereira | 4ce5994 | 2012-03-08 09:57:35 -0800 | [diff] [blame] | 527 | if (mSearch != null) { |
| 528 | mSearch.collapseActionView(); |
| 529 | mSearchWidget.setQuery("", false); |
| 530 | } |
Andy Huang | 313ac13 | 2013-03-04 23:40:58 -0800 | [diff] [blame] | 531 | mController.executeSearch(query.trim()); |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 532 | return true; |
| 533 | } |
Vikram Aggarwal | 5e5ac74 | 2011-12-19 08:14:16 -0800 | [diff] [blame] | 534 | |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 535 | @Override |
| 536 | public boolean onQueryTextChange(String newText) { |
Mindy Pereira | 68f2e22 | 2012-03-07 10:36:54 -0800 | [diff] [blame] | 537 | return false; |
| 538 | } |
Mindy Pereira | 9b62380 | 2012-03-07 17:15:49 -0800 | [diff] [blame] | 539 | |
Mindy Pereira | 641de65 | 2012-08-02 15:21:50 -0700 | [diff] [blame] | 540 | private boolean setRefreshInProgress(boolean inProgress) { |
Mindy Pereira | 9b62380 | 2012-03-07 17:15:49 -0800 | [diff] [blame] | 541 | if (inProgress != mRefreshInProgress) { |
| 542 | mRefreshInProgress = inProgress; |
| 543 | if (mSearch == null || !mSearch.isActionViewExpanded()) { |
| 544 | mHandler.post(mInvalidateMenu); |
| 545 | } |
| 546 | return true; |
| 547 | } |
| 548 | return false; |
| 549 | } |
| 550 | |
Mindy Pereira | 641de65 | 2012-08-02 15:21:50 -0700 | [diff] [blame] | 551 | private void onRefreshStarted() { |
Mindy Pereira | 9b62380 | 2012-03-07 17:15:49 -0800 | [diff] [blame] | 552 | setRefreshInProgress(true); |
| 553 | } |
| 554 | |
Vikram Aggarwal | 41b9e8f | 2012-09-25 10:15:04 -0700 | [diff] [blame] | 555 | private void onRefreshStopped() { |
Paul Westbrook | 4f393ec | 2012-08-10 09:19:50 -0700 | [diff] [blame] | 556 | setRefreshInProgress(false); |
Mindy Pereira | 9b62380 | 2012-03-07 17:15:49 -0800 | [diff] [blame] | 557 | } |
Mindy Pereira | fd0c297 | 2012-03-27 13:50:39 -0700 | [diff] [blame] | 558 | |
Vikram Aggarwal | 12a7c04 | 2012-03-28 16:48:08 -0700 | [diff] [blame] | 559 | // Next two methods are called when search suggestions are clicked. |
| 560 | @Override |
| 561 | public boolean onSuggestionSelect(int position) { |
| 562 | return onSuggestionClick(position); |
| 563 | } |
| 564 | |
| 565 | @Override |
| 566 | public boolean onSuggestionClick(int position) { |
| 567 | final Cursor c = mSearchWidget.getSuggestionsAdapter().getCursor(); |
| 568 | final boolean haveValidQuery = (c != null) && c.moveToPosition(position); |
| 569 | if (!haveValidQuery) { |
| 570 | LogUtils.d(LOG_TAG, "onSuggestionClick: Couldn't get a search query"); |
Mindy Pereira | f8fcb80 | 2012-06-25 11:43:06 -0700 | [diff] [blame] | 571 | // We haven't handled this query, but the default behavior will |
| 572 | // leave EXTRA_ACCOUNT un-populated, leading to a crash. So claim |
| 573 | // that we have handled the event. |
Vikram Aggarwal | 12a7c04 | 2012-03-28 16:48:08 -0700 | [diff] [blame] | 574 | return true; |
| 575 | } |
Mindy Pereira | b466bcf | 2012-06-14 15:52:26 -0700 | [diff] [blame] | 576 | collapseSearch(); |
Mindy Pereira | 1e180dd | 2012-08-16 10:24:10 -0700 | [diff] [blame] | 577 | // what is in the text field |
Mindy Pereira | ca97b31 | 2012-06-25 14:00:20 -0700 | [diff] [blame] | 578 | String queryText = mSearchWidget.getQuery().toString(); |
Mindy Pereira | 1e180dd | 2012-08-16 10:24:10 -0700 | [diff] [blame] | 579 | // What the suggested query is |
Mindy Pereira | f8fcb80 | 2012-06-25 11:43:06 -0700 | [diff] [blame] | 580 | String query = c.getString(c.getColumnIndex(SearchManager.SUGGEST_COLUMN_QUERY)); |
mindyp | 0fc8fa5 | 2012-09-04 10:43:23 -0700 | [diff] [blame] | 581 | // If the text the user typed in is a prefix of what is in the search |
| 582 | // widget suggestion query, just take the search widget suggestion |
| 583 | // query. Otherwise, it is a suffix and we want to remove matching |
| 584 | // prefix portions. |
| 585 | if (!TextUtils.isEmpty(queryText) && query.indexOf(queryText) != 0) { |
Mindy Pereira | 5e1403c | 2012-07-18 13:22:11 -0700 | [diff] [blame] | 586 | final int queryTokenIndex = queryText |
| 587 | .lastIndexOf(SearchRecentSuggestionsProvider.QUERY_TOKEN_SEPARATOR); |
| 588 | if (queryTokenIndex > -1) { |
| 589 | queryText = queryText.substring(0, queryTokenIndex); |
Mindy Pereira | ca97b31 | 2012-06-25 14:00:20 -0700 | [diff] [blame] | 590 | } |
| 591 | // Since we auto-complete on each token in a query, if the query the |
| 592 | // user typed up until the last token is a substring of the |
| 593 | // suggestion they click, make sure we don't double include the |
| 594 | // query text. For example: |
| 595 | // user types john, that matches john palo alto |
| 596 | // User types john p, that matches john john palo alto |
| 597 | // Remove the first john |
Mindy Pereira | 5e1403c | 2012-07-18 13:22:11 -0700 | [diff] [blame] | 598 | // Only do this if we have multiple query tokens. |
| 599 | if (queryTokenIndex > -1 && !TextUtils.isEmpty(query) && query.contains(queryText) |
Mindy Pereira | ca97b31 | 2012-06-25 14:00:20 -0700 | [diff] [blame] | 600 | && queryText.length() < query.length()) { |
| 601 | int start = query.indexOf(queryText); |
| 602 | query = query.substring(0, start) + query.substring(start + queryText.length()); |
| 603 | } |
| 604 | } |
Andy Huang | 313ac13 | 2013-03-04 23:40:58 -0800 | [diff] [blame] | 605 | mController.executeSearch(query.trim()); |
Vikram Aggarwal | 12a7c04 | 2012-03-28 16:48:08 -0700 | [diff] [blame] | 606 | return true; |
| 607 | } |
Mindy Pereira | a46a57f | 2012-04-03 17:19:31 -0700 | [diff] [blame] | 608 | |
Vikram Aggarwal | f642b81 | 2013-03-15 16:30:21 -0700 | [diff] [blame] | 609 | /** |
| 610 | * Uses the current state to update the current folder {@link #mFolder} and the current |
| 611 | * account {@link #mAccount} shown in the actionbar. |
| 612 | */ |
| 613 | private void setFolderAndAccount() { |
Vikram Aggarwal | 5fce992 | 2013-04-01 21:17:42 -0700 | [diff] [blame] | 614 | // Very little can be done if the actionbar or activity is null. |
| 615 | if (mActionBar == null || mActivity == null) { |
| 616 | return; |
| 617 | } |
Vikram Aggarwal | f642b81 | 2013-03-15 16:30:21 -0700 | [diff] [blame] | 618 | // Check if we should be changing the actionbar at all, and back off if not. |
Vikram Aggarwal | 5fce992 | 2013-04-01 21:17:42 -0700 | [diff] [blame] | 619 | final boolean isShowingFolderAndAccount = mIsOnTablet || ViewMode.isListMode(mMode); |
| 620 | if (!isShowingFolderAndAccount) { |
Vikram Aggarwal | f642b81 | 2013-03-15 16:30:21 -0700 | [diff] [blame] | 621 | return; |
| 622 | } |
Vikram Aggarwal | f642b81 | 2013-03-15 16:30:21 -0700 | [diff] [blame] | 623 | if (mAccount != null && mHaveMultipleAccounts) { |
| 624 | mActionBar.setSubtitle(mAccount.name); |
| 625 | } |
Vikram Aggarwal | 26b0bfd | 2013-03-29 13:05:08 -0700 | [diff] [blame] | 626 | if (mFolder == null) { |
| 627 | return; |
| 628 | } |
| 629 | mActionBar.setTitle(mFolder.name); |
| 630 | if (mUnreadView != null) { |
| 631 | mUnreadView.setText(Utils.getUnreadCountString( |
| 632 | mActivity.getApplicationContext(), mFolder.unreadCount)); |
| 633 | } |
Vikram Aggarwal | bc462ca | 2013-03-15 10:41:03 -0700 | [diff] [blame] | 634 | } |
| 635 | |
Mindy Pereira | a46a57f | 2012-04-03 17:19:31 -0700 | [diff] [blame] | 636 | /** |
| 637 | * Notify that the folder has changed. |
| 638 | */ |
| 639 | public void onFolderUpdated(Folder folder) { |
Vikram Aggarwal | 26b0bfd | 2013-03-29 13:05:08 -0700 | [diff] [blame] | 640 | if (folder == null) { |
| 641 | return; |
| 642 | } |
Vikram Aggarwal | c18c338 | 2013-04-05 15:29:13 -0700 | [diff] [blame^] | 643 | mFolder = folder; |
Vikram Aggarwal | f642b81 | 2013-03-15 16:30:21 -0700 | [diff] [blame] | 644 | setFolderAndAccount(); |
Mindy Pereira | 641de65 | 2012-08-02 15:21:50 -0700 | [diff] [blame] | 645 | if (folder.isSyncInProgress()) { |
| 646 | onRefreshStarted(); |
| 647 | } else { |
| 648 | // Stop the spinner here. |
Vikram Aggarwal | 41b9e8f | 2012-09-25 10:15:04 -0700 | [diff] [blame] | 649 | onRefreshStopped(); |
Mindy Pereira | 641de65 | 2012-08-02 15:21:50 -0700 | [diff] [blame] | 650 | } |
Vikram Aggarwal | 49499e6 | 2013-02-08 16:39:08 -0800 | [diff] [blame] | 651 | final ConversationListContext listContext = mController == null ? null : |
| 652 | mController.getCurrentListContext(); |
| 653 | if (!ConversationListContext.isSearchResult(listContext)) { |
| 654 | closeSearchField(); |
| 655 | } |
Mindy Pereira | a46a57f | 2012-04-03 17:19:31 -0700 | [diff] [blame] | 656 | } |
Vikram Aggarwal | 0dda573 | 2012-04-06 11:20:16 -0700 | [diff] [blame] | 657 | |
| 658 | @Override |
| 659 | public boolean onMenuItemActionExpand(MenuItem item) { |
| 660 | // Do nothing. Required as part of the interface, we ar only interested in |
| 661 | // onMenuItemActionCollapse(MenuItem). |
| 662 | // Have to return true here. Unlike other callbacks, the return value here is whether |
| 663 | // we want to suppress the action (rather than consume the action). We don't want to |
| 664 | // suppress the action. |
| 665 | return true; |
| 666 | } |
| 667 | |
| 668 | @Override |
| 669 | public boolean onMenuItemActionCollapse(MenuItem item) { |
Mindy Pereira | b466bcf | 2012-06-14 15:52:26 -0700 | [diff] [blame] | 670 | // Work around b/6664203 by manually forcing this view to be VISIBLE |
| 671 | // upon ActionView collapse. DISPLAY_SHOW_CUSTOM will still control its final |
| 672 | // visibility. |
Andy Huang | 5895f7b | 2012-06-01 17:07:20 -0700 | [diff] [blame] | 673 | setVisibility(VISIBLE); |
Mindy Pereira | b466bcf | 2012-06-14 15:52:26 -0700 | [diff] [blame] | 674 | // Have to return true here. Unlike other callbacks, the return value |
| 675 | // here is whether we want to suppress the action (rather than consume the action). We |
| 676 | // don't want to suppress the action. |
Vikram Aggarwal | 0dda573 | 2012-04-06 11:20:16 -0700 | [diff] [blame] | 677 | return true; |
| 678 | } |
Vikram Aggarwal | 3726397 | 2012-04-17 15:51:14 -0700 | [diff] [blame] | 679 | |
Andy Huang | af65e73 | 2013-01-28 20:45:40 -0800 | [diff] [blame] | 680 | private void setTitleModeFlags(int enabledFlags) { |
| 681 | final int mask = ActionBar.DISPLAY_SHOW_TITLE |
| 682 | | ActionBar.DISPLAY_SHOW_CUSTOM | DISPLAY_TITLE_MULTIPLE_LINES; |
Andy Huang | af65e73 | 2013-01-28 20:45:40 -0800 | [diff] [blame] | 683 | mActionBar.setDisplayOptions(enabledFlags, mask); |
| 684 | } |
| 685 | |
Andy Huang | 5895f7b | 2012-06-01 17:07:20 -0700 | [diff] [blame] | 686 | @Override |
| 687 | public void setSubject(String subject) { |
| 688 | if (!mShowConversationSubject) { |
| 689 | return; |
| 690 | } |
| 691 | |
Alice Yang | 48706db | 2013-02-01 17:56:52 -0800 | [diff] [blame] | 692 | // Use a smaller font size than the default action bar title text size |
| 693 | SpannableStringBuilder builder = new SpannableStringBuilder(); |
| 694 | SpannableString textSizeSpannable = new SpannableString(subject); |
| 695 | textSizeSpannable.setSpan( |
| 696 | new TextAppearanceSpan(getContext(), R.style.SubjectActionBarTitleText), |
| 697 | 0, subject.length(), 0); |
| 698 | builder.append(textSizeSpannable); |
| 699 | |
| 700 | mActionBar.setTitle(builder); |
Andy Huang | af65e73 | 2013-01-28 20:45:40 -0800 | [diff] [blame] | 701 | mActionBar.setSubtitle(null); |
Andy Huang | 5895f7b | 2012-06-01 17:07:20 -0700 | [diff] [blame] | 702 | mSubjectView.setText(subject); |
| 703 | } |
| 704 | |
| 705 | @Override |
Rohan Shah | 2b288b3 | 2013-03-20 13:06:18 -0700 | [diff] [blame] | 706 | public void clearSubjectAndUpdate() { |
Andy Huang | 5895f7b | 2012-06-01 17:07:20 -0700 | [diff] [blame] | 707 | if (!mShowConversationSubject) { |
| 708 | return; |
| 709 | } |
Rohan Shah | 2b288b3 | 2013-03-20 13:06:18 -0700 | [diff] [blame] | 710 | // Wipe subject view text |
Andy Huang | 5895f7b | 2012-06-01 17:07:20 -0700 | [diff] [blame] | 711 | mSubjectView.setText(null); |
Rohan Shah | 2b288b3 | 2013-03-20 13:06:18 -0700 | [diff] [blame] | 712 | // Set folder and account as title and subtitle |
| 713 | setFolderAndAccount(); |
Andy Huang | 5895f7b | 2012-06-01 17:07:20 -0700 | [diff] [blame] | 714 | } |
| 715 | |
| 716 | @Override |
| 717 | public String getUnshownSubject(String subject) { |
| 718 | if (!mShowConversationSubject) { |
| 719 | return subject; |
| 720 | } |
| 721 | |
Andy Huang | 9da91d3 | 2013-01-30 15:27:00 -0800 | [diff] [blame] | 722 | return mSubjectView.getTextRemainder(subject); |
Andy Huang | af65e73 | 2013-01-28 20:45:40 -0800 | [diff] [blame] | 723 | } |
| 724 | |
Yorke Lee | f807ba7 | 2012-09-20 17:18:05 -0700 | [diff] [blame] | 725 | public void setCurrentConversation(Conversation conversation) { |
| 726 | mCurrentConversation = conversation; |
| 727 | } |
| 728 | |
| 729 | //We need to do this here instead of in the fragment |
| 730 | public void setConversationModeOptions(Menu menu) { |
| 731 | if (mCurrentConversation == null) { |
| 732 | return; |
| 733 | } |
| 734 | final boolean showMarkImportant = !mCurrentConversation.isImportant(); |
| 735 | Utils.setMenuItemVisibility(menu, R.id.mark_important, showMarkImportant |
| 736 | && mAccount.supportsCapability(UIProvider.AccountCapabilities.MARK_IMPORTANT)); |
| 737 | Utils.setMenuItemVisibility(menu, R.id.mark_not_important, !showMarkImportant |
| 738 | && mAccount.supportsCapability(UIProvider.AccountCapabilities.MARK_IMPORTANT)); |
| 739 | final boolean showDelete = mFolder != null && |
| 740 | mFolder.supportsCapability(UIProvider.FolderCapabilities.DELETE); |
| 741 | Utils.setMenuItemVisibility(menu, R.id.delete, showDelete); |
| 742 | // We only want to show the discard drafts menu item if we are not showing the delete menu |
| 743 | // item, and the current folder is a draft folder and the account supports discarding |
| 744 | // drafts for a conversation |
| 745 | final boolean showDiscardDrafts = !showDelete && mFolder != null && mFolder.isDraft() && |
| 746 | mAccount.supportsCapability(AccountCapabilities.DISCARD_CONVERSATION_DRAFTS); |
| 747 | Utils.setMenuItemVisibility(menu, R.id.discard_drafts, showDiscardDrafts); |
| 748 | final boolean archiveVisible = mAccount.supportsCapability(AccountCapabilities.ARCHIVE) |
| 749 | && mFolder != null && mFolder.supportsCapability(FolderCapabilities.ARCHIVE) |
| 750 | && !mFolder.isTrash(); |
| 751 | Utils.setMenuItemVisibility(menu, R.id.archive, archiveVisible); |
| 752 | Utils.setMenuItemVisibility(menu, R.id.remove_folder, !archiveVisible && mFolder != null |
| 753 | && mFolder.supportsCapability(FolderCapabilities.CAN_ACCEPT_MOVED_MESSAGES) |
| 754 | && !mFolder.isProviderFolder()); |
Alice Yang | d7d17b6 | 2013-03-26 22:23:25 -0700 | [diff] [blame] | 755 | // TODO(alice): reenable "move to" after it works properly with section inbox |
| 756 | Utils.setMenuItemVisibility(menu, R.id.move_to, false); |
| 757 | // Utils.setMenuItemVisibility(menu, R.id.move_to, mFolder != null |
| 758 | // && mFolder.supportsCapability(FolderCapabilities.ALLOWS_REMOVE_CONVERSATION)); |
Yorke Lee | f807ba7 | 2012-09-20 17:18:05 -0700 | [diff] [blame] | 759 | final MenuItem removeFolder = menu.findItem(R.id.remove_folder); |
Alice Yang | 40698e6 | 2013-03-14 11:31:53 -0700 | [diff] [blame] | 760 | if (mFolder != null && removeFolder != null) { |
Yorke Lee | f807ba7 | 2012-09-20 17:18:05 -0700 | [diff] [blame] | 761 | removeFolder.setTitle(mActivity.getApplicationContext().getString( |
| 762 | R.string.remove_folder, mFolder.name)); |
| 763 | } |
| 764 | Utils.setMenuItemVisibility(menu, R.id.report_spam, |
| 765 | mAccount.supportsCapability(AccountCapabilities.REPORT_SPAM) && mFolder != null |
| 766 | && mFolder.supportsCapability(FolderCapabilities.REPORT_SPAM) |
| 767 | && !mCurrentConversation.spam); |
| 768 | Utils.setMenuItemVisibility(menu, R.id.mark_not_spam, |
| 769 | mAccount.supportsCapability(AccountCapabilities.REPORT_SPAM) && mFolder != null |
| 770 | && mFolder.supportsCapability(FolderCapabilities.MARK_NOT_SPAM) |
| 771 | && mCurrentConversation.spam); |
| 772 | Utils.setMenuItemVisibility(menu, R.id.report_phishing, |
| 773 | mAccount.supportsCapability(AccountCapabilities.REPORT_PHISHING) && mFolder != null |
| 774 | && mFolder.supportsCapability(FolderCapabilities.REPORT_PHISHING) |
| 775 | && !mCurrentConversation.phishing); |
| 776 | Utils.setMenuItemVisibility(menu, R.id.mute, |
| 777 | mAccount.supportsCapability(AccountCapabilities.MUTE) && mFolder != null |
| 778 | && mFolder.supportsCapability(FolderCapabilities.DESTRUCTIVE_MUTE) |
| 779 | && !mCurrentConversation.muted); |
| 780 | } |
Vikram Aggarwal | 5e5ac74 | 2011-12-19 08:14:16 -0800 | [diff] [blame] | 781 | } |