ActionBarAdapter.java revision 52aa32db432085ab2cd4864bd1edd1ef843da867
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 8052aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann public interface TabState { 8152aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann public static int GROUPS = 0; 8252aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann public static int ALL = 1; 8352aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann public static int FAVORITES = 2; 8452aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann 8552aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann public static int COUNT = 3; 8652aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann public static int DEFAULT = ALL; 874d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 884d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 8952aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann private int mCurrentTab = TabState.DEFAULT; 904d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 91aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan public ActionBarAdapter(Context context, Listener listener, ActionBar actionBar, 92aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan boolean isUsingTwoPanes) { 9308e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov mContext = context; 941ee9df6facd2340416d6a33aeb739707643d2fc3Katherine Kuan mListener = listener; 954d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mActionBar = actionBar; 9635d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki mPrefs = PreferenceManager.getDefaultSharedPreferences(mContext); 9708e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 98e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki mShowHomeIcon = mContext.getResources().getBoolean(R.bool.show_home_icon); 99e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki 100aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan // On wide screens, show the tabs as text (instead of icons) 101aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan mShowTabsAsText = isUsingTwoPanes; 102aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan 1034ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki // Set up search view. 1040bf7cd0124fa6f74fe3d64619740420ded86dcceKatherine Kuan View customSearchView = LayoutInflater.from(mActionBar.getThemedContext()).inflate( 1050bf7cd0124fa6f74fe3d64619740420ded86dcceKatherine Kuan R.layout.custom_action_bar, null); 1064ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki int searchViewWidth = mContext.getResources().getDimensionPixelSize( 1074ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki R.dimen.search_view_width); 1084ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki if (searchViewWidth == 0) { 1094ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki searchViewWidth = LayoutParams.MATCH_PARENT; 1101173ae29217fc83f254404f8a5fa10419ee83c93Dmitri Plotnikov } 1114ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki LayoutParams layoutParams = new LayoutParams(searchViewWidth, LayoutParams.WRAP_CONTENT); 1124ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki mSearchView = (SearchView) customSearchView.findViewById(R.id.search_view); 113d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // Since the {@link SearchView} in this app is "click-to-expand", set the below mode on the 114d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // {@link SearchView} so that the magnifying glass icon appears inside the editable text 115d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // field. (In the "click-to-expand" search pattern, the user must explicitly expand the 116d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // search field and already knows a search is being conducted, so the icon is redundant 117d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // and can go away once the user starts typing.) 118d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan mSearchView.setIconifiedByDefault(true); 1194ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki mSearchView.setQueryHint(mContext.getString(R.string.hint_findContacts)); 120c5792ae6760291d435e91e0fdd28ece6b3311439Adam Powell mSearchView.setOnQueryTextListener(this); 12115ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov mSearchView.setOnCloseListener(this); 12215ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov mSearchView.setQuery(mQueryString, false); 1234ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki mActionBar.setCustomView(customSearchView, layoutParams); 1244ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki 125e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki // Set up tabs 1261ff189a390ce5011f729637baf248646560c1bc6Daisuke Miyakawa addTab(TabState.GROUPS, R.drawable.ic_tab_groups, R.string.contactsGroupsLabel); 1271ff189a390ce5011f729637baf248646560c1bc6Daisuke Miyakawa addTab(TabState.ALL, R.drawable.ic_tab_all, R.string.contactsAllLabel); 1281ff189a390ce5011f729637baf248646560c1bc6Daisuke Miyakawa addTab(TabState.FAVORITES, R.drawable.ic_tab_starred, R.string.contactsFavoritesLabel); 1294d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 1304d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 1314d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki public void initialize(Bundle savedState, ContactsRequest request) { 1324d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki if (savedState == null) { 1334d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mSearchMode = request.isSearchMode(); 1344d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mQueryString = request.getQueryString(); 13535d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki mCurrentTab = loadLastTabPreference(); 1364d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } else { 1374d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mSearchMode = savedState.getBoolean(EXTRA_KEY_SEARCH_MODE); 1384d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mQueryString = savedState.getString(EXTRA_KEY_QUERY); 1394d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 1404d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // Just set to the field here. The listener will be notified by update(). 14152aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann mCurrentTab = savedState.getInt(EXTRA_KEY_SELECTED_TAB); 1424d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 1433c877e33cb7fecc7a63af1cf3c25061d53811bf6Katherine Kuan // Show tabs or the expanded {@link SearchView}, depending on whether or not we are in 1443c877e33cb7fecc7a63af1cf3c25061d53811bf6Katherine Kuan // search mode. 1454ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki update(); 1463c877e33cb7fecc7a63af1cf3c25061d53811bf6Katherine Kuan // Expanding the {@link SearchView} clears the query, so set the query from the 1473c877e33cb7fecc7a63af1cf3c25061d53811bf6Katherine Kuan // {@link ContactsRequest} after it has been expanded, if applicable. 1483c877e33cb7fecc7a63af1cf3c25061d53811bf6Katherine Kuan if (mSearchMode && !TextUtils.isEmpty(mQueryString)) { 1493c877e33cb7fecc7a63af1cf3c25061d53811bf6Katherine Kuan setQueryString(mQueryString); 1503c877e33cb7fecc7a63af1cf3c25061d53811bf6Katherine Kuan } 1518b8264620f9362e089322d1e3a7cc5620900e6f9Dmitri Plotnikov } 1528b8264620f9362e089322d1e3a7cc5620900e6f9Dmitri Plotnikov 1531a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov public void setListener(Listener listener) { 1541a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov mListener = listener; 15508e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 15608e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 15752aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann private void addTab(int expectedTabIndex, int icon, int description) { 1584d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki final Tab tab = mActionBar.newTab(); 1594d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki tab.setTabListener(mTabListener); 160aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan if (mShowTabsAsText) { 161aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan tab.setText(description); 162aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan } else { 163aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan tab.setIcon(icon); 164aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan tab.setContentDescription(description); 165aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan } 1664d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mActionBar.addTab(tab); 16752aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann if (expectedTabIndex != tab.getPosition()) { 16852aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann throw new IllegalStateException("Tabs must be created in the right order"); 16952aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann } 1704d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 1714d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 1724d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki private class MyTabListener implements ActionBar.TabListener { 1734d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki /** 1744d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki * If true, it won't call {@link #setCurrentTab} in {@link #onTabSelected}. 1754d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki * This flag is used when we want to programmatically update the current tab without 1764d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki * {@link #onTabSelected} getting called. 1774d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki */ 1784d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki public boolean mIgnoreTabSelected; 1794d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 1804d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki @Override public void onTabReselected(Tab tab, FragmentTransaction ft) { } 1814d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki @Override public void onTabUnselected(Tab tab, FragmentTransaction ft) { } 1824d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 1834d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki @Override public void onTabSelected(Tab tab, FragmentTransaction ft) { 1844d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki if (!mIgnoreTabSelected) { 18552aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann setCurrentTab(tab.getPosition()); 1864d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 1874d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 1884d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 1894d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 1904d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki /** 1914d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki * Change the current tab, and notify the listener. 1924d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki */ 19352aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann public void setCurrentTab(int tab) { 194250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki setCurrentTab(tab, true); 195250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki } 196250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki 197250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki /** 198250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki * Change the current tab 199250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki */ 20052aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann public void setCurrentTab(int tab, boolean notifyListener) { 2014d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki if (tab == mCurrentTab) { 2024d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki return; 2034d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 2044d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mCurrentTab = tab; 2054d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 2064d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki if ((mActionBar.getNavigationMode() == ActionBar.NAVIGATION_MODE_TABS) 20752aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann && (mCurrentTab != mActionBar.getSelectedNavigationIndex())) { 20852aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann mActionBar.setSelectedNavigationItem(mCurrentTab); 2094d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 2104d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 211250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki if (notifyListener && mListener != null) mListener.onSelectedTabChanged(); 21235d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki saveLastTabPreference(mCurrentTab); 2134d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 2144d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 21552aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann public int getCurrentTab() { 2164d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki return mCurrentTab; 2174d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 2184d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 2191db00f68b34f6cf7e9d19fedb559cf12f8c05e9cMakoto Onuki /** 2201db00f68b34f6cf7e9d19fedb559cf12f8c05e9cMakoto Onuki * @return Whether in search mode, i.e. if the search view is visible/expanded. 2211db00f68b34f6cf7e9d19fedb559cf12f8c05e9cMakoto Onuki * 2221db00f68b34f6cf7e9d19fedb559cf12f8c05e9cMakoto Onuki * Note even if the action bar is in search mode, if the query is empty, the search fragment 2231db00f68b34f6cf7e9d19fedb559cf12f8c05e9cMakoto Onuki * will not be in search mode. 2241db00f68b34f6cf7e9d19fedb559cf12f8c05e9cMakoto Onuki */ 22515ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov public boolean isSearchMode() { 22615ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov return mSearchMode; 22708e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 22808e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 22915ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov public void setSearchMode(boolean flag) { 23015ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov if (mSearchMode != flag) { 23115ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov mSearchMode = flag; 232bee8d3bd37edf44c535c0cc401e8d03f095b8328Dmitri Plotnikov update(); 233f3f933a798c9cfffb1c1c0cc770187b5b0b763ebKatherine Kuan if (mSearchView == null) { 234f3f933a798c9cfffb1c1c0cc770187b5b0b763ebKatherine Kuan return; 235f3f933a798c9cfffb1c1c0cc770187b5b0b763ebKatherine Kuan } 2360b51a8d4539cc516dc2c02948335347d1df8769bDmitri Plotnikov if (mSearchMode) { 2374ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki setFocusOnSearchView(); 238ab4d29f6a2edbfae008dbaff35f25baad73aa0dfDmitri Plotnikov } else { 239ab4d29f6a2edbfae008dbaff35f25baad73aa0dfDmitri Plotnikov mSearchView.setQuery(null, false); 2400b51a8d4539cc516dc2c02948335347d1df8769bDmitri Plotnikov } 24108e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 24208e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 24308e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 24408e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov public String getQueryString() { 2451db00f68b34f6cf7e9d19fedb559cf12f8c05e9cMakoto Onuki return mSearchMode ? mQueryString : null; 24608e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 24708e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 24808e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov public void setQueryString(String query) { 24908e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov mQueryString = query; 250f3f933a798c9cfffb1c1c0cc770187b5b0b763ebKatherine Kuan if (mSearchView != null) { 251f3f933a798c9cfffb1c1c0cc770187b5b0b763ebKatherine Kuan mSearchView.setQuery(query, false); 252f3f933a798c9cfffb1c1c0cc770187b5b0b763ebKatherine Kuan } 25308e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 25408e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 255e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki /** @return true if the "UP" icon is showing. */ 256e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki public boolean isUpShowing() { 257e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki return mSearchMode; // Only shown on the search mode. 258e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki } 259e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki 260e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki private void updateDisplayOptions() { 261e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki // All the flags we may change in this method. 262e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki final int MASK = ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_HOME 263d84ea5c8b0a8376e7cf80b0e15b65a72fd99d7a9Adam Powell | ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_CUSTOM; 264e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki 265e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki // The current flags set to the action bar. (only the ones that we may change here) 266e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki final int current = mActionBar.getDisplayOptions() & MASK; 267e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki 268e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki // Build the new flags... 269e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki int newFlags = 0; 270e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki newFlags |= ActionBar.DISPLAY_SHOW_TITLE; 271e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki if (mShowHomeIcon) { 272e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki newFlags |= ActionBar.DISPLAY_SHOW_HOME; 273e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki } 274e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki if (mSearchMode) { 275e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki newFlags |= ActionBar.DISPLAY_SHOW_HOME; 276e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki newFlags |= ActionBar.DISPLAY_HOME_AS_UP; 277e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki newFlags |= ActionBar.DISPLAY_SHOW_CUSTOM; 278e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki } 279d84ea5c8b0a8376e7cf80b0e15b65a72fd99d7a9Adam Powell mActionBar.setHomeButtonEnabled(mSearchMode); 280e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki 281e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki if (current != newFlags) { 282e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki // Pass the mask here to preserve other flags that we're not interested here. 283e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki mActionBar.setDisplayOptions(newFlags, MASK); 284e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki } 285e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki } 286e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki 2874d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki private void update() { 288b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan if (mSearchMode) { 2895267367e202dcb39ef0fe912a7be9209548ec436Makoto Onuki setFocusOnSearchView(); 290d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // Since we have the {@link SearchView} in a custom action bar, we must manually handle 2913c877e33cb7fecc7a63af1cf3c25061d53811bf6Katherine Kuan // expanding the {@link SearchView} when a search is initiated. Note that a side effect 2923c877e33cb7fecc7a63af1cf3c25061d53811bf6Katherine Kuan // of this method is that the {@link SearchView} query text is set to empty string. 293d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan mSearchView.onActionViewExpanded(); 2944d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki if (mActionBar.getNavigationMode() != ActionBar.NAVIGATION_MODE_STANDARD) { 2954d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); 2964d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 297b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan if (mListener != null) { 298b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan mListener.onAction(Action.START_SEARCH_MODE); 299c9916d303cc48b4a4ff94ef9c56d7ca5da72c4deDmitri Plotnikov } 30015ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov } else { 3014d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki if (mActionBar.getNavigationMode() != ActionBar.NAVIGATION_MODE_TABS) { 3024d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // setNavigationMode will trigger onTabSelected() with the tab which was previously 3034d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // selected. 3044d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // The issue is that when we're first switching to the tab navigation mode after 3054d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // screen orientation changes, onTabSelected() will get called with the first tab 3064d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // (i.e. favorite), which would results in mCurrentTab getting set to FAVORITES and 3074d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // we'd lose restored tab. 3084d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // So let's just disable the callback here temporarily. We'll notify the listener 3094d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // after this anyway. 3104d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mTabListener.mIgnoreTabSelected = true; 3114d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); 31252aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann mActionBar.setSelectedNavigationItem(mCurrentTab); 3134d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mTabListener.mIgnoreTabSelected = false; 3144d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 315b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan mActionBar.setTitle(null); 316d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // Since we have the {@link SearchView} in a custom action bar, we must manually handle 317d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // collapsing the {@link SearchView} when search mode is exited. 318d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan mSearchView.onActionViewCollapsed(); 319b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan if (mListener != null) { 320b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan mListener.onAction(Action.STOP_SEARCH_MODE); 3214d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mListener.onSelectedTabChanged(); 322bee8d3bd37edf44c535c0cc401e8d03f095b8328Dmitri Plotnikov } 32308e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 324e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki updateDisplayOptions(); 32508e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 32608e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 32708e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov @Override 328c5792ae6760291d435e91e0fdd28ece6b3311439Adam Powell public boolean onQueryTextChange(String queryString) { 329b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan // TODO: Clean up SearchView code because it keeps setting the SearchView query, 330b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan // invoking onQueryChanged, setting up the fragment again, invalidating the options menu, 331b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan // storing the SearchView again, and etc... unless we add in the early return statements. 332b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan if (queryString.equals(mQueryString)) { 333b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan return false; 334b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan } 33508e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov mQueryString = queryString; 336ab4d29f6a2edbfae008dbaff35f25baad73aa0dfDmitri Plotnikov if (!mSearchMode) { 337ab4d29f6a2edbfae008dbaff35f25baad73aa0dfDmitri Plotnikov if (!TextUtils.isEmpty(queryString)) { 338ab4d29f6a2edbfae008dbaff35f25baad73aa0dfDmitri Plotnikov setSearchMode(true); 3399806ed099226a19e92c29d9efa791cd187fd2a53Dmitri Plotnikov } 340ab4d29f6a2edbfae008dbaff35f25baad73aa0dfDmitri Plotnikov } else if (mListener != null) { 341b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan mListener.onAction(Action.CHANGE_SEARCH_QUERY); 3429806ed099226a19e92c29d9efa791cd187fd2a53Dmitri Plotnikov } 343ab4d29f6a2edbfae008dbaff35f25baad73aa0dfDmitri Plotnikov 3441a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov return true; 3451a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov } 3461a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov 3471a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov @Override 348c5792ae6760291d435e91e0fdd28ece6b3311439Adam Powell public boolean onQueryTextSubmit(String query) { 349d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // When the search is "committed" by the user, then hide the keyboard so the user can 350d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // more easily browse the list of results. 351d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan if (mSearchView != null) { 352d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan InputMethodManager imm = (InputMethodManager) mContext.getSystemService( 353d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan Context.INPUT_METHOD_SERVICE); 354d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan if (imm != null) { 355d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan imm.hideSoftInputFromWindow(mSearchView.getWindowToken(), 0); 356d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan } 357d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan mSearchView.clearFocus(); 358d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan } 3591a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov return true; 36008e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 36108e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 36208e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov @Override 3631a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov public boolean onClose() { 36415ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov setSearchMode(false); 36515ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov return false; 36608e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 36708e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 36815ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov public void onSaveInstanceState(Bundle outState) { 36915ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov outState.putBoolean(EXTRA_KEY_SEARCH_MODE, mSearchMode); 37015ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov outState.putString(EXTRA_KEY_QUERY, mQueryString); 37152aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann outState.putInt(EXTRA_KEY_SELECTED_TAB, mCurrentTab); 372fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov } 373fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov 3745c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan /** 3755c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan * Clears the focus from the {@link SearchView} if we are in search mode. 3765c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan * This will suppress the IME if it is visible. 3775c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan */ 3785c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan public void clearFocusOnSearchView() { 3795c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan if (isSearchMode()) { 3805c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan if (mSearchView != null) { 3815c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan mSearchView.clearFocus(); 3825c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan } 3835c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan } 3845c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan } 3855c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan 3864ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki private void setFocusOnSearchView() { 3874ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki mSearchView.requestFocus(); 3884ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki mSearchView.setIconified(false); // Workaround for the "IME not popping up" issue. 3894ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki } 39035d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki 39152aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann private void saveLastTabPreference(int tab) { 39252aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann mPrefs.edit().putInt(PERSISTENT_LAST_TAB, tab).apply(); 39335d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki } 39435d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki 39552aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann private int loadLastTabPreference() { 39635d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki try { 39752aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann return mPrefs.getInt(PERSISTENT_LAST_TAB, TabState.DEFAULT); 39835d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki } catch (IllegalArgumentException e) { 39935d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki // Preference is corrupt? 40052aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann return TabState.DEFAULT; 40135d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki } 40235d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki } 40308e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov} 404