• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.email.activity;
18 
19 import android.app.ActionBar;
20 import android.app.LoaderManager;
21 import android.app.LoaderManager.LoaderCallbacks;
22 import android.content.Context;
23 import android.content.Loader;
24 import android.database.Cursor;
25 import android.graphics.drawable.Drawable;
26 import android.os.Bundle;
27 import android.text.SpannableString;
28 import android.text.SpannableStringBuilder;
29 import android.text.TextUtils;
30 import android.text.style.TextAppearanceSpan;
31 import android.view.LayoutInflater;
32 import android.view.View;
33 import android.view.ViewGroup;
34 import android.widget.AdapterView;
35 import android.widget.AdapterView.OnItemClickListener;
36 import android.widget.ListPopupWindow;
37 import android.widget.ListView;
38 import android.widget.SearchView;
39 import android.widget.TextView;
40 
41 import com.android.email.R;
42 import com.android.emailcommon.provider.Account;
43 import com.android.emailcommon.provider.Mailbox;
44 import com.android.emailcommon.utility.DelayedOperations;
45 import com.android.emailcommon.utility.Utility;
46 
47 /**
48  * Manages the account name and the custom view part on the action bar.
49  */
50 public class ActionBarController {
51     private static final String BUNDLE_KEY_MODE = "ActionBarController.BUNDLE_KEY_MODE";
52 
53     /**
54      * Constants for {@link #mSearchMode}.
55      *
56      * In {@link #MODE_NORMAL} mode, we don't show the search box.
57      * In {@link #MODE_SEARCH} mode, we do show the search box.
58      * The action bar doesn't really care if the activity is showing search results.
59      * If the activity is showing search results, and the {@link Callback#onSearchExit} is called,
60      * the activity probably wants to close itself, but this class doesn't make the desision.
61      */
62     private static final int MODE_NORMAL = 0;
63     private static final int MODE_SEARCH = 1;
64 
65     private static final int LOADER_ID_ACCOUNT_LIST
66             = EmailActivity.ACTION_BAR_CONTROLLER_LOADER_ID_BASE + 0;
67 
68     private static final int DISPLAY_TITLE_MULTIPLE_LINES = 0x20;
69     private static final int ACTION_BAR_MASK =
70             ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_CUSTOM
71                     | ActionBar.DISPLAY_SHOW_TITLE | DISPLAY_TITLE_MULTIPLE_LINES;
72     private static final int CUSTOM_ACTION_BAR_OPTIONS =
73             ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_CUSTOM;
74 
75     private final Context mContext;
76     private final LoaderManager mLoaderManager;
77     private final ActionBar mActionBar;
78     private final DelayedOperations mDelayedOperations;
79 
80     /** "Folders" label shown with account name on 1-pane mailbox list */
81     private final String mAllFoldersLabel;
82 
83     private final ViewGroup mActionBarCustomView;
84     private final ViewGroup mAccountSpinnerContainer;
85     private final View mAccountSpinner;
86     private final Drawable mAccountSpinnerDefaultBackground;
87     private final TextView mAccountSpinnerLine1View;
88     private final TextView mAccountSpinnerLine2View;
89     private final TextView mAccountSpinnerCountView;
90 
91     private View mSearchContainer;
92     private SearchView mSearchView;
93 
94     private final AccountDropdownPopup mAccountDropdown;
95 
96     private final AccountSelectorAdapter mAccountsSelectorAdapter;
97 
98     private AccountSelectorAdapter.CursorWithExtras mCursor;
99 
100     /** The current account ID; used to determine if the account has changed. */
101     private long mLastAccountIdForDirtyCheck = Account.NO_ACCOUNT;
102 
103     /** The current mailbox ID; used to determine if the mailbox has changed. */
104     private long mLastMailboxIdForDirtyCheck = Mailbox.NO_MAILBOX;
105 
106     /** Either {@link #MODE_NORMAL} or {@link #MODE_SEARCH}. */
107     private int mSearchMode = MODE_NORMAL;
108 
109     /** The current title mode, which should be one of {@code Callback TITLE_MODE_*} */
110     private int mTitleMode;
111 
112     public final Callback mCallback;
113 
114     public interface SearchContext {
getTargetMailboxId()115         public long getTargetMailboxId();
116     }
117 
118     private static final int TITLE_MODE_SPINNER_ENABLED = 0x10;
119 
120     public interface Callback {
121         /** Values for {@link #getTitleMode}.  Show only account name */
122         public static final int TITLE_MODE_ACCOUNT_NAME_ONLY = 0 | TITLE_MODE_SPINNER_ENABLED;
123 
124         /**
125          * Show the current account name with "Folders"
126          * The account spinner will be disabled in this mode.
127          */
128         public static final int TITLE_MODE_ACCOUNT_WITH_ALL_FOLDERS_LABEL = 1;
129 
130         /**
131          * Show the current account name and the current mailbox name.
132          */
133         public static final int TITLE_MODE_ACCOUNT_WITH_MAILBOX = 2 | TITLE_MODE_SPINNER_ENABLED;
134         /**
135          * Show the current message subject.  Actual subject is obtained via
136          * {@link #getMessageSubject()}.
137          *
138          * The account spinner will be disabled in this mode.
139          */
140         public static final int TITLE_MODE_MESSAGE_SUBJECT = 3;
141 
142         /** @return true if an account is selected. */
isAccountSelected()143         public boolean isAccountSelected();
144 
145         /**
146          * @return currently selected account ID, {@link Account#ACCOUNT_ID_COMBINED_VIEW},
147          * or -1 if no account is selected.
148          */
getUIAccountId()149         public long getUIAccountId();
150 
151         /**
152          * @return currently selected mailbox ID, or {@link Mailbox#NO_MAILBOX} if no mailbox is
153          * selected.
154          */
getMailboxId()155         public long getMailboxId();
156 
157         /**
158          * @return constants such as {@link #TITLE_MODE_ACCOUNT_NAME_ONLY}.
159          */
getTitleMode()160         public int getTitleMode();
161 
162         /** @see #TITLE_MODE_MESSAGE_SUBJECT */
getMessageSubject()163         public String getMessageSubject();
164 
165         /** @return the "UP" arrow should be shown. */
shouldShowUp()166         public boolean shouldShowUp();
167 
168         /**
169          * Called when an account is selected on the account spinner.
170          * @param accountId ID of the selected account, or {@link Account#ACCOUNT_ID_COMBINED_VIEW}.
171          */
onAccountSelected(long accountId)172         public void onAccountSelected(long accountId);
173 
174         /**
175          * Invoked when a recent mailbox is selected on the account spinner.
176          *
177          * @param accountId ID of the selected account, or {@link Account#ACCOUNT_ID_COMBINED_VIEW}.
178          * @param mailboxId The ID of the selected mailbox, or {@link Mailbox#NO_MAILBOX} if the
179          *          special option "show all mailboxes" was selected.
180          */
onMailboxSelected(long accountId, long mailboxId)181         public void onMailboxSelected(long accountId, long mailboxId);
182 
183         /** Called when no accounts are found in the database. */
onNoAccountsFound()184         public void onNoAccountsFound();
185 
186         /**
187          * Retrieves the hint text to be shown for when a search entry is being made.
188          */
getSearchHint()189         public String getSearchHint();
190 
191         /**
192          * Called when the action bar initially shows the search entry field.
193          */
onSearchStarted()194         public void onSearchStarted();
195 
196         /**
197          * Called when a search is submitted.
198          *
199          * @param queryTerm query string
200          */
onSearchSubmit(String queryTerm)201         public void onSearchSubmit(String queryTerm);
202 
203         /**
204          * Called when the search box is closed.
205          */
onSearchExit()206         public void onSearchExit();
207 
onUpPressed()208         public void onUpPressed();
209     }
210 
ActionBarController(Context context, LoaderManager loaderManager, ActionBar actionBar, Callback callback)211     public ActionBarController(Context context, LoaderManager loaderManager,
212             ActionBar actionBar, Callback callback) {
213         mContext = context;
214         mLoaderManager = loaderManager;
215         mActionBar = actionBar;
216         mCallback = callback;
217         mDelayedOperations = new DelayedOperations(Utility.getMainThreadHandler());
218         mAllFoldersLabel = mContext.getResources().getString(
219                 R.string.action_bar_mailbox_list_title);
220         mAccountsSelectorAdapter = new AccountSelectorAdapter(mContext);
221 
222         // Configure action bar.
223         enterCustomActionBarMode();
224 
225         // Prepare the custom view
226         mActionBar.setCustomView(R.layout.action_bar_custom_view);
227         mActionBarCustomView = (ViewGroup) mActionBar.getCustomView();
228 
229         // Account spinner
230         mAccountSpinnerContainer =
231                 UiUtilities.getView(mActionBarCustomView, R.id.account_spinner_container);
232         mAccountSpinner = UiUtilities.getView(mActionBarCustomView, R.id.account_spinner);
233         mAccountSpinnerDefaultBackground = mAccountSpinner.getBackground();
234 
235         mAccountSpinnerLine1View = UiUtilities.getView(mActionBarCustomView, R.id.spinner_line_1);
236         mAccountSpinnerLine2View = UiUtilities.getView(mActionBarCustomView, R.id.spinner_line_2);
237         mAccountSpinnerCountView = UiUtilities.getView(mActionBarCustomView, R.id.spinner_count);
238 
239         // Account dropdown
240         mAccountDropdown = new AccountDropdownPopup(mContext);
241         mAccountDropdown.setAdapter(mAccountsSelectorAdapter);
242 
243         mAccountSpinner.setOnClickListener(new View.OnClickListener() {
244             @Override public void onClick(View v) {
245                 if (mAccountsSelectorAdapter.getCount() > 0) {
246                     mAccountDropdown.show();
247                 }
248             }
249         });
250         // this other click listener handles clicks that ought to be aliased to "up"
251         // only one or the other listener should be active at any time
252         mActionBarCustomView.setOnClickListener(new View.OnClickListener() {
253             @Override
254             public void onClick(View v) {
255                 mCallback.onUpPressed();
256             }
257         });
258         // pick a sane default. later enabled in updateTitle().
259         mActionBarCustomView.setClickable(false);
260     }
261 
initSearchViews()262     private void initSearchViews() {
263         if (mSearchContainer == null) {
264             final LayoutInflater inflater = LayoutInflater.from(mContext);
265             mSearchContainer = inflater.inflate(R.layout.action_bar_search, null);
266             mSearchView = UiUtilities.getView(mSearchContainer, R.id.search_view);
267             mSearchView.setSubmitButtonEnabled(false);
268             mSearchView.setOnQueryTextListener(mOnQueryText);
269             mSearchView.onActionViewExpanded();
270             mActionBarCustomView.addView(mSearchContainer);
271         }
272     }
273 
274 
275     /** Must be called from {@link UIControllerBase#onActivityCreated()} */
onActivityCreated()276     public void onActivityCreated() {
277         refresh();
278     }
279 
280     /** Must be called from {@link UIControllerBase#onActivityDestroy()} */
onActivityDestroy()281     public void onActivityDestroy() {
282         if (mAccountDropdown.isShowing()) {
283             mAccountDropdown.dismiss();
284         }
285     }
286 
287     /** Must be called from {@link UIControllerBase#onSaveInstanceState} */
onSaveInstanceState(Bundle outState)288     public void onSaveInstanceState(Bundle outState) {
289         mDelayedOperations.removeCallbacks(); // Remove all pending operations
290         outState.putInt(BUNDLE_KEY_MODE, mSearchMode);
291     }
292 
293     /** Must be called from {@link UIControllerBase#onRestoreInstanceState} */
onRestoreInstanceState(Bundle savedState)294     public void onRestoreInstanceState(Bundle savedState) {
295         int mode = savedState.getInt(BUNDLE_KEY_MODE);
296         if (mode == MODE_SEARCH) {
297             // No need to re-set the initial query, as the View tree restoration does that
298             enterSearchMode(null);
299         }
300     }
301 
302     /**
303      * @return true if the search box is shown.
304      */
isInSearchMode()305     public boolean isInSearchMode() {
306         return mSearchMode == MODE_SEARCH;
307     }
308 
309     /**
310      * @return Whether or not the search bar should be shown. This is a function of whether or not a
311      *     search is active, and if the current layout supports it.
312      */
shouldShowSearchBar()313     private boolean shouldShowSearchBar() {
314         return isInSearchMode() && (mTitleMode != Callback.TITLE_MODE_MESSAGE_SUBJECT);
315     }
316 
317     /**
318      * Show the search box.
319      *
320      * @param initialQueryTerm if non-empty, set to the search box.
321      */
enterSearchMode(String initialQueryTerm)322     public void enterSearchMode(String initialQueryTerm) {
323         initSearchViews();
324         if (isInSearchMode()) {
325             return;
326         }
327         if (!TextUtils.isEmpty(initialQueryTerm)) {
328             mSearchView.setQuery(initialQueryTerm, false);
329         } else {
330             mSearchView.setQuery("", false);
331         }
332         mSearchView.setQueryHint(mCallback.getSearchHint());
333 
334         mSearchMode = MODE_SEARCH;
335 
336         // Focus on the search input box and throw up the IME if specified.
337         // TODO: HACK. this is a workaround IME not popping up.
338         mSearchView.setIconified(false);
339 
340         refresh();
341         mCallback.onSearchStarted();
342     }
343 
exitSearchMode()344     public void exitSearchMode() {
345         if (!isInSearchMode()) {
346             return;
347         }
348         mSearchMode = MODE_NORMAL;
349 
350         refresh();
351         mCallback.onSearchExit();
352     }
353 
354     /**
355      * Performs the back action.
356      *
357      * @param isSystemBackKey <code>true</code> if the system back key was pressed.
358      * <code>false</code> if it's caused by the "home" icon click on the action bar.
359      */
onBackPressed(boolean isSystemBackKey)360     public boolean onBackPressed(boolean isSystemBackKey) {
361         if (shouldShowSearchBar()) {
362             exitSearchMode();
363             return true;
364         }
365         return false;
366     }
367 
368     /** Refreshes the action bar display. */
refresh()369     public void refresh() {
370         // The actual work is in refreshInernal(), but we don't call it directly here, because:
371         // 1. refresh() is called very often.
372         // 2. to avoid nested fragment transaction.
373         //    refresh is often called during a fragment transaction, but updateTitle() may call
374         //    a callback which would initiate another fragment transaction.
375         mDelayedOperations.removeCallbacks(mRefreshRunnable);
376         mDelayedOperations.post(mRefreshRunnable);
377     }
378 
379     private final Runnable mRefreshRunnable = new Runnable() {
380         @Override public void run() {
381             refreshInernal();
382         }
383     };
refreshInernal()384     private void refreshInernal() {
385         final boolean showUp = isInSearchMode() || mCallback.shouldShowUp();
386         mActionBar.setDisplayOptions(showUp
387                 ? ActionBar.DISPLAY_HOME_AS_UP : 0, ActionBar.DISPLAY_HOME_AS_UP);
388 
389         final long accountId = mCallback.getUIAccountId();
390         final long mailboxId = mCallback.getMailboxId();
391         if ((mLastAccountIdForDirtyCheck != accountId)
392                 || (mLastMailboxIdForDirtyCheck != mailboxId)) {
393             mLastAccountIdForDirtyCheck = accountId;
394             mLastMailboxIdForDirtyCheck = mailboxId;
395 
396             if (accountId != Account.NO_ACCOUNT) {
397                 loadAccountMailboxInfo(accountId, mailboxId);
398             }
399         }
400 
401         updateTitle();
402     }
403 
404     /**
405      * Load account/mailbox info, and account/recent mailbox list.
406      */
loadAccountMailboxInfo(final long accountId, final long mailboxId)407     private void loadAccountMailboxInfo(final long accountId, final long mailboxId) {
408         mLoaderManager.restartLoader(LOADER_ID_ACCOUNT_LIST, null,
409                 new LoaderCallbacks<Cursor>() {
410             @Override
411             public Loader<Cursor> onCreateLoader(int id, Bundle args) {
412                 return AccountSelectorAdapter.createLoader(mContext, accountId, mailboxId);
413             }
414 
415             @Override
416             public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
417                 mCursor = (AccountSelectorAdapter.CursorWithExtras) data;
418                 updateTitle();
419             }
420 
421             @Override
422             public void onLoaderReset(Loader<Cursor> loader) {
423                 mCursor = null;
424                 updateTitle();
425             }
426         });
427     }
428 
429     /**
430      * Update the "title" part.
431      */
updateTitle()432     private void updateTitle() {
433         mAccountsSelectorAdapter.swapCursor(mCursor);
434 
435         enterCustomActionBarMode();
436         if (mCursor == null) {
437             // Initial load not finished.
438             mActionBarCustomView.setVisibility(View.GONE);
439             return;
440         }
441         mActionBarCustomView.setVisibility(View.VISIBLE);
442 
443         if (mCursor.getAccountCount() == 0) {
444             mCallback.onNoAccountsFound();
445             return;
446         }
447 
448         if ((mCursor.getAccountId() != Account.NO_ACCOUNT) && !mCursor.accountExists()) {
449             // Account specified, but does not exist.
450             if (isInSearchMode()) {
451                 exitSearchMode();
452             }
453 
454             // Switch to the default account.
455             mCallback.onAccountSelected(Account.getDefaultAccountId(mContext));
456             return;
457         }
458 
459         mTitleMode = mCallback.getTitleMode();
460 
461         if (shouldShowSearchBar()) {
462             initSearchViews();
463             // In search mode, the search box is a replacement of the account spinner, so ignore
464             // the work needed to update that. It will get updated when it goes visible again.
465             mAccountSpinnerContainer.setVisibility(View.GONE);
466             mSearchContainer.setVisibility(View.VISIBLE);
467             return;
468         }
469 
470         // Account spinner visible.
471         mAccountSpinnerContainer.setVisibility(View.VISIBLE);
472         UiUtilities.setVisibilitySafe(mSearchContainer, View.GONE);
473 
474         if (mTitleMode == Callback.TITLE_MODE_MESSAGE_SUBJECT) {
475             // Use two line title action bar mode
476             enterMultiLineTitleActionBarMode();
477             String subject = mCallback.getMessageSubject();
478             if (subject == null) {
479                 subject = "";
480             }
481             final SpannableString title = new SpannableString(subject);
482             final SpannableStringBuilder builder = new SpannableStringBuilder();
483             title.setSpan(new TextAppearanceSpan(mContext, R.style.subject_action_bar_title_text),
484                     0, subject.length(), 0);
485             builder.append(title);
486             mActionBar.setTitle(builder);
487             mActionBar.setSubtitle(null);
488         } else if (mTitleMode == Callback.TITLE_MODE_ACCOUNT_WITH_ALL_FOLDERS_LABEL) {
489             enterSingleLineTitleActionBarMode();
490             mActionBar.setTitle(mAllFoldersLabel);
491             mActionBar.setSubtitle(mCursor.getAccountDisplayName());
492         } else {
493             // Get mailbox name
494             final String mailboxName;
495             if (mTitleMode == Callback.TITLE_MODE_ACCOUNT_WITH_MAILBOX) {
496                 mailboxName = mCursor.getMailboxDisplayName();
497             } else {
498                 mailboxName = null;
499             }
500 
501             // Note - setSingleLine is needed as well as setMaxLines since they set different
502             // flags on the view.
503             mAccountSpinnerLine1View.setSingleLine();
504             mAccountSpinnerLine1View.setMaxLines(1);
505             if (TextUtils.isEmpty(mailboxName)) {
506                 mAccountSpinnerLine1View.setText(mCursor.getAccountDisplayName());
507 
508                 // Change the visibility of line 2, so line 1 will be vertically-centered.
509                 mAccountSpinnerLine2View.setVisibility(View.GONE);
510             } else {
511                 mAccountSpinnerLine1View.setText(mailboxName);
512                 mAccountSpinnerLine2View.setVisibility(View.VISIBLE);
513                 mAccountSpinnerLine2View.setText(mCursor.getAccountDisplayName());
514             }
515 
516             mAccountSpinnerCountView.setVisibility(View.VISIBLE);
517             mAccountSpinnerCountView.setText(UiUtilities.getMessageCountForUi(
518                     mContext, mCursor.getMailboxMessageCount(), true));
519         }
520 
521         boolean spinnerEnabled =
522             ((mTitleMode & TITLE_MODE_SPINNER_ENABLED) != 0) && mCursor.shouldEnableSpinner();
523 
524         setSpinnerEnabled(spinnerEnabled);
525     }
526 
enterCustomActionBarMode()527     private void enterCustomActionBarMode() {
528         mActionBar.setDisplayOptions(CUSTOM_ACTION_BAR_OPTIONS, ACTION_BAR_MASK);
529     }
530 
enterMultiLineTitleActionBarMode()531     private void enterMultiLineTitleActionBarMode() {
532         mActionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE
533                 | DISPLAY_TITLE_MULTIPLE_LINES, ACTION_BAR_MASK);
534     }
535 
enterSingleLineTitleActionBarMode()536     private void enterSingleLineTitleActionBarMode() {
537         mActionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE,
538                 ACTION_BAR_MASK);
539     }
540 
setSpinnerEnabled(boolean enabled)541     private void setSpinnerEnabled(boolean enabled) {
542         if (enabled == mAccountSpinner.isEnabled()) {
543             return;
544         }
545 
546         mAccountSpinner.setEnabled(enabled);
547         mAccountSpinner.setClickable(enabled);
548         mActionBarCustomView.setClickable(!enabled);
549         if (enabled) {
550             mAccountSpinner.setBackgroundDrawable(mAccountSpinnerDefaultBackground);
551         } else {
552             mAccountSpinner.setBackgroundDrawable(null);
553         }
554 
555         // For some reason, changing the background mucks with the padding so we have to manually
556         // reset vertical padding here (also specified in XML, but it seems to be ignored for
557         // some reason.
558         mAccountSpinner.setPadding(
559                 mAccountSpinner.getPaddingLeft(),
560                 0,
561                 mAccountSpinner.getPaddingRight(),
562                 0);
563     }
564 
565 
566     private final SearchView.OnQueryTextListener mOnQueryText
567             = new SearchView.OnQueryTextListener() {
568         @Override
569         public boolean onQueryTextChange(String newText) {
570             // Event not handled.  Let the search do the default action.
571             return false;
572         }
573 
574         @Override
575         public boolean onQueryTextSubmit(String query) {
576             mCallback.onSearchSubmit(mSearchView.getQuery().toString());
577             return true; // Event handled.
578         }
579     };
580 
onAccountSpinnerItemClicked(int position)581     private void onAccountSpinnerItemClicked(int position) {
582         if (mAccountsSelectorAdapter == null) { // just in case...
583             return;
584         }
585         final long accountId = mAccountsSelectorAdapter.getAccountId(position);
586 
587         if (mAccountsSelectorAdapter.isAccountItem(position)) {
588             mCallback.onAccountSelected(accountId);
589         } else if (mAccountsSelectorAdapter.isMailboxItem(position)) {
590             mCallback.onMailboxSelected(accountId,
591                     mAccountsSelectorAdapter.getId(position));
592         }
593     }
594 
595     // Based on Spinner.DropdownPopup
596     private class AccountDropdownPopup extends ListPopupWindow {
AccountDropdownPopup(Context context)597         public AccountDropdownPopup(Context context) {
598             super(context);
599             setAnchorView(mAccountSpinner);
600             setModal(true);
601             setPromptPosition(POSITION_PROMPT_ABOVE);
602             setOnItemClickListener(new OnItemClickListener() {
603                 public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
604                     onAccountSpinnerItemClicked(position);
605                     dismiss();
606                 }
607             });
608         }
609 
610         @Override
show()611         public void show() {
612             setWidth(mContext.getResources().getDimensionPixelSize(
613                     R.dimen.account_dropdown_dropdownwidth));
614             setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
615             super.show();
616             // List view is instantiated in super.show(), so we need to do this after...
617             getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
618         }
619     }
620 }
621