ActionBarAdapter.java revision 5c3a0a1440625fab859aab420cb08bc0276358a0
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; 78aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan private boolean mShowTabsAsText; 79e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki 804d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki public enum TabState { 819d2a74ef7b1817ad0708e41151358f5899aeeb67Makoto Onuki GROUPS, 829d2a74ef7b1817ad0708e41151358f5899aeeb67Makoto Onuki ALL, 839d2a74ef7b1817ad0708e41151358f5899aeeb67Makoto Onuki FAVORITES; 841ee9df6facd2340416d6a33aeb739707643d2fc3Katherine Kuan 854d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki public static TabState fromInt(int value) { 869d2a74ef7b1817ad0708e41151358f5899aeeb67Makoto Onuki if (GROUPS.ordinal() == value) { 879d2a74ef7b1817ad0708e41151358f5899aeeb67Makoto Onuki return GROUPS; 889d2a74ef7b1817ad0708e41151358f5899aeeb67Makoto Onuki } 899d2a74ef7b1817ad0708e41151358f5899aeeb67Makoto Onuki if (ALL.ordinal() == value) { 909d2a74ef7b1817ad0708e41151358f5899aeeb67Makoto Onuki return ALL; 919d2a74ef7b1817ad0708e41151358f5899aeeb67Makoto Onuki } 929d2a74ef7b1817ad0708e41151358f5899aeeb67Makoto Onuki if (FAVORITES.ordinal() == value) { 939d2a74ef7b1817ad0708e41151358f5899aeeb67Makoto Onuki return FAVORITES; 944d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 954d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki throw new IllegalArgumentException("Invalid value: " + value); 964d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 974d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 984d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 9935d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki private static final TabState DEFAULT_TAB = TabState.ALL; 10035d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki private TabState mCurrentTab = DEFAULT_TAB; 1014d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 102aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan public ActionBarAdapter(Context context, Listener listener, ActionBar actionBar, 103aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan boolean isUsingTwoPanes) { 10408e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov mContext = context; 1051ee9df6facd2340416d6a33aeb739707643d2fc3Katherine Kuan mListener = listener; 1064d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mActionBar = actionBar; 10735d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki mPrefs = PreferenceManager.getDefaultSharedPreferences(mContext); 10808e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 109e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki mShowHomeIcon = mContext.getResources().getBoolean(R.bool.show_home_icon); 110e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki 111aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan // On wide screens, show the tabs as text (instead of icons) 112aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan mShowTabsAsText = isUsingTwoPanes; 113aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan 1144ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki // Set up search view. 1150bf7cd0124fa6f74fe3d64619740420ded86dcceKatherine Kuan View customSearchView = LayoutInflater.from(mActionBar.getThemedContext()).inflate( 1160bf7cd0124fa6f74fe3d64619740420ded86dcceKatherine Kuan R.layout.custom_action_bar, null); 1174ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki int searchViewWidth = mContext.getResources().getDimensionPixelSize( 1184ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki R.dimen.search_view_width); 1194ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki if (searchViewWidth == 0) { 1204ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki searchViewWidth = LayoutParams.MATCH_PARENT; 1211173ae29217fc83f254404f8a5fa10419ee83c93Dmitri Plotnikov } 1224ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki LayoutParams layoutParams = new LayoutParams(searchViewWidth, LayoutParams.WRAP_CONTENT); 1234ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki mSearchView = (SearchView) customSearchView.findViewById(R.id.search_view); 124d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // Since the {@link SearchView} in this app is "click-to-expand", set the below mode on the 125d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // {@link SearchView} so that the magnifying glass icon appears inside the editable text 126d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // field. (In the "click-to-expand" search pattern, the user must explicitly expand the 127d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // search field and already knows a search is being conducted, so the icon is redundant 128d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // and can go away once the user starts typing.) 129d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan mSearchView.setIconifiedByDefault(true); 1304ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki mSearchView.setQueryHint(mContext.getString(R.string.hint_findContacts)); 131c5792ae6760291d435e91e0fdd28ece6b3311439Adam Powell mSearchView.setOnQueryTextListener(this); 13215ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov mSearchView.setOnCloseListener(this); 13315ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov mSearchView.setQuery(mQueryString, false); 1344ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki mActionBar.setCustomView(customSearchView, layoutParams); 1354ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki 136e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki // Set up tabs 1371ff189a390ce5011f729637baf248646560c1bc6Daisuke Miyakawa addTab(TabState.GROUPS, R.drawable.ic_tab_groups, R.string.contactsGroupsLabel); 1381ff189a390ce5011f729637baf248646560c1bc6Daisuke Miyakawa addTab(TabState.ALL, R.drawable.ic_tab_all, R.string.contactsAllLabel); 1391ff189a390ce5011f729637baf248646560c1bc6Daisuke Miyakawa addTab(TabState.FAVORITES, R.drawable.ic_tab_starred, R.string.contactsFavoritesLabel); 1404d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 1414d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 1424d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki public void initialize(Bundle savedState, ContactsRequest request) { 1434d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki if (savedState == null) { 1444d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mSearchMode = request.isSearchMode(); 1454d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mQueryString = request.getQueryString(); 14635d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki mCurrentTab = loadLastTabPreference(); 1474d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } else { 1484d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mSearchMode = savedState.getBoolean(EXTRA_KEY_SEARCH_MODE); 1494d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mQueryString = savedState.getString(EXTRA_KEY_QUERY); 1504d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 1514d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // Just set to the field here. The listener will be notified by update(). 1524d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mCurrentTab = TabState.fromInt(savedState.getInt(EXTRA_KEY_SELECTED_TAB)); 1534d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 1544ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki update(); 1558b8264620f9362e089322d1e3a7cc5620900e6f9Dmitri Plotnikov } 1568b8264620f9362e089322d1e3a7cc5620900e6f9Dmitri Plotnikov 1571a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov public void setListener(Listener listener) { 1581a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov mListener = listener; 15908e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 16008e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 161aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan private void addTab(TabState tabState, int icon, int description) { 1624d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki final Tab tab = mActionBar.newTab(); 1634d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki tab.setTag(tabState); 1644d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki tab.setTabListener(mTabListener); 165aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan if (mShowTabsAsText) { 166aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan tab.setText(description); 167aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan } else { 168aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan tab.setIcon(icon); 169aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan tab.setContentDescription(description); 170aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan } 1714d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mActionBar.addTab(tab); 1724d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 1734d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 1744d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki private class MyTabListener implements ActionBar.TabListener { 1754d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki /** 1764d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki * If true, it won't call {@link #setCurrentTab} in {@link #onTabSelected}. 1774d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki * This flag is used when we want to programmatically update the current tab without 1784d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki * {@link #onTabSelected} getting called. 1794d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki */ 1804d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki public boolean mIgnoreTabSelected; 1814d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 1824d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki @Override public void onTabReselected(Tab tab, FragmentTransaction ft) { } 1834d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki @Override public void onTabUnselected(Tab tab, FragmentTransaction ft) { } 1844d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 1854d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki @Override public void onTabSelected(Tab tab, FragmentTransaction ft) { 1864d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki if (!mIgnoreTabSelected) { 1874d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki setCurrentTab((TabState)tab.getTag()); 1884d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 1894d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 1904d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 1914d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 1924d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki /** 1934d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki * Change the current tab, and notify the listener. 1944d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki */ 1954d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki public void setCurrentTab(TabState tab) { 196250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki setCurrentTab(tab, true); 197250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki } 198250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki 199250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki /** 200250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki * Change the current tab 201250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki */ 202250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki public void setCurrentTab(TabState tab, boolean notifyListener) { 2034d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki if (tab == null) throw new NullPointerException(); 2044d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki if (tab == mCurrentTab) { 2054d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki return; 2064d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 2074d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mCurrentTab = tab; 2084d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 2094d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki int index = mCurrentTab.ordinal(); 2104d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki if ((mActionBar.getNavigationMode() == ActionBar.NAVIGATION_MODE_TABS) 2114d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki && (index != mActionBar.getSelectedNavigationIndex())) { 2124d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mActionBar.setSelectedNavigationItem(index); 2134d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 2144d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 215250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki if (notifyListener && mListener != null) mListener.onSelectedTabChanged(); 21635d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki saveLastTabPreference(mCurrentTab); 2174d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 2184d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 2194d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki public TabState getCurrentTab() { 2204d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki return mCurrentTab; 2214d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 2224d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 2231db00f68b34f6cf7e9d19fedb559cf12f8c05e9cMakoto Onuki /** 2241db00f68b34f6cf7e9d19fedb559cf12f8c05e9cMakoto Onuki * @return Whether in search mode, i.e. if the search view is visible/expanded. 2251db00f68b34f6cf7e9d19fedb559cf12f8c05e9cMakoto Onuki * 2261db00f68b34f6cf7e9d19fedb559cf12f8c05e9cMakoto Onuki * Note even if the action bar is in search mode, if the query is empty, the search fragment 2271db00f68b34f6cf7e9d19fedb559cf12f8c05e9cMakoto Onuki * will not be in search mode. 2281db00f68b34f6cf7e9d19fedb559cf12f8c05e9cMakoto Onuki */ 22915ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov public boolean isSearchMode() { 23015ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov return mSearchMode; 23108e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 23208e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 23315ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov public void setSearchMode(boolean flag) { 23415ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov if (mSearchMode != flag) { 23515ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov mSearchMode = flag; 236bee8d3bd37edf44c535c0cc401e8d03f095b8328Dmitri Plotnikov update(); 237f3f933a798c9cfffb1c1c0cc770187b5b0b763ebKatherine Kuan if (mSearchView == null) { 238f3f933a798c9cfffb1c1c0cc770187b5b0b763ebKatherine Kuan return; 239f3f933a798c9cfffb1c1c0cc770187b5b0b763ebKatherine Kuan } 2400b51a8d4539cc516dc2c02948335347d1df8769bDmitri Plotnikov if (mSearchMode) { 2414ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki setFocusOnSearchView(); 242ab4d29f6a2edbfae008dbaff35f25baad73aa0dfDmitri Plotnikov } else { 243ab4d29f6a2edbfae008dbaff35f25baad73aa0dfDmitri Plotnikov mSearchView.setQuery(null, false); 2440b51a8d4539cc516dc2c02948335347d1df8769bDmitri Plotnikov } 24508e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 24608e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 24708e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 24808e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov public String getQueryString() { 2491db00f68b34f6cf7e9d19fedb559cf12f8c05e9cMakoto Onuki return mSearchMode ? mQueryString : null; 25008e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 25108e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 25208e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov public void setQueryString(String query) { 25308e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov mQueryString = query; 254f3f933a798c9cfffb1c1c0cc770187b5b0b763ebKatherine Kuan if (mSearchView != null) { 255f3f933a798c9cfffb1c1c0cc770187b5b0b763ebKatherine Kuan mSearchView.setQuery(query, false); 256f3f933a798c9cfffb1c1c0cc770187b5b0b763ebKatherine Kuan } 25708e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 25808e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 259e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki /** @return true if the "UP" icon is showing. */ 260e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki public boolean isUpShowing() { 261e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki return mSearchMode; // Only shown on the search mode. 262e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki } 263e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki 264e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki private void updateDisplayOptions() { 265e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki // All the flags we may change in this method. 266e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki final int MASK = ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_HOME 267d84ea5c8b0a8376e7cf80b0e15b65a72fd99d7a9Adam Powell | ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_CUSTOM; 268e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki 269e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki // The current flags set to the action bar. (only the ones that we may change here) 270e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki final int current = mActionBar.getDisplayOptions() & MASK; 271e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki 272e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki // Build the new flags... 273e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki int newFlags = 0; 274e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki newFlags |= ActionBar.DISPLAY_SHOW_TITLE; 275e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki if (mShowHomeIcon) { 276e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki newFlags |= ActionBar.DISPLAY_SHOW_HOME; 277e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki } 278e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki if (mSearchMode) { 279e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki newFlags |= ActionBar.DISPLAY_SHOW_HOME; 280e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki newFlags |= ActionBar.DISPLAY_HOME_AS_UP; 281e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki newFlags |= ActionBar.DISPLAY_SHOW_CUSTOM; 282e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki } 283d84ea5c8b0a8376e7cf80b0e15b65a72fd99d7a9Adam Powell mActionBar.setHomeButtonEnabled(mSearchMode); 284e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki 285e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki if (current != newFlags) { 286e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki // Pass the mask here to preserve other flags that we're not interested here. 287e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki mActionBar.setDisplayOptions(newFlags, MASK); 288e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki } 289e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki } 290e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki 2914d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki private void update() { 292b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan if (mSearchMode) { 2935267367e202dcb39ef0fe912a7be9209548ec436Makoto Onuki setFocusOnSearchView(); 294d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // Since we have the {@link SearchView} in a custom action bar, we must manually handle 295d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // expanding the {@link SearchView} when a search is initiated. 296d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan mSearchView.onActionViewExpanded(); 2974d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki if (mActionBar.getNavigationMode() != ActionBar.NAVIGATION_MODE_STANDARD) { 2984d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); 2994d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 300b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan if (mListener != null) { 301b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan mListener.onAction(Action.START_SEARCH_MODE); 302c9916d303cc48b4a4ff94ef9c56d7ca5da72c4deDmitri Plotnikov } 30315ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov } else { 3044d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki if (mActionBar.getNavigationMode() != ActionBar.NAVIGATION_MODE_TABS) { 3054d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // setNavigationMode will trigger onTabSelected() with the tab which was previously 3064d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // selected. 3074d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // The issue is that when we're first switching to the tab navigation mode after 3084d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // screen orientation changes, onTabSelected() will get called with the first tab 3094d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // (i.e. favorite), which would results in mCurrentTab getting set to FAVORITES and 3104d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // we'd lose restored tab. 3114d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // So let's just disable the callback here temporarily. We'll notify the listener 3124d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // after this anyway. 3134d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mTabListener.mIgnoreTabSelected = true; 3144d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); 3154d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mActionBar.setSelectedNavigationItem(mCurrentTab.ordinal()); 3164d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mTabListener.mIgnoreTabSelected = false; 3174d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 318b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan mActionBar.setTitle(null); 319d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // Since we have the {@link SearchView} in a custom action bar, we must manually handle 320d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // collapsing the {@link SearchView} when search mode is exited. 321d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan mSearchView.onActionViewCollapsed(); 322b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan if (mListener != null) { 323b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan mListener.onAction(Action.STOP_SEARCH_MODE); 3244d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mListener.onSelectedTabChanged(); 325bee8d3bd37edf44c535c0cc401e8d03f095b8328Dmitri Plotnikov } 32608e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 327e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki updateDisplayOptions(); 32808e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 32908e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 33008e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov @Override 331c5792ae6760291d435e91e0fdd28ece6b3311439Adam Powell public boolean onQueryTextChange(String queryString) { 332b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan // TODO: Clean up SearchView code because it keeps setting the SearchView query, 333b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan // invoking onQueryChanged, setting up the fragment again, invalidating the options menu, 334b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan // storing the SearchView again, and etc... unless we add in the early return statements. 335b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan if (queryString.equals(mQueryString)) { 336b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan return false; 337b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan } 33808e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov mQueryString = queryString; 339ab4d29f6a2edbfae008dbaff35f25baad73aa0dfDmitri Plotnikov if (!mSearchMode) { 340ab4d29f6a2edbfae008dbaff35f25baad73aa0dfDmitri Plotnikov if (!TextUtils.isEmpty(queryString)) { 341ab4d29f6a2edbfae008dbaff35f25baad73aa0dfDmitri Plotnikov setSearchMode(true); 3429806ed099226a19e92c29d9efa791cd187fd2a53Dmitri Plotnikov } 343ab4d29f6a2edbfae008dbaff35f25baad73aa0dfDmitri Plotnikov } else if (mListener != null) { 344b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan mListener.onAction(Action.CHANGE_SEARCH_QUERY); 3459806ed099226a19e92c29d9efa791cd187fd2a53Dmitri Plotnikov } 346ab4d29f6a2edbfae008dbaff35f25baad73aa0dfDmitri Plotnikov 3471a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov return true; 3481a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov } 3491a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov 3501a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov @Override 351c5792ae6760291d435e91e0fdd28ece6b3311439Adam Powell public boolean onQueryTextSubmit(String query) { 352d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // When the search is "committed" by the user, then hide the keyboard so the user can 353d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // more easily browse the list of results. 354d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan if (mSearchView != null) { 355d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan InputMethodManager imm = (InputMethodManager) mContext.getSystemService( 356d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan Context.INPUT_METHOD_SERVICE); 357d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan if (imm != null) { 358d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan imm.hideSoftInputFromWindow(mSearchView.getWindowToken(), 0); 359d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan } 360d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan mSearchView.clearFocus(); 361d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan } 3621a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov return true; 36308e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 36408e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 36508e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov @Override 3661a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov public boolean onClose() { 36715ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov setSearchMode(false); 36815ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov return false; 36908e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 37008e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 37115ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov public void onSaveInstanceState(Bundle outState) { 37215ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov outState.putBoolean(EXTRA_KEY_SEARCH_MODE, mSearchMode); 37315ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov outState.putString(EXTRA_KEY_QUERY, mQueryString); 3744d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki outState.putInt(EXTRA_KEY_SELECTED_TAB, mCurrentTab.ordinal()); 375fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov } 376fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov 3775c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan /** 3785c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan * Clears the focus from the {@link SearchView} if we are in search mode. 3795c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan * This will suppress the IME if it is visible. 3805c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan */ 3815c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan public void clearFocusOnSearchView() { 3825c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan if (isSearchMode()) { 3835c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan if (mSearchView != null) { 3845c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan mSearchView.clearFocus(); 3855c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan } 3865c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan } 3875c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan } 3885c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan 3894ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki private void setFocusOnSearchView() { 3904ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki mSearchView.requestFocus(); 3914ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki mSearchView.setIconified(false); // Workaround for the "IME not popping up" issue. 3924ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki } 39335d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki 39435d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki private void saveLastTabPreference(TabState tab) { 39535d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki mPrefs.edit().putInt(PERSISTENT_LAST_TAB, tab.ordinal()).apply(); 39635d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki } 39735d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki 39835d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki private TabState loadLastTabPreference() { 39935d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki try { 40035d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki return TabState.fromInt(mPrefs.getInt(PERSISTENT_LAST_TAB, DEFAULT_TAB.ordinal())); 40135d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki } catch (IllegalArgumentException e) { 40235d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki // Preference is corrupt? 40335d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki return DEFAULT_TAB; 40435d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki } 40535d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki } 40608e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov} 407