diff --git a/src/com/android/email/activity/ActionBarController.java b/src/com/android/email/activity/ActionBarController.java new file mode 100644 index 000000000..2db3b7caf --- /dev/null +++ b/src/com/android/email/activity/ActionBarController.java @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +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; +import android.app.LoaderManager; +import android.app.LoaderManager.LoaderCallbacks; +import android.content.Context; +import android.content.Loader; +import android.database.Cursor; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.TextView; + +/** + * Manages the account name and the custom view part on the action bar. + */ +public class ActionBarController { + private static final int LOADER_ID_ACCOUNT_LIST + = EmailActivity.ACTION_BAR_CONTROLLER_LOADER_ID_BASE + 0; + + private final Context mContext; + private final LoaderManager mLoaderManager; + private final ActionBar mActionBar; + + private final View mActionBarMailboxNameView; + private final TextView mActionBarMailboxName; + private final TextView mActionBarUnreadCount; + + private final ActionBarNavigationCallback mActionBarNavigationCallback = + new ActionBarNavigationCallback(); + + private final AccountSelectorAdapter mAccountsSelectorAdapter; + private Cursor mAccountsSelectorCursor; + + public final Callback mCallback; + + public interface Callback { + /** @return true if an account is selected. */ + public boolean isAccountSelected(); + + /** + * @return currently selected account ID, {@link Account#ACCOUNT_ID_COMBINED_VIEW}, + * or -1 if no account is selected. + */ + public long getUIAccountId(); + + /** @return true if the current mailbox name should be shown. */ + public boolean shouldShowMailboxName(); + + /** @return current mailbox name */ + public String getCurrentMailboxName(); + /** + * @return unread count for the current mailbox. (0 if the mailbox doesn't have the concept + * of "unread"; e.g. Drafts) + */ + public int getCurrentMailboxUnreadCount(); + + /** @return the "UP" arrow should be shown. */ + public boolean shouldShowUp(); + + /** + * Called when an account is selected on the account spinner. + * + * @param accountId ID of the selected account, or + * {@link Account#ACCOUNT_ID_COMBINED_VIEW}. + */ + public void onAccountSelected(long accountId); + + /** Called when no accounts are found in the database. */ + public void onNoAccountsFound(); + } + + public ActionBarController(Context context, LoaderManager loaderManager, + ActionBar actionBar, Callback callback) { + mContext = context; + mLoaderManager = loaderManager; + mActionBar = actionBar; + mCallback = callback; + mAccountsSelectorAdapter = new AccountSelectorAdapter(mContext); + + mActionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_HOME + | ActionBar.DISPLAY_SHOW_CUSTOM); + + // The custom view for the current mailbox and the unread count. + final LayoutInflater inflater = LayoutInflater.from(mContext); + mActionBarMailboxNameView = inflater.inflate(R.layout.action_bar_current_mailbox, null); + final ActionBar.LayoutParams customViewLayout = new ActionBar.LayoutParams( + ActionBar.LayoutParams.WRAP_CONTENT, + ActionBar.LayoutParams.MATCH_PARENT); + customViewLayout.setMargins(mContext.getResources().getDimensionPixelSize( + R.dimen.action_bar_mailbox_name_left_margin) , 0, 0, 0); + mActionBar.setCustomView(mActionBarMailboxNameView, customViewLayout); + + mActionBarMailboxName = UiUtilities.getView(mActionBarMailboxNameView, R.id.mailbox_name); + mActionBarUnreadCount = UiUtilities.getView(mActionBarMailboxNameView, R.id.unread_count); + } + + /** + * Must be called when the host activity is created. + */ + public void onActivityCreated() { + loadAccounts(); + refresh(); + } + + /** Used only in {@link #refresh()} to determine if the account has changed. */ + private long mLastAccountIdForDirtyCheck = -1; + + /** + * Refresh the content. + */ + public void refresh() { + mActionBar.setDisplayOptions(mCallback.shouldShowUp() + ? ActionBar.DISPLAY_HOME_AS_UP : 0, ActionBar.DISPLAY_HOME_AS_UP); + + mActionBarMailboxNameView.setVisibility(mCallback.shouldShowMailboxName() + ? View.VISIBLE : View.GONE); + + mActionBarMailboxName.setText(mCallback.getCurrentMailboxName()); + + // Note on action bar, we show only "unread count". Some mailboxes such as Outbox don't + // have the idea of "unread count", in which case we just omit the count. + mActionBarUnreadCount.setText(UiUtilities.getMessageCountForUi(mContext, + mCallback.getCurrentMailboxUnreadCount(), true)); + + // Update the account list only when the account has changed. + if (mLastAccountIdForDirtyCheck != mCallback.getUIAccountId()) { + mLastAccountIdForDirtyCheck = mCallback.getUIAccountId(); + updateAccountList(); + } + } + + /** + * Load account cursor, and update the action bar. + */ + private void loadAccounts() { + mLoaderManager.initLoader(LOADER_ID_ACCOUNT_LIST, null, + new LoaderCallbacks() { + @Override + public Loader onCreateLoader(int id, Bundle args) { + return AccountSelectorAdapter.createLoader(mContext); + } + + @Override + public void onLoadFinished(Loader loader, Cursor data) { + mAccountsSelectorCursor = data; + updateAccountList(); + } + + @Override + public void onLoaderReset(Loader loader) { + mAccountsSelectorCursor = null; + updateAccountList(); + } + }); + } + + /** + * Called when the LOADER_ID_ACCOUNT_LIST loader loads the data. Update the account spinner + * on the action bar. + */ + private void updateAccountList() { + mAccountsSelectorAdapter.swapCursor(mAccountsSelectorCursor); + + final ActionBar ab = mActionBar; + if (mAccountsSelectorCursor == null) { + // Cursor not ready or closed. + mAccountsSelectorAdapter.swapCursor(null); + ab.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE); + ab.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); + return; + } + + final int count = mAccountsSelectorCursor.getCount(); + if (count == 0) { + mCallback.onNoAccountsFound(); + return; + } + + // If only one acount, don't show the dropdown. + if (count == 1) { + mAccountsSelectorCursor.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)); + return; + } + + // Find the currently selected account, and select it. + int defaultSelection = 0; + if (mCallback.isAccountSelected()) { + final long accountId = mCallback.getUIAccountId(); + mAccountsSelectorCursor.moveToPosition(-1); + int i = 0; + while (mAccountsSelectorCursor.moveToNext()) { + if (accountId == AccountSelectorAdapter.getAccountId(mAccountsSelectorCursor)) { + defaultSelection = i; + break; + } + i++; + } + } + + // Update the dropdown list. + if (ab.getNavigationMode() != ActionBar.NAVIGATION_MODE_LIST) { + ab.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE); + ab.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); + ab.setListNavigationCallbacks(mAccountsSelectorAdapter, mActionBarNavigationCallback); + } + ab.setSelectedNavigationItem(defaultSelection); + } + + private class ActionBarNavigationCallback implements ActionBar.OnNavigationListener { + @Override + public boolean onNavigationItemSelected(int itemPosition, long accountId) { + if (accountId != mCallback.getUIAccountId()) { + mCallback.onAccountSelected(accountId); + } + return true; + } + } +} diff --git a/src/com/android/email/activity/EmailActivity.java b/src/com/android/email/activity/EmailActivity.java index c3fdee4b3..b312d4fa7 100644 --- a/src/com/android/email/activity/EmailActivity.java +++ b/src/com/android/email/activity/EmailActivity.java @@ -68,6 +68,9 @@ public class EmailActivity extends Activity implements View.OnClickListener { /** Loader IDs starting with this is safe to use from UIControllers. */ static final int UI_CONTROLLER_LOADER_ID_BASE = 100; + /** Loader IDs starting with this is safe to use from ActionBarController. */ + static final int ACTION_BAR_CONTROLLER_LOADER_ID_BASE = 200; + private static final int MAILBOX_SYNC_FREQUENCY_DIALOG = 1; private static final int MAILBOX_SYNC_LOOKBACK_DIALOG = 2; diff --git a/src/com/android/email/activity/UIControllerTwoPane.java b/src/com/android/email/activity/UIControllerTwoPane.java index 9794e497c..13afb364d 100644 --- a/src/com/android/email/activity/UIControllerTwoPane.java +++ b/src/com/android/email/activity/UIControllerTwoPane.java @@ -29,23 +29,16 @@ import com.android.emailcommon.provider.EmailContent.Mailbox; import com.android.emailcommon.utility.EmailAsyncTask; import com.android.emailcommon.utility.Utility; -import android.app.ActionBar; import android.app.Activity; import android.app.Fragment; import android.app.FragmentManager; import android.app.FragmentTransaction; -import android.app.LoaderManager.LoaderCallbacks; import android.content.Context; -import android.content.Loader; -import android.database.Cursor; import android.os.Bundle; import android.util.Log; -import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; -import android.view.View; -import android.widget.TextView; import java.security.InvalidParameterException; import java.util.ArrayList; @@ -78,9 +71,6 @@ class UIControllerTwoPane implements /* package */ static final int MAILBOX_REFRESH_MIN_INTERVAL = 30 * 1000; // in milliseconds /* package */ static final int INBOX_AUTO_REFRESH_MIN_INTERVAL = 10 * 1000; // in milliseconds - private static final int LOADER_ID_ACCOUNT_LIST - = EmailActivity.UI_CONTROLLER_LOADER_ID_BASE + 0; - /** No account selected */ static final long NO_ACCOUNT = -1; /** No mailbox selected */ @@ -102,14 +92,9 @@ class UIControllerTwoPane implements /** Current message id */ private long mMessageId = NO_MESSAGE; - // Action bar - private ActionBar mActionBar; - private AccountSelectorAdapter mAccountsSelectorAdapter; - private final ActionBarNavigationCallback mActionBarNavigationCallback = - new ActionBarNavigationCallback(); - private View mActionBarMailboxNameView; - private TextView mActionBarMailboxName; - private TextView mActionBarUnreadCount; + private ActionBarController mActionBarController; + private final ActionBarControllerCallback mActionBarControllerCallback = + new ActionBarControllerCallback(); // Other UI elements private ThreePaneLayout mThreePane; @@ -140,6 +125,20 @@ class UIControllerTwoPane implements /** Mailbox IDs that the user has navigated away from; used to provide "back" functionality */ private final Stack mMailboxStack = new Stack(); + /** + * The mailbox name selected on the mailbox list. + * Passed via {@link #onCurrentMailboxUpdated}. + */ + private String mCurrentMailboxName; + + /** + * The unread count for the mailbox selected on the mailbox list. + * Passed via {@link #onCurrentMailboxUpdated}. + * + * 0 if the mailbox doesn't have the concept of "unread". e.g. Drafts. + */ + private int mCurrentMailboxUnreadCount; + /** * List of fragments that are restored by the framework while the activity is being re-created * for configuration changes (e.g. screen rotation). We'll install them later when the activity @@ -207,8 +206,7 @@ class UIControllerTwoPane implements // ThreePaneLayoutCallback @Override public void onVisiblePanesChanged(int previousVisiblePanes) { - - updateActionBar(); + refreshActionBar(); // If the right pane is gone, remove the message view. final int visiblePanes = mThreePane.getVisiblePanes(); @@ -228,22 +226,10 @@ class UIControllerTwoPane implements } } - /** - * Update the action bar according to the current state. - * - * - Show/hide the "back" button next to the "Home" icon. - * - Show/hide the current mailbox name. - */ - private void updateActionBar() { - final int visiblePanes = mThreePane.getVisiblePanes(); - - // If the left pane (mailbox list pane) is hidden, the back action on action bar will be - // enabled, and we also show the current mailbox name. - final boolean leftPaneHidden = ((visiblePanes & ThreePaneLayout.PANE_LEFT) == 0); - boolean displayUp = leftPaneHidden || !mMailboxStack.isEmpty(); - mActionBar.setDisplayOptions(displayUp ? ActionBar.DISPLAY_HOME_AS_UP : 0, - ActionBar.DISPLAY_HOME_AS_UP); - mActionBarMailboxNameView.setVisibility(leftPaneHidden ? View.VISIBLE : View.GONE); + private void refreshActionBar() { + if (mActionBarController != null) { + mActionBarController.refresh(); + } } // MailboxListFragment$Callback @@ -276,18 +262,14 @@ class UIControllerTwoPane implements // TODO openAccount should do the check eventually, but it's necessary for now. if (accountId != getUIAccountId()) { openAccount(accountId); - loadAccounts(); // update account spinner } } @Override public void onCurrentMailboxUpdated(long mailboxId, String mailboxName, int unreadCount) { - mActionBarMailboxName.setText(mailboxName); - - // Note on action bar, we show only "unread count". Some mailboxes such as Outbox don't - // have the idea of "unread count", in which case we just omit the count. - mActionBarUnreadCount.setText( - UiUtilities.getMessageCountForUi(mActivity, unreadCount, true)); + mCurrentMailboxName = mailboxName; + mCurrentMailboxUnreadCount = unreadCount; + refreshActionBar(); } // MessageListFragment$Callback @@ -433,26 +415,8 @@ class UIControllerTwoPane implements if (Email.DEBUG_LIFECYCLE && Email.DEBUG) { Log.d(Logging.LOG_TAG, "" + this + " onActivityViewReady"); } - // Set up action bar - mActionBar = mActivity.getActionBar(); - mActionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_HOME); - - // Set a view for the current mailbox to the action bar. - final LayoutInflater inflater = LayoutInflater.from(mActivity); - mActionBarMailboxNameView = inflater.inflate(R.layout.action_bar_current_mailbox, null); - mActionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, ActionBar.DISPLAY_SHOW_CUSTOM); - final ActionBar.LayoutParams customViewLayout = new ActionBar.LayoutParams( - ActionBar.LayoutParams.WRAP_CONTENT, - ActionBar.LayoutParams.MATCH_PARENT); - customViewLayout.setMargins(mActivity.getResources().getDimensionPixelSize( - R.dimen.action_bar_mailbox_name_left_margin) , 0, 0, 0); - mActionBar.setCustomView(mActionBarMailboxNameView, customViewLayout); - - mActionBarMailboxName = - (TextView) mActionBarMailboxNameView.findViewById(R.id.mailbox_name); - mActionBarUnreadCount = - (TextView) mActionBarMailboxNameView.findViewById(R.id.unread_count); - + mActionBarController = new ActionBarController(mActivity, mActivity.getLoaderManager(), + mActivity.getActionBar(), mActionBarControllerCallback); // Set up content mThreePane = (ThreePaneLayout) mActivity.findViewById(R.id.three_pane); @@ -534,7 +498,7 @@ class UIControllerTwoPane implements */ public void onActivityCreated() { mRefreshManager.registerListener(mRefreshListener); - loadAccounts(); + mActionBarController.onActivityCreated(); } /** @@ -581,7 +545,7 @@ class UIControllerTwoPane implements * Called from {@link EmailActivity#onResume}. */ public void onResume() { - updateActionBar(); + refreshActionBar(); } /** @@ -698,8 +662,8 @@ class UIControllerTwoPane implements */ public void openAccount(long accountId) { mMailboxStack.clear(); - updateActionBar(); open(accountId, NO_MAILBOX, NO_MESSAGE); + refreshActionBar(); } /** @@ -710,9 +674,9 @@ class UIControllerTwoPane implements * mailbox is not associated with the account, the behaviour is undefined. */ private void openMailbox(long accountId, long mailboxId) { - updateActionBar(); updateMailboxList(accountId, mailboxId, true, true); updateMessageList(mailboxId, true, true); + refreshActionBar(); } /** @@ -1012,97 +976,6 @@ class UIControllerTwoPane implements return false; } - /** - * Load account list for the action bar. - * - * If there's only one account configured, show the account name in the action bar. - * If more than one account are configured, show a spinner in the action bar, and select the - * current account. - */ - private void loadAccounts() { - if (mAccountsSelectorAdapter == null) { - mAccountsSelectorAdapter = new AccountSelectorAdapter(mActivity); - } - mActivity.getLoaderManager().initLoader(LOADER_ID_ACCOUNT_LIST, null, - new LoaderCallbacks() { - @Override - public Loader onCreateLoader(int id, Bundle args) { - return AccountSelectorAdapter.createLoader(mActivity); - } - - @Override - public void onLoadFinished(Loader loader, Cursor data) { - updateAccountList(data); - } - - @Override - public void onLoaderReset(Loader loader) { - mAccountsSelectorAdapter.swapCursor(null); - } - }); - } - - /** - * Called when the LOADER_ID_ACCOUNT_LIST loader loads the data. Update the account spinner - * on the action bar. - */ - private void updateAccountList(Cursor accountsCursor) { - final int count = accountsCursor.getCount(); - if (count == 0) { - // Open Welcome, which in turn shows the adding a new account screen. - Welcome.actionStart(mActivity); - mActivity.finish(); - return; - } - - // If ony one acount, don't show dropdown. - final ActionBar ab = mActionBar; - if (count == 1) { - accountsCursor.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(accountsCursor)); - return; - } - - // Find the currently selected account, and select it. - int defaultSelection = 0; - if (isAccountSelected()) { - accountsCursor.moveToPosition(-1); - int i = 0; - while (accountsCursor.moveToNext()) { - final long accountId = AccountSelectorAdapter.getAccountId(accountsCursor); - if (accountId == getUIAccountId()) { - defaultSelection = i; - break; - } - i++; - } - } - - // Update the dropdown list. - mAccountsSelectorAdapter.swapCursor(accountsCursor); - - // Don't show the title. - ab.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE); - ab.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); - ab.setListNavigationCallbacks(mAccountsSelectorAdapter, mActionBarNavigationCallback); - ab.setSelectedNavigationItem(defaultSelection); - } - - private class ActionBarNavigationCallback implements ActionBar.OnNavigationListener { - @Override - public boolean onNavigationItemSelected(int itemPosition, long accountId) { - // TODO openAccount should do the check eventually, but it's necessary for now. - if (accountId != getUIAccountId()) { - openAccount(accountId); - } - return true; - } - } - /** * Handles {@link android.app.Activity#onCreateOptionsMenu} callback. */ @@ -1321,4 +1194,50 @@ class UIControllerTwoPane implements return true; } } + + private class ActionBarControllerCallback implements ActionBarController.Callback { + @Override + public String getCurrentMailboxName() { + return mCurrentMailboxName; + } + + @Override + public int getCurrentMailboxUnreadCount() { + return mCurrentMailboxUnreadCount; + } + + @Override + public long getUIAccountId() { + return UIControllerTwoPane.this.getUIAccountId(); + } + + @Override + public boolean isAccountSelected() { + return UIControllerTwoPane.this.isAccountSelected(); + } + + @Override + public void onAccountSelected(long accountId) { + openAccount(accountId); + } + + @Override + public void onNoAccountsFound() { + Welcome.actionStart(mActivity); + mActivity.finish(); + } + + @Override + public boolean shouldShowMailboxName() { + // Show when the left pane is hidden. + return (mThreePane.getVisiblePanes() & ThreePaneLayout.PANE_LEFT) == 0; + } + + @Override + public boolean shouldShowUp() { + final int visiblePanes = mThreePane.getVisiblePanes(); + final boolean leftPaneHidden = ((visiblePanes & ThreePaneLayout.PANE_LEFT) == 0); + return leftPaneHidden || !mMailboxStack.isEmpty(); + } + } }