Add tabs to People app
- Move account filter into overflow menu
- Use action bar in tab and standard mode so the SearchView
is right aligned and part of the options menu instead of the
custom view in the action bar
- Make visibility of action bar home icon a style so the icon
can be hidden on the phone
- TODO: Make physical search button work on the phone so the
search box can be removed from the action bar
- TODO: Fix SearchView focus problems
Change-Id: I7b3ba49f80e1911fb4a096679a00560967584426
diff --git a/src/com/android/contacts/activities/ActionBarAdapter.java b/src/com/android/contacts/activities/ActionBarAdapter.java
index 7ca5921..680c8bd 100644
--- a/src/com/android/contacts/activities/ActionBarAdapter.java
+++ b/src/com/android/contacts/activities/ActionBarAdapter.java
@@ -17,22 +17,18 @@
package com.android.contacts.activities;
import com.android.contacts.R;
+import com.android.contacts.activities.ActionBarAdapter.Listener.Action;
import com.android.contacts.list.ContactListFilterController;
import com.android.contacts.list.ContactListFilterController.ContactListFilterListener;
-import com.android.contacts.list.ContactListFilterView;
import com.android.contacts.list.ContactsRequest;
import android.app.ActionBar;
-import android.app.ActionBar.LayoutParams;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
import android.widget.SearchView;
import android.widget.SearchView.OnCloseListener;
import android.widget.SearchView.OnQueryTextListener;
-import android.widget.TextView;
/**
* Adapter for the action bar at the top of the Contacts activity.
@@ -41,7 +37,11 @@
implements OnQueryTextListener, OnCloseListener, ContactListFilterListener {
public interface Listener {
- void onAction();
+ public enum Action {
+ CHANGE_SEARCH_QUERY, START_SEARCH_MODE, STOP_SEARCH_MODE
+ }
+
+ void onAction(Action action);
}
private static final String EXTRA_KEY_SEARCH_MODE = "navBar.searchMode";
@@ -50,24 +50,25 @@
private boolean mSearchMode;
private String mQueryString;
- private View mNavigationBar;
- private TextView mSearchLabel;
+ private String mSearchLabelText;
private SearchView mSearchView;
private final Context mContext;
private Listener mListener;
- private ContactListFilterView mFilterView;
private ContactListFilterController mFilterController;
- private boolean mEnabled;
+ private ActionBar mActionBar;
public ActionBarAdapter(Context context) {
mContext = context;
+ mSearchLabelText = mContext.getString(R.string.search_label);
}
public void onCreate(Bundle savedState, ContactsRequest request, ActionBar actionBar) {
+ mActionBar = actionBar;
mQueryString = null;
+
if (savedState != null) {
mSearchMode = savedState.getBoolean(EXTRA_KEY_SEARCH_MODE);
mQueryString = savedState.getString(EXTRA_KEY_QUERY);
@@ -76,33 +77,18 @@
mQueryString = request.getQueryString();
}
- if (actionBar != null) {
- actionBar.setDisplayOptions(
- ActionBar.DISPLAY_SHOW_CUSTOM, ActionBar.DISPLAY_SHOW_CUSTOM);
+ if (mSearchView != null) {
+ mSearchView.setQuery(mQueryString, false);
}
- mNavigationBar = LayoutInflater.from(mContext).inflate(R.layout.navigation_bar, null);
- LayoutParams layoutParams = new LayoutParams(
- LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
- if (actionBar != null) {
- actionBar.setCustomView(mNavigationBar, layoutParams);
- }
-
- mFilterView = (ContactListFilterView) mNavigationBar.findViewById(R.id.filter_view);
- mSearchLabel = (TextView) mNavigationBar.findViewById(R.id.search_label);
- mSearchView = (SearchView) mNavigationBar.findViewById(R.id.search_view);
-
- mSearchView.setOnQueryTextListener(this);
- mSearchView.setOnCloseListener(this);
- mSearchView.setQuery(mQueryString, false);
- mSearchView.setQueryHint(mContext.getString(R.string.hint_findContacts));
-
update();
}
- public void setEnabled(boolean enabled) {
- mEnabled = enabled;
- update();
+ public void setSearchView(SearchView searchView) {
+ mSearchView = searchView;
+ mSearchView.setOnQueryTextListener(this);
+ mSearchView.setOnCloseListener(this);
+ mSearchView.setQuery(mQueryString, false);
}
public void setListener(Listener listener) {
@@ -111,7 +97,6 @@
public void setContactListFilterController(ContactListFilterController controller) {
mFilterController = controller;
- mFilterController.setAnchor(mFilterView);
mFilterController.addListener(this);
}
@@ -128,9 +113,6 @@
} else {
mSearchView.setQuery(null, false);
}
- if (mListener != null) {
- mListener.onAction();
- }
}
}
@@ -144,39 +126,36 @@
}
public void update() {
- if (!mEnabled) {
- mNavigationBar.setVisibility(View.GONE);
- } else if (mSearchMode) {
- mNavigationBar.setVisibility(View.VISIBLE);
- mSearchLabel.setVisibility(View.VISIBLE);
- mFilterView.setVisibility(View.GONE);
- if (mFilterController != null) {
- mFilterController.setEnabled(false);
+ if (mSearchMode) {
+ mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
+ mActionBar.setTitle(mSearchLabelText);
+ if (mListener != null) {
+ mListener.onAction(Action.START_SEARCH_MODE);
}
} else {
- mNavigationBar.setVisibility(View.VISIBLE);
- mSearchLabel.setVisibility(View.GONE);
- mFilterView.setVisibility(View.VISIBLE);
- if (mFilterController != null){
- mFilterController.setEnabled(true);
- if (mFilterController.isLoaded()) {
- mFilterView.setContactListFilter(mFilterController.getFilter());
- mFilterView.setSingleAccount(mFilterController.getAccountCount() == 1);
- mFilterView.bindView(false);
- }
+ mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+ mActionBar.setTitle(null);
+ if (mListener != null) {
+ mListener.onAction(Action.STOP_SEARCH_MODE);
}
}
}
@Override
public boolean onQueryTextChange(String queryString) {
+ // TODO: Clean up SearchView code because it keeps setting the SearchView query,
+ // invoking onQueryChanged, setting up the fragment again, invalidating the options menu,
+ // storing the SearchView again, and etc... unless we add in the early return statements.
+ if (queryString.equals(mQueryString)) {
+ return false;
+ }
mQueryString = queryString;
if (!mSearchMode) {
if (!TextUtils.isEmpty(queryString)) {
setSearchMode(true);
}
} else if (mListener != null) {
- mListener.onAction();
+ mListener.onAction(Action.CHANGE_SEARCH_QUERY);
}
return true;
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 1379e08..f537c9e 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -19,7 +19,10 @@
import com.android.contacts.ContactSaveService;
import com.android.contacts.ContactsActivity;
import com.android.contacts.R;
+import com.android.contacts.calllog.CallLogFragment;
import com.android.contacts.detail.ContactDetailFragment;
+import com.android.contacts.dialpad.DialpadFragment;
+import com.android.contacts.group.GroupBrowseListFragment;
import com.android.contacts.interactions.ContactDeletionInteraction;
import com.android.contacts.interactions.GroupDeletionDialogFragment;
import com.android.contacts.interactions.GroupRenamingDialogFragment;
@@ -34,11 +37,13 @@
import com.android.contacts.list.ContactsRequest;
import com.android.contacts.list.ContactsUnavailableFragment;
import com.android.contacts.list.CustomContactListFilterActivity;
+import com.android.contacts.list.DefaultContactBrowseListFragment;
import com.android.contacts.list.DirectoryListLoader;
import com.android.contacts.list.OnContactBrowserActionListener;
import com.android.contacts.list.OnContactsUnavailableActionListener;
import com.android.contacts.list.ProviderStatusLoader;
import com.android.contacts.list.ProviderStatusLoader.ProviderStatusListener;
+import com.android.contacts.list.StrequentContactListFragment;
import com.android.contacts.model.AccountTypeManager;
import com.android.contacts.preference.ContactsPreferenceActivity;
import com.android.contacts.util.AccountSelectionUtil;
@@ -47,12 +52,17 @@
import android.accounts.Account;
import android.app.ActionBar;
+import android.app.ActionBar.Tab;
+import android.app.ActionBar.TabListener;
import android.app.Activity;
import android.app.Dialog;
import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
import android.content.ActivityNotFoundException;
import android.content.ContentValues;
import android.content.Intent;
+import android.content.res.TypedArray;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
@@ -68,6 +78,7 @@
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
+import android.widget.SearchView;
import android.widget.Toast;
import java.util.ArrayList;
@@ -94,7 +105,6 @@
private ContactsIntentResolver mIntentResolver;
private ContactsRequest mRequest;
- private boolean mHasActionBar;
private ActionBarAdapter mActionBarAdapter;
private boolean mSearchMode;
@@ -126,8 +136,14 @@
private boolean mOptionsMenuContactsAvailable;
private boolean mOptionsMenuGroupActionsEnabled;
+ private DefaultContactBrowseListFragment mContactsFragment;
+ private StrequentContactListFragment mFavoritesFragment;
+ private GroupBrowseListFragment mGroupsFragment;
+
public PeopleActivity() {
mIntentResolver = new ContactsIntentResolver(this);
+ // TODO: Get rid of the ContactListFilterController class because there aren't any
+ // dropdown filters anymore. Just store the selected filter as a member variable.
mContactListFilterController = new ContactListFilterController(this);
mContactListFilterController.addListener(this);
mProviderStatusLoader = new ProviderStatusLoader(this);
@@ -201,6 +217,21 @@
if (createContentView) {
setContentView(R.layout.people_activity);
+
+ final FragmentManager fragmentManager = getFragmentManager();
+ mFavoritesFragment = (StrequentContactListFragment) fragmentManager
+ .findFragmentById(R.id.favorites_fragment);
+ mContactsFragment = (DefaultContactBrowseListFragment) fragmentManager
+ .findFragmentById(R.id.contacts_fragment);
+ mGroupsFragment = (GroupBrowseListFragment) fragmentManager
+ .findFragmentById(R.id.groups_fragment);
+
+ // Hide all tabs (the current tab will later be reshown once a tab is selected)
+ final FragmentTransaction transaction = fragmentManager.beginTransaction();
+ transaction.hide(mFavoritesFragment);
+ transaction.hide(mContactsFragment);
+ transaction.hide(mGroupsFragment);
+ transaction.commit();
}
if (mRequest.getActionCode() == ContactsRequest.ACTION_VIEW_CONTACT
@@ -214,21 +245,66 @@
}
setTitle(mRequest.getActivityTitle());
+ ActionBar actionBar = getActionBar();
+ mActionBarAdapter = new ActionBarAdapter(this);
+ mActionBarAdapter.onCreate(savedState, mRequest, getActionBar());
+ mActionBarAdapter.setContactListFilterController(mContactListFilterController);
if (createContentView) {
- mHasActionBar = getWindow().hasFeature(Window.FEATURE_ACTION_BAR);
- if (mHasActionBar) {
- ActionBar actionBar = getActionBar();
+ actionBar.removeAllTabs();
+ Tab favoritesTab = actionBar.newTab();
+ favoritesTab.setText(getString(R.string.strequentList));
+ favoritesTab.setTabListener(new TabChangeListener(mFavoritesFragment));
+ actionBar.addTab(favoritesTab);
- mActionBarAdapter = new ActionBarAdapter(this);
- mActionBarAdapter.onCreate(savedState, mRequest, actionBar);
- mActionBarAdapter.setContactListFilterController(mContactListFilterController);
- }
+ Tab peopleTab = actionBar.newTab();
+ peopleTab.setText(getString(R.string.people));
+ peopleTab.setTabListener(new TabChangeListener(mContactsFragment));
+ actionBar.addTab(peopleTab);
+
+ Tab groupsTab = actionBar.newTab();
+ groupsTab.setText(getString(R.string.contactsGroupsLabel));
+ groupsTab.setTabListener(new TabChangeListener(mGroupsFragment));
+ actionBar.addTab(groupsTab);
+ actionBar.setDisplayShowTitleEnabled(true);
+
+ TypedArray a = obtainStyledAttributes(null, R.styleable.ActionBarHomeIcon);
+ boolean showHomeIcon = a.getBoolean(R.styleable.ActionBarHomeIcon_show_home_icon, true);
+ actionBar.setDisplayShowHomeEnabled(showHomeIcon);
+
+ invalidateOptionsMenu();
}
configureFragments(savedState == null);
}
+ /**
+ * Tab change listener that is instantiated once for each tab. Handles showing/hiding fragments.
+ * TODO: Use ViewPager so that tabs can be swiped left and right. Figure out how to use the
+ * support library in our app.
+ */
+ private class TabChangeListener implements TabListener {
+ private final Fragment mFragment;
+
+ public TabChangeListener(Fragment fragment) {
+ mFragment = fragment;
+ }
+
+ @Override
+ public void onTabUnselected(Tab tab, FragmentTransaction ft) {
+ ft.hide(mFragment);
+ }
+
+ @Override
+ public void onTabSelected(Tab tab, FragmentTransaction ft) {
+ ft.show(mFragment);
+ }
+
+ @Override
+ public void onTabReselected(Tab tab, FragmentTransaction ft) {
+ }
+ }
+
@Override
protected void onPause() {
if (mActionBarAdapter != null) {
@@ -259,12 +335,6 @@
super.onStart();
}
- @Override
- protected void onStop() {
- mContactListFilterController.onStop();
- super.onStop();
- }
-
private void configureFragments(boolean fromRequest) {
if (fromRequest) {
ContactListFilter filter = null;
@@ -303,7 +373,7 @@
mListFragment.setContactsRequest(mRequest);
configureListFragmentForRequest();
- } else if (mHasActionBar) {
+ } else {
mSearchMode = mActionBarAdapter.isSearchMode();
}
@@ -348,9 +418,23 @@
* Handler for action bar actions.
*/
@Override
- public void onAction() {
- configureFragments(false /* from request */);
- mListFragment.setQueryString(mActionBarAdapter.getQueryString(), true);
+ public void onAction(Action action) {
+ switch (action) {
+ case START_SEARCH_MODE:
+ // Bring the contact list fragment to the front.
+ FragmentTransaction ft = getFragmentManager().beginTransaction();
+ ft.show(mContactsFragment);
+ ft.commit();
+ break;
+ case STOP_SEARCH_MODE:
+ case CHANGE_SEARCH_QUERY:
+ // Refresh the contact list fragment.
+ configureFragments(false /* from request */);
+ mListFragment.setQueryString(mActionBarAdapter.getQueryString(), true);
+ break;
+ default:
+ throw new IllegalStateException("Unkonwn ActionBarAdapter action: " + action);
+ }
}
private void configureListFragmentForRequest() {
@@ -403,17 +487,13 @@
if (mProviderStatus == ProviderStatus.STATUS_NORMAL) {
contactsUnavailableView.setVisibility(View.GONE);
- mainView.setVisibility(View.VISIBLE);
+ if (mainView != null) {
+ mainView.setVisibility(View.VISIBLE);
+ }
if (mListFragment != null) {
mListFragment.setEnabled(true);
}
- if (mHasActionBar) {
- mActionBarAdapter.setEnabled(true);
- }
} else {
- if (mHasActionBar) {
- mActionBarAdapter.setEnabled(false);
- }
if (mListFragment != null) {
mListFragment.setEnabled(false);
}
@@ -429,7 +509,9 @@
mContactsUnavailableFragment.update();
}
contactsUnavailableView.setVisibility(View.VISIBLE);
- mainView.setVisibility(View.INVISIBLE);
+ if (mainView != null) {
+ mainView.setVisibility(View.INVISIBLE);
+ }
}
invalidateOptionsMenu();
@@ -625,27 +707,28 @@
if (!areContactsAvailable()) {
return false;
}
-
super.onCreateOptionsMenu(menu);
MenuInflater inflater = getMenuInflater();
- if (mHasActionBar) {
- inflater.inflate(R.menu.actions, menu);
+ inflater.inflate(R.menu.actions, menu);
+ // TODO: Figure out if R.menu.list or R.menu.search are necessary according to the overflow
+ // menus on the UX mocks.
+ MenuItem searchMenuItem = menu.findItem(R.id.menu_search);
+ if (searchMenuItem != null && searchMenuItem.getActionView() instanceof SearchView) {
+ SearchView searchView = (SearchView) searchMenuItem.getActionView();
+ searchView.setQueryHint(getString(R.string.hint_findContacts));
+ searchView.setIconifiedByDefault(false);
- // Change add contact button to button with a custom view
- final MenuItem addContact = menu.findItem(R.id.menu_add);
- addContact.setActionView(mAddContactImageView);
- return true;
- } else if (mRequest.getActionCode() == ContactsRequest.ACTION_ALL_CONTACTS ||
- mRequest.getActionCode() == ContactsRequest.ACTION_STREQUENT) {
- inflater.inflate(R.menu.list, menu);
- return true;
- } else if (!mListFragment.isSearchMode()) {
- inflater.inflate(R.menu.search, menu);
- return true;
- } else {
- return false;
+ if (mActionBarAdapter != null) {
+ mActionBarAdapter.setSearchView(searchView);
+ }
}
+
+ // TODO: Can remove this as a custom view because the account selector is in the editor now.
+ // Change add contact button to button with a custom view
+ final MenuItem addContact = menu.findItem(R.id.menu_add);
+ addContact.setActionView(mAddContactImageView);
+ return true;
}
@Override
@@ -723,6 +806,11 @@
startActivity(intent);
return true;
}
+ case R.id.menu_contacts_filter: {
+ final Intent intent = new Intent(this, CustomContactListFilterActivity.class);
+ startActivityForResult(intent, SUBACTIVITY_CUSTOMIZE_FILTER);
+ return true;
+ }
case R.id.menu_search: {
onSearchRequested();
return true;
@@ -865,12 +953,10 @@
final int unicodeChar = event.getUnicodeChar();
if (unicodeChar != 0 && !Character.isWhitespace(unicodeChar)) {
String query = new String(new int[]{ unicodeChar }, 0, 1);
- if (mHasActionBar) {
- if (!mActionBarAdapter.isSearchMode()) {
- mActionBarAdapter.setQueryString(query);
- mActionBarAdapter.setSearchMode(true);
- return true;
- }
+ if (!mActionBarAdapter.isSearchMode()) {
+ mActionBarAdapter.setQueryString(query);
+ mActionBarAdapter.setSearchMode(true);
+ return true;
} else if (!mRequest.isSearchMode()) {
if (!mSearchInitiated) {
mSearchInitiated = true;
diff --git a/src/com/android/contacts/list/ContactListFilterController.java b/src/com/android/contacts/list/ContactListFilterController.java
index c9f5530..4da5baa 100644
--- a/src/com/android/contacts/list/ContactListFilterController.java
+++ b/src/com/android/contacts/list/ContactListFilterController.java
@@ -43,6 +43,8 @@
/**
* Controls a list of {@link ContactListFilter}'s.
*/
+// TODO: Remove the extra functionality dealing with loading and displaying a list of filters in
+// the action bar.
public class ContactListFilterController
implements LoaderCallbacks<List<ContactListFilter>>, OnClickListener, OnItemClickListener {
@@ -84,11 +86,6 @@
mListeners.remove(listener);
}
- public void setAnchor(View anchor) {
- mAnchor = anchor;
- mAnchor.setOnClickListener(this);
- }
-
public ContactListFilter getFilter() {
return mFilter;
}
@@ -105,11 +102,6 @@
if (mFilter == null) {
mFilter = ContactListFilter.restoreFromPreferences(getSharedPreferences());
}
- mLoaderManager.initLoader(R.id.contact_list_filter_loader, null, this);
- }
-
- public void onStop() {
- mLoaderManager.destroyLoader(R.id.contact_list_filter_loader);
}
private SharedPreferences getSharedPreferences() {