399 lines
15 KiB
Java
399 lines
15 KiB
Java
/*
|
|
* 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.Account;
|
|
import com.android.emailcommon.provider.Mailbox;
|
|
|
|
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.text.TextUtils;
|
|
import android.util.Log;
|
|
import android.view.LayoutInflater;
|
|
import android.view.View;
|
|
import android.widget.SearchView;
|
|
import android.widget.TextView;
|
|
|
|
/**
|
|
* Manages the account name and the custom view part on the action bar.
|
|
*
|
|
* TODO Show current mailbox name/unread count on the account spinner
|
|
* -- and remove mMailboxNameContainer.
|
|
*
|
|
* TODO Stop using the action bar spinner and create our own spinner as a custom view.
|
|
* (so we'll be able to just hide it, etc.)
|
|
*
|
|
* TODO Update search hint somehow
|
|
*/
|
|
public class ActionBarController {
|
|
private static final String BUNDLE_KEY_MODE = "ActionBarController.BUNDLE_KEY_MODE";
|
|
|
|
/**
|
|
* Constants for {@link #mMode}.
|
|
*
|
|
* In {@link #MODE_NORMAL} mode, we don't show the search box.
|
|
* In {@link #MODE_SEARCH} mode, we do show the search box.
|
|
* The action bar doesn't really care if the activity is showing search results.
|
|
* If the activity is showing search results, and the {@link Callback#onSearchExit} is called,
|
|
* the activity probably wants to close itself, but this class doesn't make the desision.
|
|
*/
|
|
private static final int MODE_NORMAL = 0;
|
|
private static final int MODE_SEARCH = 1;
|
|
|
|
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 mActionBarCustomView;
|
|
private final View mMailboxNameContainer;
|
|
private final TextView mMailboxNameView;
|
|
private final TextView mUnreadCountView;
|
|
private final View mSearchContainer;
|
|
private final SearchView mSearchView;
|
|
|
|
private final ActionBarNavigationCallback mActionBarNavigationCallback =
|
|
new ActionBarNavigationCallback();
|
|
|
|
private final AccountSelectorAdapter mAccountsSelectorAdapter;
|
|
private AccountSelectorAdapter.CursorWithExtras mAccountCursor;
|
|
/** The current account ID; used to determine if the account has changed. */
|
|
private long mLastAccountIdForDirtyCheck = Account.NO_ACCOUNT;
|
|
|
|
/** Either {@link #MODE_NORMAL} or {@link #MODE_SEARCH}. */
|
|
private int mMode = MODE_NORMAL;
|
|
|
|
public final Callback mCallback;
|
|
|
|
public interface SearchContext {
|
|
public long getTargetMailboxId();
|
|
}
|
|
|
|
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);
|
|
|
|
/**
|
|
* 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();
|
|
|
|
/**
|
|
* Called when a search is submitted.
|
|
*
|
|
* @param queryTerm query string
|
|
*/
|
|
public void onSearchSubmit(String queryTerm);
|
|
|
|
/**
|
|
* Called when the search box is closed.
|
|
*/
|
|
public void onSearchExit();
|
|
}
|
|
|
|
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);
|
|
|
|
// Prepare the custom view
|
|
final LayoutInflater inflater = LayoutInflater.from(mContext);
|
|
mActionBarCustomView = inflater.inflate(R.layout.action_bar_custom_view, null);
|
|
final ActionBar.LayoutParams customViewLayout = new ActionBar.LayoutParams(
|
|
ActionBar.LayoutParams.WRAP_CONTENT,
|
|
ActionBar.LayoutParams.MATCH_PARENT);
|
|
customViewLayout.setMargins(0 , 0, 0, 0);
|
|
mActionBar.setCustomView(mActionBarCustomView, customViewLayout);
|
|
|
|
// Mailbox name / unread count
|
|
mMailboxNameContainer = UiUtilities.getView(mActionBarCustomView,
|
|
R.id.current_mailbox_container);
|
|
mMailboxNameView = UiUtilities.getView(mMailboxNameContainer, R.id.mailbox_name);
|
|
mUnreadCountView = UiUtilities.getView(mMailboxNameContainer, R.id.unread_count);
|
|
|
|
// Search
|
|
mSearchContainer = UiUtilities.getView(mActionBarCustomView, R.id.search_container);
|
|
mSearchView = UiUtilities.getView(mSearchContainer, R.id.search_view);
|
|
mSearchView.setSubmitButtonEnabled(true);
|
|
mSearchView.setOnQueryTextListener(mOnQueryText);
|
|
}
|
|
|
|
/** Must be called from {@link UIControllerBase#onActivityCreated()} */
|
|
public void onActivityCreated() {
|
|
loadAccounts();
|
|
refresh();
|
|
}
|
|
|
|
/** Must be called from {@link UIControllerBase#onActivityDestroy()} */
|
|
public void onActivityDestroy() {
|
|
}
|
|
|
|
/** Must be called from {@link UIControllerBase#onSaveInstanceState} */
|
|
public void onSaveInstanceState(Bundle outState) {
|
|
outState.putInt(BUNDLE_KEY_MODE, mMode);
|
|
}
|
|
|
|
/** Must be called from {@link UIControllerBase#onRestoreInstanceState} */
|
|
public void onRestoreInstanceState(Bundle savedState) {
|
|
mMode= savedState.getInt(BUNDLE_KEY_MODE);
|
|
}
|
|
|
|
/**
|
|
* @return true if the search box is shown.
|
|
*/
|
|
private boolean isInSearchMode() {
|
|
return mMode == MODE_SEARCH;
|
|
}
|
|
|
|
/**
|
|
* Show the search box.
|
|
*
|
|
* @param initialQueryTerm if non-empty, set to the search box.
|
|
*/
|
|
public void enterSearchMode(String initialQueryTerm) {
|
|
if (isInSearchMode()) {
|
|
return;
|
|
}
|
|
if (!TextUtils.isEmpty(initialQueryTerm)) {
|
|
mSearchView.setQuery(initialQueryTerm, false);
|
|
}
|
|
mMode = MODE_SEARCH;
|
|
refresh();
|
|
}
|
|
|
|
private void exitSearchMode() {
|
|
if (!isInSearchMode()) {
|
|
return;
|
|
}
|
|
mMode = MODE_NORMAL;
|
|
refresh();
|
|
mCallback.onSearchExit();
|
|
}
|
|
|
|
/**
|
|
* Performs the back action.
|
|
*
|
|
* @param isSystemBackKey <code>true</code> if the system back key was pressed.
|
|
* <code>false</code> if it's caused by the "home" icon click on the action bar.
|
|
*/
|
|
public boolean onBackPressed(boolean isSystemBackKey) {
|
|
if (isInSearchMode()) {
|
|
exitSearchMode();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/** Refreshes the action bar display. */
|
|
public void refresh() {
|
|
final boolean showUp = isInSearchMode() || mCallback.shouldShowUp();
|
|
mActionBar.setDisplayOptions(showUp
|
|
? ActionBar.DISPLAY_HOME_AS_UP : 0, ActionBar.DISPLAY_HOME_AS_UP);
|
|
|
|
// TODO In search mode, account spinner should be hidden.
|
|
// (See also the TODO in the class header -- this methods needs a lot of change.)
|
|
|
|
if (isInSearchMode()) {
|
|
boolean wasVisible = (mSearchView.getVisibility() == View.VISIBLE);
|
|
mSearchView.setVisibility(View.VISIBLE);
|
|
if (!wasVisible) {
|
|
mSearchView.requestFocus();
|
|
}
|
|
mMailboxNameContainer.setVisibility(View.GONE);
|
|
} else {
|
|
mSearchView.setVisibility(View.GONE);
|
|
mMailboxNameContainer.setVisibility(mCallback.shouldShowMailboxName()
|
|
? View.VISIBLE : View.GONE);
|
|
}
|
|
|
|
mMailboxNameView.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.
|
|
mUnreadCountView.setText(UiUtilities.getMessageCountForUi(mContext,
|
|
mCallback.getCurrentMailboxUnreadCount(), true));
|
|
|
|
// Update the account list only when the account has changed.
|
|
if (mLastAccountIdForDirtyCheck != mCallback.getUIAccountId()) {
|
|
mLastAccountIdForDirtyCheck = mCallback.getUIAccountId();
|
|
// 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();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Load account cursor, and update the action bar.
|
|
*/
|
|
private void loadAccounts() {
|
|
mLoaderManager.initLoader(LOADER_ID_ACCOUNT_LIST, null,
|
|
new LoaderCallbacks<Cursor>() {
|
|
@Override
|
|
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
|
return AccountSelectorAdapter.createLoader(mContext, mCallback.getUIAccountId());
|
|
}
|
|
|
|
@Override
|
|
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
|
|
mAccountCursor = (AccountSelectorAdapter.CursorWithExtras) data;
|
|
updateAccountList();
|
|
}
|
|
|
|
@Override
|
|
public void onLoaderReset(Loader<Cursor> loader) {
|
|
mAccountCursor = 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(mAccountCursor);
|
|
|
|
final ActionBar ab = mActionBar;
|
|
if (mAccountCursor == 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 = mAccountCursor.mAccountCount + mAccountCursor.mRecentCount;
|
|
if (count == 0) {
|
|
mCallback.onNoAccountsFound();
|
|
return;
|
|
}
|
|
|
|
// If only one account, don't show the drop down.
|
|
int selectedPosition = mAccountCursor.getPosition(mCallback.getUIAccountId());
|
|
if (count == 1) {
|
|
// Show the account name as the title.
|
|
ab.setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE, ActionBar.DISPLAY_SHOW_TITLE);
|
|
ab.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
|
|
if (selectedPosition >= 0) {
|
|
mAccountCursor.moveToPosition(selectedPosition);
|
|
ab.setTitle(AccountSelectorAdapter.getAccountDisplayName(mAccountCursor));
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Update the drop down 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);
|
|
}
|
|
// Find the currently selected account, and select it.
|
|
if (selectedPosition >= 0) {
|
|
ab.setSelectedNavigationItem(selectedPosition);
|
|
}
|
|
}
|
|
|
|
private class ActionBarNavigationCallback implements ActionBar.OnNavigationListener {
|
|
@Override
|
|
public boolean onNavigationItemSelected(int itemPosition, long itemId) {
|
|
if (mAccountsSelectorAdapter.isAccountItem(itemPosition)
|
|
&& itemId != mCallback.getUIAccountId()) {
|
|
mCallback.onAccountSelected(itemId);
|
|
} else if (mAccountsSelectorAdapter.isMailboxItem(itemPosition)) {
|
|
mCallback.onMailboxSelected(itemId);
|
|
// We need to update the selection, otherwise the user is unable to select the
|
|
// recent folder a second time w/o first selecting another item in the spinner
|
|
int selectedPosition = mAccountsSelectorAdapter.getAccountPosition(itemPosition);
|
|
if (selectedPosition != AccountSelectorAdapter.UNKNOWN_POSITION) {
|
|
mActionBar.setSelectedNavigationItem(selectedPosition);
|
|
}
|
|
} else {
|
|
Log.i(Logging.LOG_TAG,
|
|
"Invalid type selected in ActionBarController at index " + itemPosition);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
private final SearchView.OnQueryTextListener mOnQueryText
|
|
= new SearchView.OnQueryTextListener() {
|
|
@Override
|
|
public boolean onQueryTextChange(String newText) {
|
|
// Event not handled. Let the search do the default action.
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean onQueryTextSubmit(String query) {
|
|
mCallback.onSearchSubmit(mSearchView.getQuery().toString());
|
|
return true; // Event handled.
|
|
}
|
|
};
|
|
|
|
}
|