• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
createLoader(Context context, long accountId, long mailboxId)106     public static Loader<Cursor> createLoader(Context context, long accountId, long mailboxId) {
107         return new AccountsLoader(context, accountId, mailboxId, UiUtilities.useTwoPane(context));
108     }
109 
AccountSelectorAdapter(Context context)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
getView(int position, View convertView, ViewGroup parent)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
newView(Context context, Cursor cursor, ViewGroup parent)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
bindView(View view, Context context, Cursor cursor)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
getViewTypeCount()204     public int getViewTypeCount() {
205         return 2;
206     }
207 
208     @Override
getItemViewType(int position)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
isEnabled(int position)218     public boolean isEnabled(int position) {
219         return (getItemViewType(position) != AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER);
220     }
221 
isAccountItem(int position)222     public boolean isAccountItem(int position) {
223         Cursor c = getCursor();
224         c.moveToPosition(position);
225         return isAccountItem(c);
226     }
227 
isAccountItem(Cursor c)228     public boolean isAccountItem(Cursor c) {
229         return (c.getLong(c.getColumnIndex(ROW_TYPE)) == ROW_TYPE_ACCOUNT);
230     }
231 
isMailboxItem(int position)232     public boolean isMailboxItem(int position) {
233         Cursor c = getCursor();
234         c.moveToPosition(position);
235         return (c.getLong(c.getColumnIndex(ROW_TYPE)) == ROW_TYPE_MAILBOX);
236     }
237 
getAccountUnreadCount(Cursor c)238     private int getAccountUnreadCount(Cursor c) {
239         return getMessageCount(c);
240     }
241 
242     /**
243      * Returns the account/mailbox ID extracted from the given cursor.
244      */
getId(Cursor c)245     private static long getId(Cursor c) {
246         return c.getLong(c.getColumnIndex(EmailContent.RECORD_ID));
247     }
248 
249     /**
250      * @return ID of the account / mailbox for a row
251      */
getId(int position)252     public long getId(int position) {
253         final Cursor c = getCursor();
254         return c.moveToPosition(position) ? getId(c) : Account.NO_ACCOUNT;
255     }
256 
257     /**
258      * @return ID of the account for a row
259      */
getAccountId(int position)260     public long getAccountId(int position) {
261         final Cursor c = getCursor();
262         return c.moveToPosition(position)
263                 ? c.getLong(c.getColumnIndex(ACCOUNT_ID))
264                 : Account.NO_ACCOUNT;
265     }
266 
267     /** Returns the account name extracted from the given cursor. */
getDisplayName(Cursor cursor)268     static String getDisplayName(Cursor cursor) {
269         return cursor.getString(cursor.getColumnIndex(Account.DISPLAY_NAME));
270     }
271 
272     /** Returns the email address extracted from the given cursor. */
getAccountEmailAddress(Cursor cursor)273     private static String getAccountEmailAddress(Cursor cursor) {
274         return cursor.getString(cursor.getColumnIndex(Account.EMAIL_ADDRESS));
275     }
276 
277     /**
278      * Returns the message count (unread or total, depending on row) extracted from the given
279      * cursor.
280      */
getMessageCount(Cursor cursor)281     private static int getMessageCount(Cursor cursor) {
282         return cursor.getInt(cursor.getColumnIndex(MESSAGE_COUNT));
283     }
284 
285     private static String sCombinedViewDisplayName;
getCombinedViewDisplayName(Context c)286     private static String getCombinedViewDisplayName(Context c) {
287         if (sCombinedViewDisplayName == null) {
288             sCombinedViewDisplayName = c.getResources().getString(
289                     R.string.mailbox_list_account_selector_combined_view);
290         }
291         return sCombinedViewDisplayName;
292     }
293 
294     /**
295      * Load the account list.  The resulting cursor contains
296      * - Account info
297      * - # of unread messages in inbox
298      * - The "Combined view" row if there's more than one account.
299      */
300     @VisibleForTesting
301     static class AccountsLoader extends ThrottlingCursorLoader {
302         private final Context mContext;
303         private final long mAccountId;
304         private final long mMailboxId;
305         private final boolean mUseTwoPane; // Injectable for test
306         private final FolderProperties mFolderProperties;
307 
308         @VisibleForTesting
AccountsLoader(Context context, long accountId, long mailboxId, boolean useTwoPane)309         AccountsLoader(Context context, long accountId, long mailboxId, boolean useTwoPane) {
310             // Super class loads a regular account cursor, but we replace it in loadInBackground().
311             super(context, Account.CONTENT_URI, ACCOUNT_PROJECTION, null, null,
312                     ORDER_BY);
313             mContext = context;
314             mAccountId = accountId;
315             mMailboxId = mailboxId;
316             mFolderProperties = FolderProperties.getInstance(mContext);
317             mUseTwoPane = useTwoPane;
318         }
319 
320         @Override
loadInBackground()321         public Cursor loadInBackground() {
322             final Cursor accountsCursor = super.loadInBackground();
323             // Use ClosingMatrixCursor so that accountsCursor gets closed too when it's closed.
324             final CursorWithExtras resultCursor
325                     = new CursorWithExtras(ADAPTER_PROJECTION, accountsCursor);
326             final int accountPosition = addAccountsToCursor(resultCursor, accountsCursor);
327             addMailboxesToCursor(resultCursor, accountPosition);
328 
329             resultCursor.setAccountMailboxInfo(getContext(), mAccountId, mMailboxId);
330             return resultCursor;
331         }
332 
333         /** Adds the account list [with extra meta data] to the given matrix cursor */
addAccountsToCursor(CursorWithExtras matrixCursor, Cursor accountCursor)334         private int addAccountsToCursor(CursorWithExtras matrixCursor, Cursor accountCursor) {
335             int accountPosition = UNKNOWN_POSITION;
336             accountCursor.moveToPosition(-1);
337 
338             matrixCursor.mAccountCount = accountCursor.getCount();
339             int totalUnread = 0;
340             while (accountCursor.moveToNext()) {
341                 // Add account, with its unread count.
342                 final long accountId = accountCursor.getLong(0);
343                 final int unread = Mailbox.getUnreadCountByAccountAndMailboxType(
344                         mContext, accountId, Mailbox.TYPE_INBOX);
345                 final String name = getDisplayName(accountCursor);
346                 final String emailAddress = getAccountEmailAddress(accountCursor);
347                 addRow(matrixCursor, ROW_TYPE_ACCOUNT, accountId, name, emailAddress, unread,
348                     UNKNOWN_POSITION, accountId);
349                 totalUnread += unread;
350                 if (accountId == mAccountId) {
351                     accountPosition = accountCursor.getPosition();
352                 }
353             }
354             // Add "combined view" if more than one account exists
355             final int countAccounts = accountCursor.getCount();
356             if (countAccounts > 1) {
357                 final String accountCount = mContext.getResources().getQuantityString(
358                         R.plurals.number_of_accounts, countAccounts, countAccounts);
359                 addRow(matrixCursor, ROW_TYPE_ACCOUNT, Account.ACCOUNT_ID_COMBINED_VIEW,
360                         getCombinedViewDisplayName(mContext),
361                         accountCount, totalUnread, UNKNOWN_POSITION,
362                         Account.ACCOUNT_ID_COMBINED_VIEW);
363 
364                 // Increment the account count for the combined account.
365                 matrixCursor.mAccountCount++;
366             }
367             return accountPosition;
368         }
369 
370         /**
371          * Adds the recent mailbox list / "show all folders" to the given cursor.
372          *
373          * @param matrixCursor the cursor to add the list to
374          * @param accountPosition the cursor position of the currently selected account
375          */
addMailboxesToCursor(CursorWithExtras matrixCursor, int accountPosition)376         private void addMailboxesToCursor(CursorWithExtras matrixCursor, int accountPosition) {
377             if (mAccountId == Account.NO_ACCOUNT) {
378                 return; // Account not selected
379             }
380             if (mAccountId == Account.ACCOUNT_ID_COMBINED_VIEW) {
381                 if (!mUseTwoPane) {
382                     // TODO We may want a header for this to separate it from the account list
383                     addShowAllFoldersRow(matrixCursor, accountPosition);
384                 }
385                 return;
386             }
387             String emailAddress = null;
388             if (accountPosition != UNKNOWN_POSITION) {
389                 matrixCursor.moveToPosition(accountPosition);
390                 emailAddress =
391                         matrixCursor.getString(matrixCursor.getColumnIndex(Account.EMAIL_ADDRESS));
392             }
393             RecentMailboxManager mailboxManager = RecentMailboxManager.getInstance(mContext);
394             ArrayList<Long> recentMailboxes = null;
395             if (!mUseTwoPane) {
396                 // Do not display recent mailboxes in the account spinner for the two pane view
397                 recentMailboxes = mailboxManager.getMostRecent(mAccountId, mUseTwoPane);
398             }
399             final int recentCount = (recentMailboxes == null) ? 0 : recentMailboxes.size();
400             matrixCursor.mRecentCount = recentCount;
401 
402             if (!mUseTwoPane) {
403                 // "Recent mailboxes" header
404                 addHeaderRow(matrixCursor, mContext.getString(
405                         R.string.mailbox_list_account_selector_mailbox_header_fmt, emailAddress));
406             }
407 
408             if (recentCount > 0) {
409                 addMailboxRows(matrixCursor, accountPosition, recentMailboxes);
410             }
411 
412             if (!mUseTwoPane) {
413                 addShowAllFoldersRow(matrixCursor, accountPosition);
414             }
415         }
416 
addShowAllFoldersRow(CursorWithExtras matrixCursor, int accountPosition)417         private void addShowAllFoldersRow(CursorWithExtras matrixCursor, int accountPosition) {
418             matrixCursor.mHasShowAllFolders = true;
419             String name = mContext.getString(
420                     R.string.mailbox_list_account_selector_show_all_folders);
421             addRow(matrixCursor, ROW_TYPE_MAILBOX, Mailbox.NO_MAILBOX, name, null, 0,
422                     accountPosition, mAccountId);
423         }
424 
425 
426         private static final String[] RECENT_MAILBOX_INFO_PROJECTION = new String[] {
427             MailboxColumns.ID, MailboxColumns.DISPLAY_NAME, MailboxColumns.TYPE,
428             MailboxColumns.UNREAD_COUNT, MailboxColumns.MESSAGE_COUNT
429         };
430 
addMailboxRows(MatrixCursor matrixCursor, int accountPosition, Collection<Long> mailboxIds)431         private void addMailboxRows(MatrixCursor matrixCursor, int accountPosition,
432                 Collection<Long> mailboxIds) {
433             Cursor c = mContext.getContentResolver().query(
434                     Mailbox.CONTENT_URI, RECENT_MAILBOX_INFO_PROJECTION,
435                     Utility.buildInSelection(MailboxColumns.ID, mailboxIds), null,
436                     RecentMailboxManager.RECENT_MAILBOXES_SORT_ORDER);
437             try {
438                 c.moveToPosition(-1);
439                 while (c.moveToNext()) {
440                     addRow(matrixCursor, ROW_TYPE_MAILBOX,
441                             c.getLong(c.getColumnIndex(MailboxColumns.ID)),
442                             mFolderProperties.getDisplayName(c), null,
443                             mFolderProperties.getMessageCount(c), accountPosition, mAccountId);
444                 }
445             } finally {
446                 c.close();
447             }
448         }
449 
addHeaderRow(MatrixCursor cursor, String name)450         private void addHeaderRow(MatrixCursor cursor, String name) {
451             addRow(cursor, ROW_TYPE_HEADER, 0L, name, null, 0, UNKNOWN_POSITION,
452                     Account.NO_ACCOUNT);
453         }
454 
455         /** Adds a row to the given cursor */
addRow(MatrixCursor cursor, int rowType, long id, String name, String emailAddress, int messageCount, int listPosition, long accountId)456         private void addRow(MatrixCursor cursor, int rowType, long id, String name,
457                 String emailAddress, int messageCount, int listPosition, long accountId) {
458             cursor.newRow()
459                 .add(rowType)
460                 .add(id)
461                 .add(name)
462                 .add(emailAddress)
463                 .add(messageCount)
464                 .add(listPosition)
465                 .add(accountId);
466         }
467     }
468 
469     /** Cursor with some extra meta data. */
470     static class CursorWithExtras extends ClosingMatrixCursor {
471 
472         /** Number of account elements, including the combined account row. */
473         private int mAccountCount;
474         /** Number of recent mailbox elements */
475         private int mRecentCount;
476         private boolean mHasShowAllFolders;
477 
478         private boolean mAccountExists;
479 
480         /**
481          * Account ID that's loaded.
482          */
483         private long mAccountId;
484         private String mAccountDisplayName;
485 
486         /**
487          * Mailbox ID that's loaded.
488          */
489         private long mMailboxId;
490         private String mMailboxDisplayName;
491         private int mMailboxMessageCount;
492 
493         @VisibleForTesting
CursorWithExtras(String[] columnNames, Cursor innerCursor)494         CursorWithExtras(String[] columnNames, Cursor innerCursor) {
495             super(columnNames, innerCursor);
496         }
497 
498         private static final String[] ACCOUNT_INFO_PROJECTION = new String[] {
499             AccountColumns.DISPLAY_NAME,
500         };
501         private static final String[] MAILBOX_INFO_PROJECTION = new String[] {
502             MailboxColumns.ID, MailboxColumns.DISPLAY_NAME, MailboxColumns.TYPE,
503             MailboxColumns.UNREAD_COUNT, MailboxColumns.MESSAGE_COUNT
504         };
505 
506         /**
507          * Set the current account/mailbox info.
508          */
509         @VisibleForTesting
setAccountMailboxInfo(Context context, long accountId, long mailboxId)510         void setAccountMailboxInfo(Context context, long accountId, long mailboxId) {
511             mAccountId = accountId;
512             mMailboxId = mailboxId;
513 
514             // Get account info
515             if (accountId == Account.ACCOUNT_ID_COMBINED_VIEW) {
516                 // We need to treat ACCOUNT_ID_COMBINED_VIEW specially...
517                 mAccountExists = true;
518                 mAccountDisplayName = getCombinedViewDisplayName(context);
519                 if (mailboxId != Mailbox.NO_MAILBOX) {
520                     setCombinedMailboxInfo(context, mailboxId);
521                 }
522                 return;
523             }
524 
525             mAccountDisplayName = Utility.getFirstRowString(context,
526                     ContentUris.withAppendedId(Account.CONTENT_URI, accountId),
527                     ACCOUNT_INFO_PROJECTION, null, null, null, 0, null);
528             if (mAccountDisplayName == null) {
529                 // Account gone!
530                 mAccountExists = false;
531                 return;
532             }
533             mAccountExists = true;
534 
535             // If mailbox not specified, done.
536             if (mMailboxId == Mailbox.NO_MAILBOX) {
537                 return;
538             }
539             // Combined mailbox?
540             // Unfortunately this can happen even when account != ACCOUNT_ID_COMBINED_VIEW,
541             // when you open "starred" on 2-pane on non-combined view.
542             if (mMailboxId < 0) {
543                 setCombinedMailboxInfo(context, mailboxId);
544                 return;
545             }
546 
547             // Get mailbox info
548             final ContentResolver r = context.getContentResolver();
549             final Cursor mailboxCursor = r.query(
550                     ContentUris.withAppendedId(Mailbox.CONTENT_URI, mailboxId),
551                     MAILBOX_INFO_PROJECTION, null, null, null);
552             try {
553                 if (mailboxCursor.moveToFirst()) {
554                     final FolderProperties fp = FolderProperties.getInstance(context);
555                     mMailboxDisplayName = fp.getDisplayName(mailboxCursor);
556                     mMailboxMessageCount = fp.getMessageCount(mailboxCursor);
557                 }
558             } finally {
559                 mailboxCursor.close();
560             }
561         }
562 
setCombinedMailboxInfo(Context context, long mailboxId)563         private void setCombinedMailboxInfo(Context context, long mailboxId) {
564             Preconditions.checkState(mailboxId < -1, "Not combined mailbox");
565             mMailboxDisplayName = FolderProperties.getInstance(context)
566                     .getCombinedMailboxName(mMailboxId);
567 
568             mMailboxMessageCount = FolderProperties.getMessageCountForCombinedMailbox(
569                     context, mailboxId);
570         }
571 
572         /**
573          * Returns the cursor position of the item with the given ID. Or {@link #UNKNOWN_POSITION}
574          * if the given ID does not exist.
575          */
576         int getPosition(long id) {
577             moveToPosition(-1);
578             while(moveToNext()) {
579                 if (id == getId(this)) {
580                     return getPosition();
581                 }
582             }
583             return UNKNOWN_POSITION;
584         }
585 
586         public int getAccountCount() {
587             return mAccountCount;
588         }
589 
590         @VisibleForTesting
591         public int getRecentMailboxCount() {
592             return mRecentCount;
593         }
594 
595         /**
596          * @return true if the cursor has more than one selectable item so we should enable the
597          *     spinner.
598          */
599         public boolean shouldEnableSpinner() {
600             return mHasShowAllFolders || (mAccountCount + mRecentCount > 1);
601         }
602 
getAccountId()603         public long getAccountId() {
604             return mAccountId;
605         }
606 
getAccountDisplayName()607         public String getAccountDisplayName() {
608             return mAccountDisplayName;
609         }
610 
611         @VisibleForTesting
getMailboxId()612         public long getMailboxId() {
613             return mMailboxId;
614         }
615 
getMailboxDisplayName()616         public String getMailboxDisplayName() {
617             return mMailboxDisplayName;
618         }
619 
getMailboxMessageCount()620         public int getMailboxMessageCount() {
621             return mMailboxMessageCount;
622         }
623 
624         /**
625          * @return {@code true} if the specified accuont exists.
626          */
accountExists()627         public boolean accountExists() {
628             return mAccountExists;
629         }
630     }
631 }
632