ActionBarAdapter.java revision d8a61b7296bf7388505f0211d7ef44700e766193
108e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov/* 2fcfdffb439ccf1ddef971109b82e782c47161572Dmitri Plotnikov * Copyright (C) 2010 The Android Open Source Project 308e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov * 408e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov * Licensed under the Apache License, Version 2.0 (the "License"); 508e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov * you may not use this file except in compliance with the License. 608e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov * You may obtain a copy of the License at 708e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov * 808e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov * http://www.apache.org/licenses/LICENSE-2.0 908e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov * 1008e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov * Unless required by applicable law or agreed to in writing, software 1108e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov * distributed under the License is distributed on an "AS IS" BASIS, 1208e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1308e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov * See the License for the specific language governing permissions and 1408e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov * limitations under the License. 1508e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov */ 1608e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 1708e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikovpackage com.android.contacts.activities; 1808e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 1908e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikovimport com.android.contacts.R; 20b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuanimport com.android.contacts.activities.ActionBarAdapter.Listener.Action; 2108e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikovimport com.android.contacts.list.ContactsRequest; 2208e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 231a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikovimport android.app.ActionBar; 241ee9df6facd2340416d6a33aeb739707643d2fc3Katherine Kuanimport android.app.ActionBar.LayoutParams; 254d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onukiimport android.app.ActionBar.Tab; 264d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onukiimport android.app.FragmentTransaction; 2708e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikovimport android.content.Context; 2835d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onukiimport android.content.SharedPreferences; 2908e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikovimport android.os.Bundle; 3035d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onukiimport android.preference.PreferenceManager; 3115ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikovimport android.text.TextUtils; 321ee9df6facd2340416d6a33aeb739707643d2fc3Katherine Kuanimport android.view.LayoutInflater; 331ee9df6facd2340416d6a33aeb739707643d2fc3Katherine Kuanimport android.view.View; 34d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuanimport android.view.inputmethod.InputMethodManager; 351a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikovimport android.widget.SearchView; 361a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikovimport android.widget.SearchView.OnCloseListener; 37c5792ae6760291d435e91e0fdd28ece6b3311439Adam Powellimport android.widget.SearchView.OnQueryTextListener; 3892a6fdf1b07b8f1447560174e24455fd46aee7cdDmitri Plotnikov 3908e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov/** 40fcfdffb439ccf1ddef971109b82e782c47161572Dmitri Plotnikov * Adapter for the action bar at the top of the Contacts activity. 4108e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov */ 42495db43f0a08bd71ba28a42f89b80d38af15afecKatherine Kuanpublic class ActionBarAdapter implements OnQueryTextListener, OnCloseListener { 4308e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 4408e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov public interface Listener { 45b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan public enum Action { 46b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan CHANGE_SEARCH_QUERY, START_SEARCH_MODE, STOP_SEARCH_MODE 47b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan } 48b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan 49b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan void onAction(Action action); 504d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 514d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki /** 524d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki * Called when the user selects a tab. The new tab can be obtained using 534d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki * {@link #getCurrentTab}. 544d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki */ 554d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki void onSelectedTabChanged(); 5608e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 5708e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 5815ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov private static final String EXTRA_KEY_SEARCH_MODE = "navBar.searchMode"; 5908e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov private static final String EXTRA_KEY_QUERY = "navBar.query"; 6059dc2751d64f60f743b8a9e78186aa5b79dcaf83Isaac Katzenelson private static final String EXTRA_KEY_SELECTED_TAB = "navBar.selectedTab"; 6108e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 6235d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki private static final String PERSISTENT_LAST_TAB = "actionBarAdapter.lastTab"; 6335d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki 6415ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov private boolean mSearchMode; 6508e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov private String mQueryString; 6615ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov 6715ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov private SearchView mSearchView; 6892a6fdf1b07b8f1447560174e24455fd46aee7cdDmitri Plotnikov 6908e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov private final Context mContext; 7035d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki private final SharedPreferences mPrefs; 7108e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 7208e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov private Listener mListener; 7308e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 744d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki private final ActionBar mActionBar; 754d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki private final MyTabListener mTabListener = new MyTabListener(); 768b8264620f9362e089322d1e3a7cc5620900e6f9Dmitri Plotnikov 77e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki private boolean mShowHomeIcon; 78e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki 794d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki public enum TabState { 804d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki FAVORITES, ALL, GROUPS; 811ee9df6facd2340416d6a33aeb739707643d2fc3Katherine Kuan 824d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki public static TabState fromInt(int value) { 834d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki switch (value) { 844d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki case 0: 854d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki return FAVORITES; 864d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki case 1: 874d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki return ALL; 884d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki case 2: 894d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki return GROUPS; 904d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 914d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki throw new IllegalArgumentException("Invalid value: " + value); 924d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 934d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 944d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 9535d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki private static final TabState DEFAULT_TAB = TabState.ALL; 9635d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki private TabState mCurrentTab = DEFAULT_TAB; 974d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 984d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki public ActionBarAdapter(Context context, Listener listener, ActionBar actionBar) { 9908e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov mContext = context; 1001ee9df6facd2340416d6a33aeb739707643d2fc3Katherine Kuan mListener = listener; 1014d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mActionBar = actionBar; 10235d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki mPrefs = PreferenceManager.getDefaultSharedPreferences(mContext); 10308e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 104e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki mShowHomeIcon = mContext.getResources().getBoolean(R.bool.show_home_icon); 105e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki 1064ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki // Set up search view. 1074ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki View customSearchView = LayoutInflater.from(mContext).inflate(R.layout.custom_action_bar, 1084ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki null); 1094ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki int searchViewWidth = mContext.getResources().getDimensionPixelSize( 1104ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki R.dimen.search_view_width); 1114ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki if (searchViewWidth == 0) { 1124ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki searchViewWidth = LayoutParams.MATCH_PARENT; 1131173ae29217fc83f254404f8a5fa10419ee83c93Dmitri Plotnikov } 1144ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki LayoutParams layoutParams = new LayoutParams(searchViewWidth, LayoutParams.WRAP_CONTENT); 1154ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki mSearchView = (SearchView) customSearchView.findViewById(R.id.search_view); 116d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // Since the {@link SearchView} in this app is "click-to-expand", set the below mode on the 117d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // {@link SearchView} so that the magnifying glass icon appears inside the editable text 118d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // field. (In the "click-to-expand" search pattern, the user must explicitly expand the 119d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // search field and already knows a search is being conducted, so the icon is redundant 120d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // and can go away once the user starts typing.) 121d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan mSearchView.setIconifiedByDefault(true); 1224ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki mSearchView.setQueryHint(mContext.getString(R.string.hint_findContacts)); 123c5792ae6760291d435e91e0fdd28ece6b3311439Adam Powell mSearchView.setOnQueryTextListener(this); 12415ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov mSearchView.setOnCloseListener(this); 12515ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov mSearchView.setQuery(mQueryString, false); 1264ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki mActionBar.setCustomView(customSearchView, layoutParams); 1274ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki 128e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki // Set up tabs 1294d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki addTab(TabState.FAVORITES, mContext.getString(R.string.contactsFavoritesLabel)); 1304d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki addTab(TabState.ALL, mContext.getString(R.string.contactsAllLabel)); 1314d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki addTab(TabState.GROUPS, mContext.getString(R.string.contactsGroupsLabel)); 1324d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 1334d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 1344d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki public void initialize(Bundle savedState, ContactsRequest request) { 1354d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki if (savedState == null) { 1364d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mSearchMode = request.isSearchMode(); 1374d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mQueryString = request.getQueryString(); 13835d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki mCurrentTab = loadLastTabPreference(); 1394d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } else { 1404d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mSearchMode = savedState.getBoolean(EXTRA_KEY_SEARCH_MODE); 1414d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mQueryString = savedState.getString(EXTRA_KEY_QUERY); 1424d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 1434d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // Just set to the field here. The listener will be notified by update(). 1444d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mCurrentTab = TabState.fromInt(savedState.getInt(EXTRA_KEY_SELECTED_TAB)); 1454d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 1464ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki update(); 1478b8264620f9362e089322d1e3a7cc5620900e6f9Dmitri Plotnikov } 1488b8264620f9362e089322d1e3a7cc5620900e6f9Dmitri Plotnikov 1491a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov public void setListener(Listener listener) { 1501a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov mListener = listener; 15108e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 15208e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 1534d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki private void addTab(TabState tabState, String text) { 1544d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki final Tab tab = mActionBar.newTab(); 1554d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki tab.setTag(tabState); 1564d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki tab.setText(text); 1574d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki tab.setTabListener(mTabListener); 1584d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mActionBar.addTab(tab); 1594d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 1604d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 1614d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki private class MyTabListener implements ActionBar.TabListener { 1624d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki /** 1634d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki * If true, it won't call {@link #setCurrentTab} in {@link #onTabSelected}. 1644d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki * This flag is used when we want to programmatically update the current tab without 1654d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki * {@link #onTabSelected} getting called. 1664d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki */ 1674d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki public boolean mIgnoreTabSelected; 1684d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 1694d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki @Override public void onTabReselected(Tab tab, FragmentTransaction ft) { } 1704d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki @Override public void onTabUnselected(Tab tab, FragmentTransaction ft) { } 1714d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 1724d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki @Override public void onTabSelected(Tab tab, FragmentTransaction ft) { 1734d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki if (!mIgnoreTabSelected) { 1744d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki setCurrentTab((TabState)tab.getTag()); 1754d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 1764d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 1774d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 1784d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 1794d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki /** 1804d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki * Change the current tab, and notify the listener. 1814d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki */ 1824d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki public void setCurrentTab(TabState tab) { 183250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki setCurrentTab(tab, true); 184250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki } 185250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki 186250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki /** 187250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki * Change the current tab 188250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki */ 189250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki public void setCurrentTab(TabState tab, boolean notifyListener) { 1904d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki if (tab == null) throw new NullPointerException(); 1914d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki if (tab == mCurrentTab) { 1924d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki return; 1934d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 1944d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mCurrentTab = tab; 1954d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 1964d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki int index = mCurrentTab.ordinal(); 1974d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki if ((mActionBar.getNavigationMode() == ActionBar.NAVIGATION_MODE_TABS) 1984d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki && (index != mActionBar.getSelectedNavigationIndex())) { 1994d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mActionBar.setSelectedNavigationItem(index); 2004d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 2014d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 202250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki if (notifyListener && mListener != null) mListener.onSelectedTabChanged(); 20335d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki saveLastTabPreference(mCurrentTab); 2044d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 2054d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 2064d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki public TabState getCurrentTab() { 2074d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki return mCurrentTab; 2084d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 2094d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 21015ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov public boolean isSearchMode() { 21115ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov return mSearchMode; 21208e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 21308e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 21415ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov public void setSearchMode(boolean flag) { 21515ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov if (mSearchMode != flag) { 21615ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov mSearchMode = flag; 217bee8d3bd37edf44c535c0cc401e8d03f095b8328Dmitri Plotnikov update(); 218f3f933a798c9cfffb1c1c0cc770187b5b0b763ebKatherine Kuan if (mSearchView == null) { 219f3f933a798c9cfffb1c1c0cc770187b5b0b763ebKatherine Kuan return; 220f3f933a798c9cfffb1c1c0cc770187b5b0b763ebKatherine Kuan } 2210b51a8d4539cc516dc2c02948335347d1df8769bDmitri Plotnikov if (mSearchMode) { 2224ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki setFocusOnSearchView(); 223ab4d29f6a2edbfae008dbaff35f25baad73aa0dfDmitri Plotnikov } else { 224ab4d29f6a2edbfae008dbaff35f25baad73aa0dfDmitri Plotnikov mSearchView.setQuery(null, false); 2250b51a8d4539cc516dc2c02948335347d1df8769bDmitri Plotnikov } 22608e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 22708e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 22808e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 22908e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov public String getQueryString() { 23008e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov return mQueryString; 23108e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 23208e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 23308e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov public void setQueryString(String query) { 23408e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov mQueryString = query; 235f3f933a798c9cfffb1c1c0cc770187b5b0b763ebKatherine Kuan if (mSearchView != null) { 236f3f933a798c9cfffb1c1c0cc770187b5b0b763ebKatherine Kuan mSearchView.setQuery(query, false); 237f3f933a798c9cfffb1c1c0cc770187b5b0b763ebKatherine Kuan } 23808e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 23908e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 240e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki /** @return true if the "UP" icon is showing. */ 241e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki public boolean isUpShowing() { 242e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki return mSearchMode; // Only shown on the search mode. 243e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki } 244e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki 245e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki private void updateDisplayOptions() { 246e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki // All the flags we may change in this method. 247e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki final int MASK = ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_HOME 248d84ea5c8b0a8376e7cf80b0e15b65a72fd99d7a9Adam Powell | ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_CUSTOM; 249e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki 250e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki // The current flags set to the action bar. (only the ones that we may change here) 251e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki final int current = mActionBar.getDisplayOptions() & MASK; 252e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki 253e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki // Build the new flags... 254e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki int newFlags = 0; 255e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki newFlags |= ActionBar.DISPLAY_SHOW_TITLE; 256e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki if (mShowHomeIcon) { 257e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki newFlags |= ActionBar.DISPLAY_SHOW_HOME; 258e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki } 259e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki if (mSearchMode) { 260e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki newFlags |= ActionBar.DISPLAY_SHOW_HOME; 261e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki newFlags |= ActionBar.DISPLAY_HOME_AS_UP; 262e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki newFlags |= ActionBar.DISPLAY_SHOW_CUSTOM; 263e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki } 264d84ea5c8b0a8376e7cf80b0e15b65a72fd99d7a9Adam Powell mActionBar.setHomeButtonEnabled(mSearchMode); 265e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki 266e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki if (current != newFlags) { 267e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki // Pass the mask here to preserve other flags that we're not interested here. 268e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki mActionBar.setDisplayOptions(newFlags, MASK); 269e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki } 270e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki } 271e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki 2724d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki private void update() { 273b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan if (mSearchMode) { 2745267367e202dcb39ef0fe912a7be9209548ec436Makoto Onuki setFocusOnSearchView(); 275d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // Since we have the {@link SearchView} in a custom action bar, we must manually handle 276d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // expanding the {@link SearchView} when a search is initiated. 277d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan mSearchView.onActionViewExpanded(); 2784d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki if (mActionBar.getNavigationMode() != ActionBar.NAVIGATION_MODE_STANDARD) { 2794d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); 2804d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 281b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan if (mListener != null) { 282b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan mListener.onAction(Action.START_SEARCH_MODE); 283c9916d303cc48b4a4ff94ef9c56d7ca5da72c4deDmitri Plotnikov } 28415ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov } else { 2854d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki if (mActionBar.getNavigationMode() != ActionBar.NAVIGATION_MODE_TABS) { 2864d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // setNavigationMode will trigger onTabSelected() with the tab which was previously 2874d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // selected. 2884d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // The issue is that when we're first switching to the tab navigation mode after 2894d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // screen orientation changes, onTabSelected() will get called with the first tab 2904d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // (i.e. favorite), which would results in mCurrentTab getting set to FAVORITES and 2914d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // we'd lose restored tab. 2924d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // So let's just disable the callback here temporarily. We'll notify the listener 2934d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // after this anyway. 2944d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mTabListener.mIgnoreTabSelected = true; 2954d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); 2964d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mActionBar.setSelectedNavigationItem(mCurrentTab.ordinal()); 2974d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mTabListener.mIgnoreTabSelected = false; 2984d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 299b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan mActionBar.setTitle(null); 300d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // Since we have the {@link SearchView} in a custom action bar, we must manually handle 301d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // collapsing the {@link SearchView} when search mode is exited. 302d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan mSearchView.onActionViewCollapsed(); 303b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan if (mListener != null) { 304b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan mListener.onAction(Action.STOP_SEARCH_MODE); 3054d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mListener.onSelectedTabChanged(); 306bee8d3bd37edf44c535c0cc401e8d03f095b8328Dmitri Plotnikov } 30708e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 308e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki updateDisplayOptions(); 30908e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 31008e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 31108e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov @Override 312c5792ae6760291d435e91e0fdd28ece6b3311439Adam Powell public boolean onQueryTextChange(String queryString) { 313b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan // TODO: Clean up SearchView code because it keeps setting the SearchView query, 314b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan // invoking onQueryChanged, setting up the fragment again, invalidating the options menu, 315b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan // storing the SearchView again, and etc... unless we add in the early return statements. 316b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan if (queryString.equals(mQueryString)) { 317b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan return false; 318b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan } 31908e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov mQueryString = queryString; 320ab4d29f6a2edbfae008dbaff35f25baad73aa0dfDmitri Plotnikov if (!mSearchMode) { 321ab4d29f6a2edbfae008dbaff35f25baad73aa0dfDmitri Plotnikov if (!TextUtils.isEmpty(queryString)) { 322ab4d29f6a2edbfae008dbaff35f25baad73aa0dfDmitri Plotnikov setSearchMode(true); 3239806ed099226a19e92c29d9efa791cd187fd2a53Dmitri Plotnikov } 324ab4d29f6a2edbfae008dbaff35f25baad73aa0dfDmitri Plotnikov } else if (mListener != null) { 325b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan mListener.onAction(Action.CHANGE_SEARCH_QUERY); 3269806ed099226a19e92c29d9efa791cd187fd2a53Dmitri Plotnikov } 327ab4d29f6a2edbfae008dbaff35f25baad73aa0dfDmitri Plotnikov 3281a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov return true; 3291a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov } 3301a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov 3311a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov @Override 332c5792ae6760291d435e91e0fdd28ece6b3311439Adam Powell public boolean onQueryTextSubmit(String query) { 333d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // When the search is "committed" by the user, then hide the keyboard so the user can 334d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // more easily browse the list of results. 335d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan if (mSearchView != null) { 336d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan InputMethodManager imm = (InputMethodManager) mContext.getSystemService( 337d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan Context.INPUT_METHOD_SERVICE); 338d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan if (imm != null) { 339d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan imm.hideSoftInputFromWindow(mSearchView.getWindowToken(), 0); 340d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan } 341d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan mSearchView.clearFocus(); 342d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan } 3431a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov return true; 34408e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 34508e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 34608e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov @Override 3471a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov public boolean onClose() { 34815ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov setSearchMode(false); 34915ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov return false; 35008e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 35108e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 35215ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov public void onSaveInstanceState(Bundle outState) { 35315ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov outState.putBoolean(EXTRA_KEY_SEARCH_MODE, mSearchMode); 35415ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov outState.putString(EXTRA_KEY_QUERY, mQueryString); 3554d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki outState.putInt(EXTRA_KEY_SELECTED_TAB, mCurrentTab.ordinal()); 356fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov } 357fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov 3584ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki private void setFocusOnSearchView() { 3594ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki mSearchView.requestFocus(); 3604ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki mSearchView.setIconified(false); // Workaround for the "IME not popping up" issue. 3614ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki } 36235d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki 36335d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki private void saveLastTabPreference(TabState tab) { 36435d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki mPrefs.edit().putInt(PERSISTENT_LAST_TAB, tab.ordinal()).apply(); 36535d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki } 36635d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki 36735d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki private TabState loadLastTabPreference() { 36835d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki try { 36935d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki return TabState.fromInt(mPrefs.getInt(PERSISTENT_LAST_TAB, DEFAULT_TAB.ordinal())); 37035d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki } catch (IllegalArgumentException e) { 37135d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki // Preference is corrupt? 37235d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki return DEFAULT_TAB; 37335d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki } 37435d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki } 37508e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov} 376