Add recent mailboxes to the account spinner

The ability to change mailboxes using the spinner is currently only implemented
for the two-pane UI. one-pane implementation will come in a future CL.

Change-Id: If72e9d9d607508553c918f5523e748e8a481ff84
This commit is contained in:
Todd Kennedy 2011-06-06 17:33:20 -07:00
parent 63a206ea56
commit e392418840
7 changed files with 156 additions and 39 deletions

View File

@ -276,6 +276,9 @@
<string name="mailbox_list_account_selector_show_all_folders">Show all folders</string>
<!-- Account list header in the account/mailbox selector. [CHAR LIMIT=30] -->
<string name="mailbox_list_account_selector_account_header">Accounts</string>
<!-- Recent mailbox header in the account/mailbox selector. [CHAR LIMIT=30] -->
<string name="mailbox_list_account_selector_mailbox_header_fmt">
Recent mailboxes(<xliff:g id="email_address">%s</xliff:g>)</string>
<!-- Appears at the bottom of list of messages; user selects to load more messages from that folder. -->
<string name="message_list_load_more_messages_action">Load more messages</string>

View File

@ -23,9 +23,13 @@ import com.android.email.data.ClosingMatrixCursor;
import com.android.email.data.ThrottlingCursorLoader;
import com.android.emailcommon.provider.EmailContent;
import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.provider.EmailContent.MailboxColumns;
import com.android.emailcommon.provider.Mailbox;
import com.android.emailcommon.utility.Utility;
import java.util.ArrayList;
import android.content.ContentUris;
import android.content.Context;
import android.content.Loader;
import android.database.Cursor;
@ -47,11 +51,14 @@ public class AccountSelectorAdapter extends CursorAdapter {
private static final String UNREAD_COUNT = "unreadCount";
/** meta data column for the row type; used for display purposes */
private static final String ROW_TYPE = "rowType";
/** meta data position of the currently selected account in the drop-down list */
private static final String ACCOUNT_POSITION = "accountPosition";
private static final int ROW_TYPE_HEADER = AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
@SuppressWarnings("unused")
private static final int ROW_TYPE_MAILBOX = 0;
private static final int ROW_TYPE_ACCOUNT = 1;
private static final int ITEM_VIEW_TYPE_ACCOUNT = 0;
private static final int UNKNOWN_POSITION = -1;
/** Projection for account database query */
private static final String[] ACCOUNT_PROJECTION = new String[] {
Account.ID,
@ -68,6 +75,7 @@ public class AccountSelectorAdapter extends CursorAdapter {
Account.DISPLAY_NAME,
Account.EMAIL_ADDRESS,
UNREAD_COUNT,
ACCOUNT_POSITION,
};
/** Sort order. Show the default account first. */
@ -78,8 +86,13 @@ public class AccountSelectorAdapter extends CursorAdapter {
@SuppressWarnings("hiding")
private final Context mContext;
public static Loader<Cursor> createLoader(Context context) {
return new AccountsLoader(context);
/**
* Returns a loader that can populate the account spinner.
* @param context a context
* @param accountId the ID of the currently viewed account
*/
public static Loader<Cursor> createLoader(Context context, long accountId) {
return new AccountsLoader(context, accountId);
}
public AccountSelectorAdapter(Context context) {
@ -88,6 +101,22 @@ public class AccountSelectorAdapter extends CursorAdapter {
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
/**
* Invoked when the action bar needs the view of the text in the bar itself. The default
* is to show just the display name of the cursor at the given position.
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (!isAccountItem(position)) {
// asked to show a recent mailbox; instead, show the account associated w/ the mailbox
int newPosition = getAccountPosition(position);
if (newPosition != UNKNOWN_POSITION) {
position = newPosition;
}
}
return super.getView(position, convertView, parent);
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
Cursor c = getCursor();
@ -175,6 +204,11 @@ public class AccountSelectorAdapter extends CursorAdapter {
return c.moveToPosition(position) ? getAccountUnreadCount(c) : 0;
}
private int getAccountPosition(int position) {
final Cursor c = getCursor();
return c.moveToPosition(position) ? getAccountPosition(c) : UNKNOWN_POSITION;
}
/** Returns the account ID extracted from the given cursor. */
static long getAccountId(Cursor c) {
return c.getLong(c.getColumnIndex(Account.ID));
@ -195,6 +229,11 @@ public class AccountSelectorAdapter extends CursorAdapter {
return cursor.getInt(cursor.getColumnIndex(UNREAD_COUNT));
}
/** Returns the account position extracted from the given cursor. */
private static int getAccountPosition(Cursor cursor) {
return cursor.getInt(cursor.getColumnIndex(ACCOUNT_POSITION));
}
/**
* Load the account list. The resulting cursor contains
* - Account info
@ -204,12 +243,13 @@ public class AccountSelectorAdapter extends CursorAdapter {
@VisibleForTesting
static class AccountsLoader extends ThrottlingCursorLoader {
private final Context mContext;
public AccountsLoader(Context context) {
private final long mAccountId;
public AccountsLoader(Context context, long accountId) {
// Super class loads a regular account cursor, but we replace it in loadInBackground().
super(context, EmailContent.Account.CONTENT_URI, ACCOUNT_PROJECTION, null, null,
ORDER_BY);
mContext = context;
mAccountId = accountId;
}
@Override
@ -218,47 +258,100 @@ public class AccountSelectorAdapter extends CursorAdapter {
// Use ClosingMatrixCursor so that accountsCursor gets closed too when it's closed.
final MatrixCursor resultCursor
= new ClosingMatrixCursor(ADAPTER_PROJECTION, accountsCursor);
addAccountsToCursor(resultCursor, accountsCursor);
// TODO Add mailbox recent list to the end of the return cursor
final int accountPosition = addAccountsToCursor(resultCursor, accountsCursor);
addRecentsToCursor(resultCursor, accountPosition);
return Utility.CloseTraceCursorWrapper.get(resultCursor);
}
/** Adds the account list [with extra meta data] to the given matrix cursor */
private void addAccountsToCursor(MatrixCursor matrixCursor, Cursor accountCursor) {
private int addAccountsToCursor(MatrixCursor matrixCursor, Cursor accountCursor) {
int accountPosition = UNKNOWN_POSITION;
accountCursor.moveToPosition(-1);
// Add a header for the accounts
matrixCursor.newRow()
.add(ROW_TYPE_HEADER)
.add(0L)
.add(mContext.getString(R.string.mailbox_list_account_selector_account_header))
.add(null)
.add(0L);
String header =
mContext.getString(R.string.mailbox_list_account_selector_account_header);
addRow(matrixCursor, ROW_TYPE_HEADER, 0L, header, null, 0, UNKNOWN_POSITION);
int totalUnread = 0;
int currentPosition = 1;
while (accountCursor.moveToNext()) {
// Add account, with its unread count.
final long accountId = accountCursor.getLong(0);
final int unread = Mailbox.getUnreadCountByAccountAndMailboxType(
mContext, accountId, Mailbox.TYPE_INBOX);
matrixCursor.newRow()
.add(ROW_TYPE_ACCOUNT)
.add(accountId)
.add(getAccountDisplayName(accountCursor))
.add(getAccountEmailAddress(accountCursor))
.add(unread);
final String name = getAccountDisplayName(accountCursor);
final String emailAddress = getAccountEmailAddress(accountCursor);
addRow(matrixCursor, ROW_TYPE_ACCOUNT, accountId, name, emailAddress, unread,
UNKNOWN_POSITION);
totalUnread += unread;
if (accountId == mAccountId) {
accountPosition = currentPosition;
}
currentPosition++;
}
// Add "combined view" if more than one account exists
final int countAccounts = matrixCursor.getCount();
if (countAccounts > 1) {
matrixCursor.newRow()
.add(ROW_TYPE_ACCOUNT)
.add(Account.ACCOUNT_ID_COMBINED_VIEW)
.add(mContext.getResources().getString(
R.string.mailbox_list_account_selector_combined_view))
.add(mContext.getResources().getQuantityString(R.plurals.number_of_accounts,
countAccounts, countAccounts))
.add(totalUnread);
final String name = mContext.getResources().getString(
R.string.mailbox_list_account_selector_combined_view);
final String accountCount = mContext.getResources().getQuantityString(
R.plurals.number_of_accounts, countAccounts, countAccounts);
addRow(matrixCursor, ROW_TYPE_ACCOUNT, Account.ACCOUNT_ID_COMBINED_VIEW,
name, accountCount, totalUnread,UNKNOWN_POSITION);
}
return accountPosition;
}
/**
* Adds the recent mailbox list to the given cursor.
* @param matrixCursor the cursor to add the list to
* @param accountPosition the cursor position of the currently selected account
*/
private void addRecentsToCursor(MatrixCursor matrixCursor, int accountPosition) {
if (mAccountId <= 0L || mAccountId == Account.ACCOUNT_ID_COMBINED_VIEW) {
// Currently selected account isn't usable for our purposes
return;
}
String emailAddress = null;
if (accountPosition != UNKNOWN_POSITION) {
matrixCursor.moveToPosition(accountPosition);
emailAddress =
matrixCursor.getString(matrixCursor.getColumnIndex(Account.EMAIL_ADDRESS));
}
boolean useTwoPane = mContext.getResources().getBoolean(R.bool.use_two_pane);
// Filter system mailboxes if we're using a two-pane view
RecentMailboxManager mailboxManager = RecentMailboxManager.getInstance(mContext);
ArrayList<Long> recentMailboxes = mailboxManager.getMostRecent(mAccountId, useTwoPane);
if (!useTwoPane && recentMailboxes.size() == 0) {
// TODO Add default mailboxes to recentMailboxes for the one pane view
}
if (recentMailboxes.size() > 0) {
String mailboxHeader = mContext.getString(
R.string.mailbox_list_account_selector_mailbox_header_fmt, emailAddress);
addRow(matrixCursor, ROW_TYPE_HEADER, 0L, mailboxHeader, null, 0, UNKNOWN_POSITION);
for (long mailboxId : recentMailboxes) {
final int unread = Utility.getFirstRowInt(mContext,
ContentUris.withAppendedId(Mailbox.CONTENT_URI, mailboxId),
new String[] { MailboxColumns.UNREAD_COUNT }, null, null, null, 0);
final Mailbox mailbox = Mailbox.restoreMailboxWithId(mContext, mailboxId);
addRow(matrixCursor, ROW_TYPE_MAILBOX, mailboxId, mailbox.mDisplayName, null,
unread, accountPosition);
}
}
if (!useTwoPane) {
// TODO Add row to view all mailboxes
}
}
/** Adds a row to the given cursor */
private void addRow(MatrixCursor cursor, int rowType, long id, String name,
String emailAddress, int unreadCount, int listPosition) {
cursor.newRow()
.add(rowType)
.add(id)
.add(name)
.add(emailAddress)
.add(unreadCount)
.add(listPosition);
}
}
}

View File

@ -18,6 +18,7 @@ package com.android.email.activity;
import com.android.email.R;
import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.provider.Mailbox;
import android.app.ActionBar;
import android.app.LoaderManager;
@ -51,7 +52,7 @@ public class ActionBarController {
private final AccountSelectorAdapter mAccountsSelectorAdapter;
private Cursor mAccountCursor;
/** The current account ID; used to determine if the account has changed. */
private long mLastAccountIdForDirtyCheck = -1;
private long mLastAccountIdForDirtyCheck = Account.NO_ACCOUNT;
public final Callback mCallback;
@ -81,12 +82,17 @@ public class ActionBarController {
/**
* Called when an account is selected on the account spinner.
*
* @param accountId ID of the selected account, or
* {@link Account#ACCOUNT_ID_COMBINED_VIEW}.
* @param accountId ID of the selected account, or {@link Account#ACCOUNT_ID_COMBINED_VIEW}.
*/
public void onAccountSelected(long accountId);
/**
* Invoked when a recent mailbox is selected on the account spinner.
* @param mailboxId The ID of the selected mailbox, or {@link Mailbox#NO_MAILBOX} if the
* special option "show all mailboxes" was selected.
*/
public void onMailboxSelected(long mailboxId);
/** Called when no accounts are found in the database. */
public void onNoAccountsFound();
}
@ -142,8 +148,13 @@ public class ActionBarController {
// Update the account list only when the account has changed.
if (mLastAccountIdForDirtyCheck != mCallback.getUIAccountId()) {
mLastAccountIdForDirtyCheck = mCallback.getUIAccountId();
// TODO Need to do this all the time as the recent list is shown here
updateAccountList();
// If the selected account changes, reload the cursor to update the recent mailboxes
if (mLastAccountIdForDirtyCheck != Account.NO_ACCOUNT) {
mLoaderManager.destroyLoader(LOADER_ID_ACCOUNT_LIST);
loadAccounts();
} else {
updateAccountList();
}
}
}
@ -155,7 +166,7 @@ public class ActionBarController {
new LoaderCallbacks<Cursor>() {
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return AccountSelectorAdapter.createLoader(mContext);
return AccountSelectorAdapter.createLoader(mContext, mCallback.getUIAccountId());
}
@Override
@ -235,8 +246,8 @@ public class ActionBarController {
if (mAccountsSelectorAdapter.isAccountItem(itemPosition)
&& itemId != mCallback.getUIAccountId()) {
mCallback.onAccountSelected(itemId);
} else {
// TODO handle mailbox item
} else if (!mAccountsSelectorAdapter.isAccountItem(itemPosition)) {
mCallback.onMailboxSelected(itemId);
}
return true;
}

View File

@ -1026,6 +1026,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
reloadUiFromMessage(message, mOkToFetch);
queryContactStatus();
onMessageShown(mMessageId, mMailboxType);
RecentMailboxManager.getInstance(mContext).touch(message.mMailboxKey);
}
}

View File

@ -237,6 +237,11 @@ class UIControllerOnePane extends UIControllerBase {
return UIControllerOnePane.this.getUIAccountId();
}
@Override
public void onMailboxSelected(long mailboxId) {
// TODO Implement this
}
@Override
public boolean isAccountSelected() {
return UIControllerOnePane.this.isAccountSelected();

View File

@ -31,7 +31,6 @@ import com.android.emailcommon.utility.EmailAsyncTask;
import com.google.common.annotations.VisibleForTesting;
import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Context;
import android.os.Bundle;
@ -954,6 +953,11 @@ class UIControllerTwoPane extends UIControllerBase implements
switchAccount(accountId);
}
@Override
public void onMailboxSelected(long mailboxId) {
UIControllerTwoPane.this.openMailbox(getUIAccountId(), mailboxId);
}
@Override
public void onNoAccountsFound() {
Welcome.actionStart(mActivity);

View File

@ -46,7 +46,7 @@ public class AccountSelectorAdapterAccountsLoaderTest extends LoaderTestCase {
final Account a1 = ProviderTestUtils.setupAccount("a1", true, mProviderContext);
{
// Only 1 account -- no combined view row.
Loader<Cursor> l = new AccountSelectorAdapter.AccountsLoader(mProviderContext);
Loader<Cursor> l = new AccountSelectorAdapter.AccountsLoader(mProviderContext, 0L);
Cursor result = getLoaderResultSynchronously(l);
assertEquals(1, result.getCount());
}
@ -54,7 +54,7 @@ public class AccountSelectorAdapterAccountsLoaderTest extends LoaderTestCase {
final Account a2 = ProviderTestUtils.setupAccount("a2", true, mProviderContext);
{
// 2 accounts -- with combined view row, so returns 3 rows.
Loader<Cursor> l = new AccountSelectorAdapter.AccountsLoader(mProviderContext);
Loader<Cursor> l = new AccountSelectorAdapter.AccountsLoader(mProviderContext, 0L);
Cursor result = getLoaderResultSynchronously(l);
assertEquals(3, result.getCount());
}