Home | History | Annotate | Download | only in activity
      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 {
    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. */
    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          */
    149         public long getUIAccountId();
    150 
    151         /**
    152          * @return currently selected mailbox ID, or {@link Mailbox#NO_MAILBOX} if no mailbox is
    153          * selected.
    154          */
    155         public long getMailboxId();
    156 
    157         /**
    158          * @return constants such as {@link #TITLE_MODE_ACCOUNT_NAME_ONLY}.
    159          */
    160         public int getTitleMode();
    161 
    162         /** @see #TITLE_MODE_MESSAGE_SUBJECT */
    163         public String getMessageSubject();
    164 
    165         /** @return the "UP" arrow should be shown. */
    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          */
    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          */
    181         public void onMailboxSelected(long accountId, long mailboxId);
    182 
    183         /** Called when no accounts are found in the database. */
    184         public void onNoAccountsFound();
    185 
    186         /**
    187          * Retrieves the hint text to be shown for when a search entry is being made.
    188          */
    189         public String getSearchHint();
    190 
    191         /**
    192          * Called when the action bar initially shows the search entry field.
    193          */
    194         public void onSearchStarted();
    195 
    196         /**
    197          * Called when a search is submitted.
    198          *
    199          * @param queryTerm query string
    200          */
    201         public void onSearchSubmit(String queryTerm);
    202 
    203         /**
    204          * Called when the search box is closed.
    205          */
    206         public void onSearchExit();
    207 
    208         public void onUpPressed();
    209     }
    210 
    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 
    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()} */
    276     public void onActivityCreated() {
    277         refresh();
    278     }
    279 
    280     /** Must be called from {@link UIControllerBase#onActivityDestroy()} */
    281     public void onActivityDestroy() {
    282         if (mAccountDropdown.isShowing()) {
    283             mAccountDropdown.dismiss();
    284         }
    285     }
    286 
    287     /** Must be called from {@link UIControllerBase#onSaveInstanceState} */
    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} */
    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      */
    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      */
    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      */
    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 
    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      */
    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. */
    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     };
    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      */
    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      */
    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 
    527     private void enterCustomActionBarMode() {
    528         mActionBar.setDisplayOptions(CUSTOM_ACTION_BAR_OPTIONS, ACTION_BAR_MASK);
    529     }
    530 
    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 
    536     private void enterSingleLineTitleActionBarMode() {
    537         mActionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE,
    538                 ACTION_BAR_MASK);
    539     }
    540 
    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 
    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 {
    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
    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