Home | History | Annotate | Download | only in activity
      1 /*
      2  * Copyright (C) 2010 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.content.ContentResolver;
     20 import android.content.ContentUris;
     21 import android.content.Context;
     22 import android.content.Loader;
     23 import android.database.Cursor;
     24 import android.database.MatrixCursor;
     25 import android.view.LayoutInflater;
     26 import android.view.View;
     27 import android.view.ViewGroup;
     28 import android.widget.AdapterView;
     29 import android.widget.CursorAdapter;
     30 import android.widget.TextView;
     31 
     32 import com.android.email.FolderProperties;
     33 import com.android.email.R;
     34 import com.android.email.ResourceHelper;
     35 import com.android.email.data.ClosingMatrixCursor;
     36 import com.android.email.data.ThrottlingCursorLoader;
     37 import com.android.emailcommon.provider.Account;
     38 import com.android.emailcommon.provider.EmailContent;
     39 import com.android.emailcommon.provider.EmailContent.AccountColumns;
     40 import com.android.emailcommon.provider.EmailContent.MailboxColumns;
     41 import com.android.emailcommon.provider.Mailbox;
     42 import com.android.emailcommon.utility.Utility;
     43 import com.google.common.annotations.VisibleForTesting;
     44 import com.google.common.base.Preconditions;
     45 
     46 import java.util.ArrayList;
     47 import java.util.Collection;
     48 
     49 /**
     50  * Account selector spinner.
     51  *
     52  * TODO Test it!
     53  */
     54 public class AccountSelectorAdapter extends CursorAdapter {
     55     /** meta data column for an message count (unread or total, depending on row) */
     56     private static final String MESSAGE_COUNT = "unreadCount";
     57 
     58     /** meta data column for the row type; used for display purposes */
     59     private static final String ROW_TYPE = "rowType";
     60 
     61     /** meta data position of the currently selected account in the drop-down list */
     62     private static final String ACCOUNT_POSITION = "accountPosition";
     63 
     64     /** "account id" virtual column name for the matrix cursor */
     65     private static final String ACCOUNT_ID = "accountId";
     66 
     67     private static final int ROW_TYPE_HEADER = AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
     68     @SuppressWarnings("unused")
     69     private static final int ROW_TYPE_MAILBOX = 0;
     70     private static final int ROW_TYPE_ACCOUNT = 1;
     71     private static final int ITEM_VIEW_TYPE_ACCOUNT = 0;
     72     static final int UNKNOWN_POSITION = -1;
     73     /** Projection for account database query */
     74     private static final String[] ACCOUNT_PROJECTION = new String[] {
     75         EmailContent.RECORD_ID,
     76         Account.DISPLAY_NAME,
     77         Account.EMAIL_ADDRESS,
     78     };
     79     /**
     80      * Projection used for the selector display; we add meta data that doesn't exist in the
     81      * account database, so, this should be a super-set of {@link #ACCOUNT_PROJECTION}.
     82      */
     83     private static final String[] ADAPTER_PROJECTION = new String[] {
     84         ROW_TYPE,
     85         EmailContent.RECORD_ID,
     86         Account.DISPLAY_NAME,
     87         Account.EMAIL_ADDRESS,
     88         MESSAGE_COUNT,
     89         ACCOUNT_POSITION, // TODO Probably we don't really need this
     90         ACCOUNT_ID,
     91     };
     92 
     93     /** Sort order.  Show the default account first. */
     94     private static final String ORDER_BY = Account.IS_DEFAULT + " desc, " + Account.RECORD_ID;
     95 
     96     @SuppressWarnings("hiding")
     97     private final Context mContext;
     98     private final LayoutInflater mInflater;
     99     private final ResourceHelper mResourceHelper;
    100 
    101     /**
    102      * Returns a loader that can populate the account spinner.
    103      * @param context a context
    104      * @param accountId the ID of the currently viewed account
    105      */
    106     public static Loader<Cursor> createLoader(Context context, long accountId, long mailboxId) {
    107         return new AccountsLoader(context, accountId, mailboxId, UiUtilities.useTwoPane(context));
    108     }
    109 
    110     public AccountSelectorAdapter(Context context) {
    111         super(context, null, 0 /* no auto-requery */);
    112         mContext = context;
    113         mInflater = LayoutInflater.from(context);
    114         mResourceHelper = ResourceHelper.getInstance(context);
    115     }
    116 
    117     /**
    118      * {@inheritDoc}
    119      *
    120      * The account selector view can contain one of four types of row data:
    121      * <ol>
    122      * <li>headers</li>
    123      * <li>accounts</li>
    124      * <li>recent mailboxes</li>
    125      * <li>"show all folders"</li>
    126      * </ol>
    127      * Headers are handled separately as they have a unique layout and cannot be interacted with.
    128      * Accounts, recent mailboxes and "show all folders" all have the same interaction model and
    129      * share a very similar layout. The single difference is that both accounts and recent
    130      * mailboxes display an unread count; whereas "show all folders" does not. To determine
    131      * if a particular row is "show all folders" verify that a) it's not an account row and
    132      * b) it's ID is {@link Mailbox#NO_MAILBOX}.
    133      *
    134      * TODO Use recycled views.  ({@link #getViewTypeCount} and {@link #getItemViewType})
    135      */
    136     @Override
    137     public View getView(int position, View convertView, ViewGroup parent) {
    138         Cursor c = getCursor();
    139         c.moveToPosition(position);
    140         View view;
    141         if (c.getInt(c.getColumnIndex(ROW_TYPE)) == ROW_TYPE_HEADER) {
    142             view = mInflater.inflate(R.layout.action_bar_spinner_dropdown_header, parent, false);
    143             final TextView displayNameView = (TextView) view.findViewById(R.id.display_name);
    144             final String displayName = getDisplayName(c);
    145             displayNameView.setText(displayName);
    146         } else {
    147             view = mInflater.inflate(R.layout.action_bar_spinner_dropdown, parent, false);
    148             final TextView displayNameView = (TextView) view.findViewById(R.id.display_name);
    149             final TextView emailAddressView = (TextView) view.findViewById(R.id.email_address);
    150             final TextView unreadCountView = (TextView) view.findViewById(R.id.unread_count);
    151             final View chipView = view.findViewById(R.id.color_chip);
    152 
    153             final String displayName = getDisplayName(c);
    154             final String emailAddress = getAccountEmailAddress(c);
    155 
    156             displayNameView.setText(displayName);
    157 
    158             // Show the email address only when it's different from the display name.
    159             boolean isAccount = isAccountItem(c);
    160             if (displayName.equals(emailAddress) || !isAccount) {
    161                 emailAddressView.setVisibility(View.GONE);
    162             } else {
    163                 emailAddressView.setVisibility(View.VISIBLE);
    164                 emailAddressView.setText(emailAddress);
    165             }
    166 
    167             long id = getId(c);
    168             if (isAccount || id != Mailbox.NO_MAILBOX) {
    169                 unreadCountView.setVisibility(View.VISIBLE);
    170                 unreadCountView.setText(UiUtilities.getMessageCountForUi(mContext,
    171                         getAccountUnreadCount(c), true));
    172 
    173                 // If we're on a combined account, show the color chip indicators for all real
    174                 // accounts so it can be used as a legend.
    175                 boolean isCombinedActive =
    176                         ((CursorWithExtras) c).getAccountId() == Account.ACCOUNT_ID_COMBINED_VIEW;
    177 
    178                 if (isCombinedActive && Account.isNormalAccount(id)) {
    179                     chipView.setBackgroundColor(mResourceHelper.getAccountColor(id));
    180                     chipView.setVisibility(View.VISIBLE);
    181                 } else {
    182                     chipView.setVisibility(View.GONE);
    183                 }
    184             } else {
    185                 unreadCountView.setVisibility(View.INVISIBLE);
    186                 chipView.setVisibility(View.GONE);
    187             }
    188 
    189         }
    190         return view;
    191     }
    192 
    193     @Override
    194     public View newView(Context context, Cursor cursor, ViewGroup parent) {
    195         return null; // we don't reuse views.  This method never gets called.
    196     }
    197 
    198     @Override
    199     public void bindView(View view, Context context, Cursor cursor) {
    200         // we don't reuse views.  This method never gets called.
    201     }
    202 
    203     @Override
    204     public int getViewTypeCount() {
    205         return 2;
    206     }
    207 
    208     @Override
    209     public int getItemViewType(int position) {
    210         Cursor c = getCursor();
    211         c.moveToPosition(position);
    212         return c.getLong(c.getColumnIndex(ROW_TYPE)) == ROW_TYPE_HEADER
    213                 ? AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER
    214                 : ITEM_VIEW_TYPE_ACCOUNT;
    215     }
    216 
    217     @Override
    218     public boolean areAllItemsEnabled() {
    219         return false;
    220     }
    221 
    222     @Override
    223     public boolean isEnabled(int position) {
    224         return (getItemViewType(position) != AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER);
    225     }
    226 
    227     public boolean isAccountItem(int position) {
    228         Cursor c = getCursor();
    229         c.moveToPosition(position);
    230         return isAccountItem(c);
    231     }
    232 
    233     public boolean isAccountItem(Cursor c) {
    234         return (c.getLong(c.getColumnIndex(ROW_TYPE)) == ROW_TYPE_ACCOUNT);
    235     }
    236 
    237     public boolean isMailboxItem(int position) {
    238         Cursor c = getCursor();
    239         c.moveToPosition(position);
    240         return (c.getLong(c.getColumnIndex(ROW_TYPE)) == ROW_TYPE_MAILBOX);
    241     }
    242 
    243     private int getAccountUnreadCount(Cursor c) {
    244         return getMessageCount(c);
    245     }
    246 
    247     /**
    248      * Returns the account/mailbox ID extracted from the given cursor.
    249      */
    250     private static long getId(Cursor c) {
    251         return c.getLong(c.getColumnIndex(EmailContent.RECORD_ID));
    252     }
    253 
    254     /**
    255      * @return ID of the account / mailbox for a row
    256      */
    257     public long getId(int position) {
    258         final Cursor c = getCursor();
    259         return c.moveToPosition(position) ? getId(c) : Account.NO_ACCOUNT;
    260     }
    261 
    262     /**
    263      * @return ID of the account for a row
    264      */
    265     public long getAccountId(int position) {
    266         final Cursor c = getCursor();
    267         return c.moveToPosition(position)
    268                 ? c.getLong(c.getColumnIndex(ACCOUNT_ID))
    269                 : Account.NO_ACCOUNT;
    270     }
    271 
    272     /** Returns the account name extracted from the given cursor. */
    273     static String getDisplayName(Cursor cursor) {
    274         return cursor.getString(cursor.getColumnIndex(Account.DISPLAY_NAME));
    275     }
    276 
    277     /** Returns the email address extracted from the given cursor. */
    278     private static String getAccountEmailAddress(Cursor cursor) {
    279         return cursor.getString(cursor.getColumnIndex(Account.EMAIL_ADDRESS));
    280     }
    281 
    282     /**
    283      * Returns the message count (unread or total, depending on row) extracted from the given
    284      * cursor.
    285      */
    286     private static int getMessageCount(Cursor cursor) {
    287         return cursor.getInt(cursor.getColumnIndex(MESSAGE_COUNT));
    288     }
    289 
    290     private static String sCombinedViewDisplayName;
    291     private static String getCombinedViewDisplayName(Context c) {
    292         if (sCombinedViewDisplayName == null) {
    293             sCombinedViewDisplayName = c.getResources().getString(
    294                     R.string.mailbox_list_account_selector_combined_view);
    295         }
    296         return sCombinedViewDisplayName;
    297     }
    298 
    299     /**
    300      * Load the account list.  The resulting cursor contains
    301      * - Account info
    302      * - # of unread messages in inbox
    303      * - The "Combined view" row if there's more than one account.
    304      */
    305     @VisibleForTesting
    306     static class AccountsLoader extends ThrottlingCursorLoader {
    307         private final Context mContext;
    308         private final long mAccountId;
    309         private final long mMailboxId;
    310         private final boolean mUseTwoPane; // Injectable for test
    311         private final FolderProperties mFolderProperties;
    312 
    313         @VisibleForTesting
    314         AccountsLoader(Context context, long accountId, long mailboxId, boolean useTwoPane) {
    315             // Super class loads a regular account cursor, but we replace it in loadInBackground().
    316             super(context, Account.CONTENT_URI, ACCOUNT_PROJECTION, null, null,
    317                     ORDER_BY);
    318             mContext = context;
    319             mAccountId = accountId;
    320             mMailboxId = mailboxId;
    321             mFolderProperties = FolderProperties.getInstance(mContext);
    322             mUseTwoPane = useTwoPane;
    323         }
    324 
    325         @Override
    326         public Cursor loadInBackground() {
    327             final Cursor accountsCursor = super.loadInBackground();
    328             // Use ClosingMatrixCursor so that accountsCursor gets closed too when it's closed.
    329             final CursorWithExtras resultCursor
    330                     = new CursorWithExtras(ADAPTER_PROJECTION, accountsCursor);
    331             final int accountPosition = addAccountsToCursor(resultCursor, accountsCursor);
    332             addMailboxesToCursor(resultCursor, accountPosition);
    333 
    334             resultCursor.setAccountMailboxInfo(getContext(), mAccountId, mMailboxId);
    335             return resultCursor;
    336         }
    337 
    338         /** Adds the account list [with extra meta data] to the given matrix cursor */
    339         private int addAccountsToCursor(CursorWithExtras matrixCursor, Cursor accountCursor) {
    340             int accountPosition = UNKNOWN_POSITION;
    341             accountCursor.moveToPosition(-1);
    342 
    343             matrixCursor.mAccountCount = accountCursor.getCount();
    344             int totalUnread = 0;
    345             while (accountCursor.moveToNext()) {
    346                 // Add account, with its unread count.
    347                 final long accountId = accountCursor.getLong(0);
    348                 final int unread = Mailbox.getUnreadCountByAccountAndMailboxType(
    349                         mContext, accountId, Mailbox.TYPE_INBOX);
    350                 final String name = getDisplayName(accountCursor);
    351                 final String emailAddress = getAccountEmailAddress(accountCursor);
    352                 addRow(matrixCursor, ROW_TYPE_ACCOUNT, accountId, name, emailAddress, unread,
    353                     UNKNOWN_POSITION, accountId);
    354                 totalUnread += unread;
    355                 if (accountId == mAccountId) {
    356                     accountPosition = accountCursor.getPosition();
    357                 }
    358             }
    359             // Add "combined view" if more than one account exists
    360             final int countAccounts = accountCursor.getCount();
    361             if (countAccounts > 1) {
    362                 final String accountCount = mContext.getResources().getQuantityString(
    363                         R.plurals.number_of_accounts, countAccounts, countAccounts);
    364                 addRow(matrixCursor, ROW_TYPE_ACCOUNT, Account.ACCOUNT_ID_COMBINED_VIEW,
    365                         getCombinedViewDisplayName(mContext),
    366                         accountCount, totalUnread, UNKNOWN_POSITION,
    367                         Account.ACCOUNT_ID_COMBINED_VIEW);
    368 
    369                 // Increment the account count for the combined account.
    370                 matrixCursor.mAccountCount++;
    371             }
    372             return accountPosition;
    373         }
    374 
    375         /**
    376          * Adds the recent mailbox list / "show all folders" to the given cursor.
    377          *
    378          * @param matrixCursor the cursor to add the list to
    379          * @param accountPosition the cursor position of the currently selected account
    380          */
    381         private void addMailboxesToCursor(CursorWithExtras matrixCursor, int accountPosition) {
    382             if (mAccountId == Account.NO_ACCOUNT) {
    383                 return; // Account not selected
    384             }
    385             if (mAccountId == Account.ACCOUNT_ID_COMBINED_VIEW) {
    386                 if (!mUseTwoPane) {
    387                     // TODO We may want a header for this to separate it from the account list
    388                     addShowAllFoldersRow(matrixCursor, accountPosition);
    389                 }
    390                 return;
    391             }
    392             String emailAddress = null;
    393             if (accountPosition != UNKNOWN_POSITION) {
    394                 matrixCursor.moveToPosition(accountPosition);
    395                 emailAddress =
    396                         matrixCursor.getString(matrixCursor.getColumnIndex(Account.EMAIL_ADDRESS));
    397             }
    398             RecentMailboxManager mailboxManager = RecentMailboxManager.getInstance(mContext);
    399             ArrayList<Long> recentMailboxes = null;
    400             if (!mUseTwoPane) {
    401                 // Do not display recent mailboxes in the account spinner for the two pane view
    402                 recentMailboxes = mailboxManager.getMostRecent(mAccountId, mUseTwoPane);
    403             }
    404             final int recentCount = (recentMailboxes == null) ? 0 : recentMailboxes.size();
    405             matrixCursor.mRecentCount = recentCount;
    406 
    407             if (!mUseTwoPane) {
    408                 // "Recent mailboxes" header
    409                 addHeaderRow(matrixCursor, mContext.getString(
    410                         R.string.mailbox_list_account_selector_mailbox_header_fmt, emailAddress));
    411             }
    412 
    413             if (recentCount > 0) {
    414                 addMailboxRows(matrixCursor, accountPosition, recentMailboxes);
    415             }
    416 
    417             if (!mUseTwoPane) {
    418                 addShowAllFoldersRow(matrixCursor, accountPosition);
    419             }
    420         }
    421 
    422         private void addShowAllFoldersRow(CursorWithExtras matrixCursor, int accountPosition) {
    423             matrixCursor.mHasShowAllFolders = true;
    424             String name = mContext.getString(
    425                     R.string.mailbox_list_account_selector_show_all_folders);
    426             addRow(matrixCursor, ROW_TYPE_MAILBOX, Mailbox.NO_MAILBOX, name, null, 0,
    427                     accountPosition, mAccountId);
    428         }
    429 
    430 
    431         private static final String[] RECENT_MAILBOX_INFO_PROJECTION = new String[] {
    432             MailboxColumns.ID, MailboxColumns.DISPLAY_NAME, MailboxColumns.TYPE,
    433             MailboxColumns.UNREAD_COUNT, MailboxColumns.MESSAGE_COUNT
    434         };
    435 
    436         private void addMailboxRows(MatrixCursor matrixCursor, int accountPosition,
    437                 Collection<Long> mailboxIds) {
    438             Cursor c = mContext.getContentResolver().query(
    439                     Mailbox.CONTENT_URI, RECENT_MAILBOX_INFO_PROJECTION,
    440                     Utility.buildInSelection(MailboxColumns.ID, mailboxIds), null,
    441                     RecentMailboxManager.RECENT_MAILBOXES_SORT_ORDER);
    442             try {
    443                 c.moveToPosition(-1);
    444                 while (c.moveToNext()) {
    445                     addRow(matrixCursor, ROW_TYPE_MAILBOX,
    446                             c.getLong(c.getColumnIndex(MailboxColumns.ID)),
    447                             mFolderProperties.getDisplayName(c), null,
    448                             mFolderProperties.getMessageCount(c), accountPosition, mAccountId);
    449                 }
    450             } finally {
    451                 c.close();
    452             }
    453         }
    454 
    455         private void addHeaderRow(MatrixCursor cursor, String name) {
    456             addRow(cursor, ROW_TYPE_HEADER, 0L, name, null, 0, UNKNOWN_POSITION,
    457                     Account.NO_ACCOUNT);
    458         }
    459 
    460         /** Adds a row to the given cursor */
    461         private void addRow(MatrixCursor cursor, int rowType, long id, String name,
    462                 String emailAddress, int messageCount, int listPosition, long accountId) {
    463             cursor.newRow()
    464                 .add(rowType)
    465                 .add(id)
    466                 .add(name)
    467                 .add(emailAddress)
    468                 .add(messageCount)
    469                 .add(listPosition)
    470                 .add(accountId);
    471         }
    472     }
    473 
    474     /** Cursor with some extra meta data. */
    475     static class CursorWithExtras extends ClosingMatrixCursor {
    476 
    477         /** Number of account elements, including the combined account row. */
    478         private int mAccountCount;
    479         /** Number of recent mailbox elements */
    480         private int mRecentCount;
    481         private boolean mHasShowAllFolders;
    482 
    483         private boolean mAccountExists;
    484 
    485         /**
    486          * Account ID that's loaded.
    487          */
    488         private long mAccountId;
    489         private String mAccountDisplayName;
    490 
    491         /**
    492          * Mailbox ID that's loaded.
    493          */
    494         private long mMailboxId;
    495         private String mMailboxDisplayName;
    496         private int mMailboxMessageCount;
    497 
    498         @VisibleForTesting
    499         CursorWithExtras(String[] columnNames, Cursor innerCursor) {
    500             super(columnNames, innerCursor);
    501         }
    502 
    503         private static final String[] ACCOUNT_INFO_PROJECTION = new String[] {
    504             AccountColumns.DISPLAY_NAME,
    505         };
    506         private static final String[] MAILBOX_INFO_PROJECTION = new String[] {
    507             MailboxColumns.ID, MailboxColumns.DISPLAY_NAME, MailboxColumns.TYPE,
    508             MailboxColumns.UNREAD_COUNT, MailboxColumns.MESSAGE_COUNT
    509         };
    510 
    511         /**
    512          * Set the current account/mailbox info.
    513          */
    514         @VisibleForTesting
    515         void setAccountMailboxInfo(Context context, long accountId, long mailboxId) {
    516             mAccountId = accountId;
    517             mMailboxId = mailboxId;
    518 
    519             // Get account info
    520             if (accountId == Account.ACCOUNT_ID_COMBINED_VIEW) {
    521                 // We need to treat ACCOUNT_ID_COMBINED_VIEW specially...
    522                 mAccountExists = true;
    523                 mAccountDisplayName = getCombinedViewDisplayName(context);
    524                 if (mailboxId != Mailbox.NO_MAILBOX) {
    525                     setCombinedMailboxInfo(context, mailboxId);
    526                 }
    527                 return;
    528             }
    529 
    530             mAccountDisplayName = Utility.getFirstRowString(context,
    531                     ContentUris.withAppendedId(Account.CONTENT_URI, accountId),
    532                     ACCOUNT_INFO_PROJECTION, null, null, null, 0, null);
    533             if (mAccountDisplayName == null) {
    534                 // Account gone!
    535                 mAccountExists = false;
    536                 return;
    537             }
    538             mAccountExists = true;
    539 
    540             // If mailbox not specified, done.
    541             if (mMailboxId == Mailbox.NO_MAILBOX) {
    542                 return;
    543             }
    544             // Combined mailbox?
    545             // Unfortunately this can happen even when account != ACCOUNT_ID_COMBINED_VIEW,
    546             // when you open "starred" on 2-pane on non-combined view.
    547             if (mMailboxId < 0) {
    548                 setCombinedMailboxInfo(context, mailboxId);
    549                 return;
    550             }
    551 
    552             // Get mailbox info
    553             final ContentResolver r = context.getContentResolver();
    554             final Cursor mailboxCursor = r.query(
    555                     ContentUris.withAppendedId(Mailbox.CONTENT_URI, mailboxId),
    556                     MAILBOX_INFO_PROJECTION, null, null, null);
    557             try {
    558                 if (mailboxCursor.moveToFirst()) {
    559                     final FolderProperties fp = FolderProperties.getInstance(context);
    560                     mMailboxDisplayName = fp.getDisplayName(mailboxCursor);
    561                     mMailboxMessageCount = fp.getMessageCount(mailboxCursor);
    562                 }
    563             } finally {
    564                 mailboxCursor.close();
    565             }
    566         }
    567 
    568         private void setCombinedMailboxInfo(Context context, long mailboxId) {
    569             Preconditions.checkState(mailboxId < -1, "Not combined mailbox");
    570             mMailboxDisplayName = FolderProperties.getInstance(context)
    571                     .getCombinedMailboxName(mMailboxId);
    572 
    573             mMailboxMessageCount = FolderProperties.getMessageCountForCombinedMailbox(
    574                     context, mailboxId);
    575         }
    576 
    577         /**
    578          * Returns the cursor position of the item with the given ID. Or {@link #UNKNOWN_POSITION}
    579          * if the given ID does not exist.
    580          */
    581         int getPosition(long id) {
    582             moveToPosition(-1);
    583             while(moveToNext()) {
    584                 if (id == getId(this)) {
    585                     return getPosition();
    586                 }
    587             }
    588             return UNKNOWN_POSITION;
    589         }
    590 
    591         public int getAccountCount() {
    592             return mAccountCount;
    593         }
    594 
    595         @VisibleForTesting
    596         public int getRecentMailboxCount() {
    597             return mRecentCount;
    598         }
    599 
    600         /**
    601          * @return true if the cursor has more than one selectable item so we should enable the
    602          *     spinner.
    603          */
    604         public boolean shouldEnableSpinner() {
    605             return mHasShowAllFolders || (mAccountCount + mRecentCount > 1);
    606         }
    607 
    608         public long getAccountId() {
    609             return mAccountId;
    610         }
    611 
    612         public String getAccountDisplayName() {
    613             return mAccountDisplayName;
    614         }
    615 
    616         @VisibleForTesting
    617         public long getMailboxId() {
    618             return mMailboxId;
    619         }
    620 
    621         public String getMailboxDisplayName() {
    622             return mMailboxDisplayName;
    623         }
    624 
    625         public int getMailboxMessageCount() {
    626             return mMailboxMessageCount;
    627         }
    628 
    629         /**
    630          * @return {@code true} if the specified accuont exists.
    631          */
    632         public boolean accountExists() {
    633             return mAccountExists;
    634         }
    635     }
    636 }
    637