From 69461503fb5faa24c37c86cd54012fb2ec2308be Mon Sep 17 00:00:00 2001 From: Todd Kennedy Date: Mon, 6 Jun 2011 14:03:40 -0700 Subject: [PATCH] Add "Accounts" header to the AB spinner Change-Id: I35e4b7295db16db803ede084cf65fa3f2115f770 --- .../account_selector_dropdown_header.xml | 20 ++ res/values/strings.xml | 3 +- .../activity/AccountSelectorAdapter.java | 204 +++++++++++------- .../email/activity/ActionBarController.java | 42 ++-- 4 files changed, 170 insertions(+), 99 deletions(-) create mode 100644 res/layout/account_selector_dropdown_header.xml diff --git a/res/layout/account_selector_dropdown_header.xml b/res/layout/account_selector_dropdown_header.xml new file mode 100644 index 000000000..2f4aad9d7 --- /dev/null +++ b/res/layout/account_selector_dropdown_header.xml @@ -0,0 +1,20 @@ + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index c0fef4cc2..55db0c6bc 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -172,7 +172,6 @@ %1$d unread (%2$s) - @@ -275,6 +274,8 @@ Show all folders + + Accounts Load more messages diff --git a/src/com/android/email/activity/AccountSelectorAdapter.java b/src/com/android/email/activity/AccountSelectorAdapter.java index 36d7e2821..2f800c7c2 100644 --- a/src/com/android/email/activity/AccountSelectorAdapter.java +++ b/src/com/android/email/activity/AccountSelectorAdapter.java @@ -16,6 +16,8 @@ package com.android.email.activity; +import com.google.common.annotations.VisibleForTesting; + import com.android.email.R; import com.android.email.data.ClosingMatrixCursor; import com.android.email.data.ThrottlingCursorLoader; @@ -28,47 +30,52 @@ import android.content.Context; import android.content.Loader; import android.database.Cursor; import android.database.MatrixCursor; -import android.database.MatrixCursor.RowBuilder; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.AdapterView; import android.widget.CursorAdapter; import android.widget.TextView; /** - * Adapter for the account selector on {@link UIControllerTwoPane}. + * Account selector spinner. * * TODO Test it! */ public class AccountSelectorAdapter extends CursorAdapter { - /** Projection used to query from Account */ + /** meta data column for an account's unread count */ + 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"; + 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; + /** Projection for account database query */ private static final String[] ACCOUNT_PROJECTION = new String[] { - EmailContent.RECORD_ID, - EmailContent.Account.DISPLAY_NAME, - EmailContent.Account.EMAIL_ADDRESS, + Account.ID, + Account.DISPLAY_NAME, + Account.EMAIL_ADDRESS, }; - /** - * Projection for the resulting MatrixCursor -- must be {@link #ACCOUNT_PROJECTION} - * with "UNREAD_COUNT". + * Projection used for the selector display; we add meta data that doesn't exist in the + * account database, so, this should be a super-set of {@link #ACCOUNT_PROJECTION}. */ - private static final String[] RESULT_PROJECTION = new String[] { - EmailContent.RECORD_ID, - EmailContent.Account.DISPLAY_NAME, - EmailContent.Account.EMAIL_ADDRESS, - "UNREAD_COUNT" + private static final String[] ADAPTER_PROJECTION = new String[] { + ROW_TYPE, + Account.ID, + Account.DISPLAY_NAME, + Account.EMAIL_ADDRESS, + UNREAD_COUNT, }; - private static final int ID_COLUMN = 0; - private static final int DISPLAY_NAME_COLUMN = 1; - private static final int EMAIL_ADDRESS_COLUMN = 2; - private static final int UNREAD_COUNT_COLUMN = 3; - /** Sort order. Show the default account first. */ private static final String ORDER_BY = EmailContent.Account.IS_DEFAULT + " desc, " + EmailContent.Account.RECORD_ID; private final LayoutInflater mInflater; + @SuppressWarnings("hiding") private final Context mContext; public static Loader createLoader(Context context) { @@ -83,27 +90,37 @@ public class AccountSelectorAdapter extends CursorAdapter { @Override public View getDropDownView(int position, View convertView, ViewGroup parent) { - final View view = mInflater.inflate(R.layout.account_selector_dropdown, parent, false); + Cursor c = getCursor(); + c.moveToPosition(position); - final TextView displayNameView = (TextView) view.findViewById(R.id.display_name); - final TextView emailAddressView = (TextView) view.findViewById(R.id.email_address); - final TextView unreadCountView = (TextView) view.findViewById(R.id.unread_count); - - final String displayName = getAccountDisplayName(position); - final String emailAddress = getAccountEmailAddress(position); - - displayNameView.setText(displayName); - - // Show the email address only when it's different from the display name. - if (emailAddress.equals(displayName)) { - emailAddressView.setVisibility(View.GONE); + View view; + if (c.getInt(c.getColumnIndex(ROW_TYPE)) == ROW_TYPE_HEADER) { + view = mInflater.inflate(R.layout.account_selector_dropdown_header, parent, false); + final TextView displayNameView = (TextView) view.findViewById(R.id.display_name); + final String displayName = getAccountDisplayName(c); + displayNameView.setText(displayName); } else { - emailAddressView.setVisibility(View.VISIBLE); - emailAddressView.setText(emailAddress); - } + view = mInflater.inflate(R.layout.account_selector_dropdown, parent, false); + final TextView displayNameView = (TextView) view.findViewById(R.id.display_name); + final TextView emailAddressView = (TextView) view.findViewById(R.id.email_address); + final TextView unreadCountView = (TextView) view.findViewById(R.id.unread_count); - unreadCountView.setText(UiUtilities.getMessageCountForUi(mContext, - getAccountUnreadCount(position), false)); + final String displayName = getAccountDisplayName(position); + final String emailAddress = getAccountEmailAddress(position); + + displayNameView.setText(displayName); + + // Show the email address only when it's different from the display name. + if (displayName.equals(emailAddress)) { + emailAddressView.setVisibility(View.GONE); + } else { + emailAddressView.setVisibility(View.VISIBLE); + emailAddressView.setText(emailAddress); + } + + unreadCountView.setText(UiUtilities.getMessageCountForUi(mContext, + getAccountUnreadCount(position), false)); + } return view; } @@ -118,9 +135,29 @@ public class AccountSelectorAdapter extends CursorAdapter { return mInflater.inflate(R.layout.account_selector, parent, false); } - /** @return Account id extracted from a Cursor. */ - public static long getAccountId(Cursor c) { - return c.getLong(ID_COLUMN); + @Override + public int getViewTypeCount() { + return 2; + } + + @Override + public int getItemViewType(int position) { + Cursor c = getCursor(); + c.moveToPosition(position); + return c.getLong(c.getColumnIndex(ROW_TYPE)) == ROW_TYPE_HEADER + ? AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER + : ITEM_VIEW_TYPE_ACCOUNT; + } + + @Override + public boolean isEnabled(int position) { + return (getItemViewType(position) != AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER); + } + + public boolean isAccountItem(int position) { + Cursor c = getCursor(); + c.moveToPosition(position); + return (c.getLong(c.getColumnIndex(ROW_TYPE)) == ROW_TYPE_ACCOUNT); } private String getAccountDisplayName(int position) { @@ -138,19 +175,24 @@ public class AccountSelectorAdapter extends CursorAdapter { return c.moveToPosition(position) ? getAccountUnreadCount(c) : 0; } - /** @return Account name extracted from a Cursor. */ - public static String getAccountDisplayName(Cursor cursor) { - return cursor.getString(DISPLAY_NAME_COLUMN); + /** Returns the account ID extracted from the given cursor. */ + static long getAccountId(Cursor c) { + return c.getLong(c.getColumnIndex(Account.ID)); } - /** @return Email address extracted from a Cursor. */ - public static String getAccountEmailAddress(Cursor cursor) { - return cursor.getString(EMAIL_ADDRESS_COLUMN); + /** Returns the account name extracted from the given cursor. */ + static String getAccountDisplayName(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Account.DISPLAY_NAME)); } - /** @return Unread count extracted from a Cursor. */ - public static int getAccountUnreadCount(Cursor cursor) { - return cursor.getInt(UNREAD_COUNT_COLUMN); + /** Returns the email address extracted from the given cursor. */ + private static String getAccountEmailAddress(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Account.EMAIL_ADDRESS)); + } + + /** Returns the unread count extracted from the given cursor. */ + private static int getAccountUnreadCount(Cursor cursor) { + return cursor.getInt(cursor.getColumnIndex(UNREAD_COUNT)); } /** @@ -159,7 +201,8 @@ public class AccountSelectorAdapter extends CursorAdapter { * - # of unread messages in inbox * - The "Combined view" row if there's more than one account. */ - /* package */ static class AccountsLoader extends ThrottlingCursorLoader { + @VisibleForTesting + static class AccountsLoader extends ThrottlingCursorLoader { private final Context mContext; public AccountsLoader(Context context) { @@ -171,44 +214,51 @@ public class AccountSelectorAdapter extends CursorAdapter { @Override public Cursor loadInBackground() { - // Fetch account list final Cursor accountsCursor = super.loadInBackground(); - - // Cursor that's actually returned. // Use ClosingMatrixCursor so that accountsCursor gets closed too when it's closed. - final MatrixCursor resultCursor = new ClosingMatrixCursor(RESULT_PROJECTION, - accountsCursor); - accountsCursor.moveToPosition(-1); + final MatrixCursor resultCursor + = new ClosingMatrixCursor(ADAPTER_PROJECTION, accountsCursor); + addAccountsToCursor(resultCursor, accountsCursor); + // TODO Add mailbox recent list to the end of the return cursor + return Utility.CloseTraceCursorWrapper.get(resultCursor); + } - // Build the cursor... + /** Adds the account list [with extra meta data] to the given matrix cursor */ + private void addAccountsToCursor(MatrixCursor matrixCursor, Cursor accountCursor) { + 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); int totalUnread = 0; - while (accountsCursor.moveToNext()) { + while (accountCursor.moveToNext()) { // Add account, with its unread count. - final long accountId = accountsCursor.getLong(0); + final long accountId = accountCursor.getLong(0); final int unread = Mailbox.getUnreadCountByAccountAndMailboxType( mContext, accountId, Mailbox.TYPE_INBOX); - - RowBuilder rb = resultCursor.newRow(); - rb.add(accountId); - rb.add(getAccountDisplayName(accountsCursor)); - rb.add(getAccountEmailAddress(accountsCursor)); - rb.add(unread); + matrixCursor.newRow() + .add(ROW_TYPE_ACCOUNT) + .add(accountId) + .add(getAccountDisplayName(accountCursor)) + .add(getAccountEmailAddress(accountCursor)) + .add(unread); totalUnread += unread; } - // Add "combined view" - final int countAccounts = resultCursor.getCount(); + // Add "combined view" if more than one account exists + final int countAccounts = matrixCursor.getCount(); if (countAccounts > 1) { - RowBuilder rb = resultCursor.newRow(); - - // Add ID, display name, # of accounts, total unread count. - rb.add(Account.ACCOUNT_ID_COMBINED_VIEW); - rb.add(mContext.getResources().getString( - R.string.mailbox_list_account_selector_combined_view)); - rb.add(mContext.getResources().getQuantityString(R.plurals.number_of_accounts, - countAccounts, countAccounts)); - rb.add(totalUnread); + 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); } - return Utility.CloseTraceCursorWrapper.get(resultCursor); } } } diff --git a/src/com/android/email/activity/ActionBarController.java b/src/com/android/email/activity/ActionBarController.java index 2db3b7caf..c20e19275 100644 --- a/src/com/android/email/activity/ActionBarController.java +++ b/src/com/android/email/activity/ActionBarController.java @@ -17,7 +17,6 @@ package com.android.email.activity; import com.android.email.R; -import com.android.emailcommon.Logging; import com.android.emailcommon.provider.EmailContent.Account; import android.app.ActionBar; @@ -50,7 +49,9 @@ public class ActionBarController { new ActionBarNavigationCallback(); private final AccountSelectorAdapter mAccountsSelectorAdapter; - private Cursor mAccountsSelectorCursor; + private Cursor mAccountCursor; + /** The current account ID; used to determine if the account has changed. */ + private long mLastAccountIdForDirtyCheck = -1; public final Callback mCallback; @@ -123,12 +124,7 @@ public class ActionBarController { refresh(); } - /** Used only in {@link #refresh()} to determine if the account has changed. */ - private long mLastAccountIdForDirtyCheck = -1; - - /** - * Refresh the content. - */ + /** Refreshes the action bar display. */ public void refresh() { mActionBar.setDisplayOptions(mCallback.shouldShowUp() ? ActionBar.DISPLAY_HOME_AS_UP : 0, ActionBar.DISPLAY_HOME_AS_UP); @@ -146,6 +142,7 @@ 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(); } } @@ -163,13 +160,13 @@ public class ActionBarController { @Override public void onLoadFinished(Loader loader, Cursor data) { - mAccountsSelectorCursor = data; + mAccountCursor = data; updateAccountList(); } @Override public void onLoaderReset(Loader loader) { - mAccountsSelectorCursor = null; + mAccountCursor = null; updateAccountList(); } }); @@ -180,10 +177,10 @@ public class ActionBarController { * on the action bar. */ private void updateAccountList() { - mAccountsSelectorAdapter.swapCursor(mAccountsSelectorCursor); + mAccountsSelectorAdapter.swapCursor(mAccountCursor); final ActionBar ab = mActionBar; - if (mAccountsSelectorCursor == null) { + if (mAccountCursor == null) { // Cursor not ready or closed. mAccountsSelectorAdapter.swapCursor(null); ab.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE); @@ -191,7 +188,7 @@ public class ActionBarController { return; } - final int count = mAccountsSelectorCursor.getCount(); + final int count = mAccountCursor.getCount(); if (count == 0) { mCallback.onNoAccountsFound(); return; @@ -199,12 +196,12 @@ public class ActionBarController { // If only one acount, don't show the dropdown. if (count == 1) { - mAccountsSelectorCursor.moveToFirst(); + mAccountCursor.moveToFirst(); // Show the account name as the title. ab.setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE, ActionBar.DISPLAY_SHOW_TITLE); ab.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); - ab.setTitle(AccountSelectorAdapter.getAccountDisplayName(mAccountsSelectorCursor)); + ab.setTitle(AccountSelectorAdapter.getAccountDisplayName(mAccountCursor)); return; } @@ -212,10 +209,10 @@ public class ActionBarController { int defaultSelection = 0; if (mCallback.isAccountSelected()) { final long accountId = mCallback.getUIAccountId(); - mAccountsSelectorCursor.moveToPosition(-1); + mAccountCursor.moveToPosition(-1); int i = 0; - while (mAccountsSelectorCursor.moveToNext()) { - if (accountId == AccountSelectorAdapter.getAccountId(mAccountsSelectorCursor)) { + while (mAccountCursor.moveToNext()) { + if (accountId == AccountSelectorAdapter.getAccountId(mAccountCursor)) { defaultSelection = i; break; } @@ -234,9 +231,12 @@ public class ActionBarController { private class ActionBarNavigationCallback implements ActionBar.OnNavigationListener { @Override - public boolean onNavigationItemSelected(int itemPosition, long accountId) { - if (accountId != mCallback.getUIAccountId()) { - mCallback.onAccountSelected(accountId); + public boolean onNavigationItemSelected(int itemPosition, long itemId) { + if (mAccountsSelectorAdapter.isAccountItem(itemPosition) + && itemId != mCallback.getUIAccountId()) { + mCallback.onAccountSelected(itemId); + } else { + // TODO handle mailbox item } return true; }