ActionBarAdapter.java revision 311882ab2fc1e9c8264f5892a6d50722b834e348
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 { 45311882ab2fc1e9c8264f5892a6d50722b834e348Daniel Lehmann public abstract class Action { 46311882ab2fc1e9c8264f5892a6d50722b834e348Daniel Lehmann public static final int CHANGE_SEARCH_QUERY = 0; 47311882ab2fc1e9c8264f5892a6d50722b834e348Daniel Lehmann public static final int START_SEARCH_MODE = 1; 48311882ab2fc1e9c8264f5892a6d50722b834e348Daniel Lehmann public static final int STOP_SEARCH_MODE = 2; 49b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan } 50b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan 51311882ab2fc1e9c8264f5892a6d50722b834e348Daniel Lehmann void onAction(int action); 524d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 534d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki /** 544d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki * Called when the user selects a tab. The new tab can be obtained using 554d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki * {@link #getCurrentTab}. 564d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki */ 574d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki void onSelectedTabChanged(); 5808e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 5908e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 6015ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov private static final String EXTRA_KEY_SEARCH_MODE = "navBar.searchMode"; 6108e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov private static final String EXTRA_KEY_QUERY = "navBar.query"; 6259dc2751d64f60f743b8a9e78186aa5b79dcaf83Isaac Katzenelson private static final String EXTRA_KEY_SELECTED_TAB = "navBar.selectedTab"; 6308e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 6435d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki private static final String PERSISTENT_LAST_TAB = "actionBarAdapter.lastTab"; 6535d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki 6615ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov private boolean mSearchMode; 6708e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov private String mQueryString; 6815ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov 6915ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov private SearchView mSearchView; 7092a6fdf1b07b8f1447560174e24455fd46aee7cdDmitri Plotnikov 7108e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov private final Context mContext; 7235d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki private final SharedPreferences mPrefs; 7308e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 7408e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov private Listener mListener; 7508e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 764d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki private final ActionBar mActionBar; 774d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki private final MyTabListener mTabListener = new MyTabListener(); 788b8264620f9362e089322d1e3a7cc5620900e6f9Dmitri Plotnikov 79e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki private boolean mShowHomeIcon; 80aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan private boolean mShowTabsAsText; 81e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki 8252aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann public interface TabState { 8352aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann public static int GROUPS = 0; 8452aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann public static int ALL = 1; 8552aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann public static int FAVORITES = 2; 8652aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann 8752aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann public static int COUNT = 3; 8852aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann public static int DEFAULT = ALL; 894d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 904d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 9152aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann private int mCurrentTab = TabState.DEFAULT; 924d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 93aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan public ActionBarAdapter(Context context, Listener listener, ActionBar actionBar, 94aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan boolean isUsingTwoPanes) { 9508e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov mContext = context; 961ee9df6facd2340416d6a33aeb739707643d2fc3Katherine Kuan mListener = listener; 974d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mActionBar = actionBar; 9835d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki mPrefs = PreferenceManager.getDefaultSharedPreferences(mContext); 9908e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 100e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki mShowHomeIcon = mContext.getResources().getBoolean(R.bool.show_home_icon); 101e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki 102aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan // On wide screens, show the tabs as text (instead of icons) 103aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan mShowTabsAsText = isUsingTwoPanes; 104aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan 1054ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki // Set up search view. 1060bf7cd0124fa6f74fe3d64619740420ded86dcceKatherine Kuan View customSearchView = LayoutInflater.from(mActionBar.getThemedContext()).inflate( 1070bf7cd0124fa6f74fe3d64619740420ded86dcceKatherine Kuan R.layout.custom_action_bar, null); 1084ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki int searchViewWidth = mContext.getResources().getDimensionPixelSize( 1094ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki R.dimen.search_view_width); 1104ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki if (searchViewWidth == 0) { 1114ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki searchViewWidth = LayoutParams.MATCH_PARENT; 1121173ae29217fc83f254404f8a5fa10419ee83c93Dmitri Plotnikov } 1134ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki LayoutParams layoutParams = new LayoutParams(searchViewWidth, LayoutParams.WRAP_CONTENT); 1144ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki mSearchView = (SearchView) customSearchView.findViewById(R.id.search_view); 115d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // Since the {@link SearchView} in this app is "click-to-expand", set the below mode on the 116d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // {@link SearchView} so that the magnifying glass icon appears inside the editable text 117d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // field. (In the "click-to-expand" search pattern, the user must explicitly expand the 118d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // search field and already knows a search is being conducted, so the icon is redundant 119d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // and can go away once the user starts typing.) 120d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan mSearchView.setIconifiedByDefault(true); 1214ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki mSearchView.setQueryHint(mContext.getString(R.string.hint_findContacts)); 122c5792ae6760291d435e91e0fdd28ece6b3311439Adam Powell mSearchView.setOnQueryTextListener(this); 12315ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov mSearchView.setOnCloseListener(this); 12415ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov mSearchView.setQuery(mQueryString, false); 1254ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki mActionBar.setCustomView(customSearchView, layoutParams); 1264ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki 127e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki // Set up tabs 1281ff189a390ce5011f729637baf248646560c1bc6Daisuke Miyakawa addTab(TabState.GROUPS, R.drawable.ic_tab_groups, R.string.contactsGroupsLabel); 1291ff189a390ce5011f729637baf248646560c1bc6Daisuke Miyakawa addTab(TabState.ALL, R.drawable.ic_tab_all, R.string.contactsAllLabel); 1301ff189a390ce5011f729637baf248646560c1bc6Daisuke Miyakawa addTab(TabState.FAVORITES, R.drawable.ic_tab_starred, R.string.contactsFavoritesLabel); 1314d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 1324d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 1334d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki public void initialize(Bundle savedState, ContactsRequest request) { 1344d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki if (savedState == null) { 1354d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mSearchMode = request.isSearchMode(); 1364d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mQueryString = request.getQueryString(); 13735d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki mCurrentTab = loadLastTabPreference(); 1384d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } else { 1394d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mSearchMode = savedState.getBoolean(EXTRA_KEY_SEARCH_MODE); 1404d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mQueryString = savedState.getString(EXTRA_KEY_QUERY); 1414d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 1424d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // Just set to the field here. The listener will be notified by update(). 14352aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann mCurrentTab = savedState.getInt(EXTRA_KEY_SELECTED_TAB); 1444d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 1453c877e33cb7fecc7a63af1cf3c25061d53811bf6Katherine Kuan // Show tabs or the expanded {@link SearchView}, depending on whether or not we are in 1463c877e33cb7fecc7a63af1cf3c25061d53811bf6Katherine Kuan // search mode. 1474ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki update(); 1483c877e33cb7fecc7a63af1cf3c25061d53811bf6Katherine Kuan // Expanding the {@link SearchView} clears the query, so set the query from the 1493c877e33cb7fecc7a63af1cf3c25061d53811bf6Katherine Kuan // {@link ContactsRequest} after it has been expanded, if applicable. 1503c877e33cb7fecc7a63af1cf3c25061d53811bf6Katherine Kuan if (mSearchMode && !TextUtils.isEmpty(mQueryString)) { 1513c877e33cb7fecc7a63af1cf3c25061d53811bf6Katherine Kuan setQueryString(mQueryString); 1523c877e33cb7fecc7a63af1cf3c25061d53811bf6Katherine Kuan } 1538b8264620f9362e089322d1e3a7cc5620900e6f9Dmitri Plotnikov } 1548b8264620f9362e089322d1e3a7cc5620900e6f9Dmitri Plotnikov 1551a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov public void setListener(Listener listener) { 1561a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov mListener = listener; 15708e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 15808e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 15952aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann private void addTab(int expectedTabIndex, int icon, int description) { 1604d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki final Tab tab = mActionBar.newTab(); 1614d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki tab.setTabListener(mTabListener); 162aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan if (mShowTabsAsText) { 163aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan tab.setText(description); 164aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan } else { 165aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan tab.setIcon(icon); 166aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan tab.setContentDescription(description); 167aaadf9393e98b04a40d249cd97ec758047268ed9Katherine Kuan } 1684d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mActionBar.addTab(tab); 16952aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann if (expectedTabIndex != tab.getPosition()) { 17052aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann throw new IllegalStateException("Tabs must be created in the right order"); 17152aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann } 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) { 18752aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann setCurrentTab(tab.getPosition()); 1884d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 1894d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 1904d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 1914d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 1924d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki /** 1934d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki * Change the current tab, and notify the listener. 1944d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki */ 19552aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann public void setCurrentTab(int tab) { 196250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki setCurrentTab(tab, true); 197250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki } 198250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki 199250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki /** 200250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki * Change the current tab 201250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki */ 20252aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann public void setCurrentTab(int tab, boolean notifyListener) { 2034d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki if (tab == mCurrentTab) { 2044d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki return; 2054d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 2064d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mCurrentTab = tab; 2074d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 2084d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki if ((mActionBar.getNavigationMode() == ActionBar.NAVIGATION_MODE_TABS) 20952aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann && (mCurrentTab != mActionBar.getSelectedNavigationIndex())) { 21052aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann mActionBar.setSelectedNavigationItem(mCurrentTab); 2114d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 2124d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 213250ce43794cdf6820f7a13ef0195a566bd0c8c64Makoto Onuki if (notifyListener && mListener != null) mListener.onSelectedTabChanged(); 21435d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki saveLastTabPreference(mCurrentTab); 2154d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 2164d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 21752aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann public int getCurrentTab() { 2184d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki return mCurrentTab; 2194d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 2204d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki 2211db00f68b34f6cf7e9d19fedb559cf12f8c05e9cMakoto Onuki /** 2221db00f68b34f6cf7e9d19fedb559cf12f8c05e9cMakoto Onuki * @return Whether in search mode, i.e. if the search view is visible/expanded. 2231db00f68b34f6cf7e9d19fedb559cf12f8c05e9cMakoto Onuki * 2241db00f68b34f6cf7e9d19fedb559cf12f8c05e9cMakoto Onuki * Note even if the action bar is in search mode, if the query is empty, the search fragment 2251db00f68b34f6cf7e9d19fedb559cf12f8c05e9cMakoto Onuki * will not be in search mode. 2261db00f68b34f6cf7e9d19fedb559cf12f8c05e9cMakoto Onuki */ 22715ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov public boolean isSearchMode() { 22815ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov return mSearchMode; 22908e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 23008e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 23115ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov public void setSearchMode(boolean flag) { 23215ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov if (mSearchMode != flag) { 23315ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov mSearchMode = flag; 234bee8d3bd37edf44c535c0cc401e8d03f095b8328Dmitri Plotnikov update(); 235f3f933a798c9cfffb1c1c0cc770187b5b0b763ebKatherine Kuan if (mSearchView == null) { 236f3f933a798c9cfffb1c1c0cc770187b5b0b763ebKatherine Kuan return; 237f3f933a798c9cfffb1c1c0cc770187b5b0b763ebKatherine Kuan } 2380b51a8d4539cc516dc2c02948335347d1df8769bDmitri Plotnikov if (mSearchMode) { 2394ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki setFocusOnSearchView(); 240ab4d29f6a2edbfae008dbaff35f25baad73aa0dfDmitri Plotnikov } else { 241ab4d29f6a2edbfae008dbaff35f25baad73aa0dfDmitri Plotnikov mSearchView.setQuery(null, false); 2420b51a8d4539cc516dc2c02948335347d1df8769bDmitri Plotnikov } 243311882ab2fc1e9c8264f5892a6d50722b834e348Daniel Lehmann } else if (flag) { 244311882ab2fc1e9c8264f5892a6d50722b834e348Daniel Lehmann // Everything is already set up. Still make sure the keyboard is up 245311882ab2fc1e9c8264f5892a6d50722b834e348Daniel Lehmann if (mSearchView != null) setFocusOnSearchView(); 24608e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 24708e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 24808e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 24908e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov public String getQueryString() { 2501db00f68b34f6cf7e9d19fedb559cf12f8c05e9cMakoto Onuki return mSearchMode ? mQueryString : null; 25108e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 25208e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 25308e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov public void setQueryString(String query) { 25408e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov mQueryString = query; 255f3f933a798c9cfffb1c1c0cc770187b5b0b763ebKatherine Kuan if (mSearchView != null) { 256f3f933a798c9cfffb1c1c0cc770187b5b0b763ebKatherine Kuan mSearchView.setQuery(query, false); 257f3f933a798c9cfffb1c1c0cc770187b5b0b763ebKatherine Kuan } 25808e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 25908e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 260e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki /** @return true if the "UP" icon is showing. */ 261e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki public boolean isUpShowing() { 262e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki return mSearchMode; // Only shown on the search mode. 263e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki } 264e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki 265e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki private void updateDisplayOptions() { 266e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki // All the flags we may change in this method. 267e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki final int MASK = ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_HOME 268d84ea5c8b0a8376e7cf80b0e15b65a72fd99d7a9Adam Powell | ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_CUSTOM; 269e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki 270e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki // The current flags set to the action bar. (only the ones that we may change here) 271e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki final int current = mActionBar.getDisplayOptions() & MASK; 272e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki 273e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki // Build the new flags... 274e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki int newFlags = 0; 275e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki newFlags |= ActionBar.DISPLAY_SHOW_TITLE; 276e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki if (mShowHomeIcon) { 277e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki newFlags |= ActionBar.DISPLAY_SHOW_HOME; 278e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki } 279e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki if (mSearchMode) { 280e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki newFlags |= ActionBar.DISPLAY_SHOW_HOME; 281e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki newFlags |= ActionBar.DISPLAY_HOME_AS_UP; 282e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki newFlags |= ActionBar.DISPLAY_SHOW_CUSTOM; 283e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki } 284d84ea5c8b0a8376e7cf80b0e15b65a72fd99d7a9Adam Powell mActionBar.setHomeButtonEnabled(mSearchMode); 285e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki 286e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki if (current != newFlags) { 287e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki // Pass the mask here to preserve other flags that we're not interested here. 288e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki mActionBar.setDisplayOptions(newFlags, MASK); 289e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki } 290e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki } 291e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki 2924d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki private void update() { 293b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan if (mSearchMode) { 2945267367e202dcb39ef0fe912a7be9209548ec436Makoto Onuki setFocusOnSearchView(); 295d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // Since we have the {@link SearchView} in a custom action bar, we must manually handle 2963c877e33cb7fecc7a63af1cf3c25061d53811bf6Katherine Kuan // expanding the {@link SearchView} when a search is initiated. Note that a side effect 2973c877e33cb7fecc7a63af1cf3c25061d53811bf6Katherine Kuan // of this method is that the {@link SearchView} query text is set to empty string. 298d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan mSearchView.onActionViewExpanded(); 2994d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki if (mActionBar.getNavigationMode() != ActionBar.NAVIGATION_MODE_STANDARD) { 3004d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); 3014d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 302b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan if (mListener != null) { 303b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan mListener.onAction(Action.START_SEARCH_MODE); 304c9916d303cc48b4a4ff94ef9c56d7ca5da72c4deDmitri Plotnikov } 30515ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov } else { 3064d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki if (mActionBar.getNavigationMode() != ActionBar.NAVIGATION_MODE_TABS) { 3074d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // setNavigationMode will trigger onTabSelected() with the tab which was previously 3084d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // selected. 3094d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // The issue is that when we're first switching to the tab navigation mode after 3104d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // screen orientation changes, onTabSelected() will get called with the first tab 3114d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // (i.e. favorite), which would results in mCurrentTab getting set to FAVORITES and 3124d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // we'd lose restored tab. 3134d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // So let's just disable the callback here temporarily. We'll notify the listener 3144d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki // after this anyway. 3154d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mTabListener.mIgnoreTabSelected = true; 3164d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); 31752aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann mActionBar.setSelectedNavigationItem(mCurrentTab); 3184d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mTabListener.mIgnoreTabSelected = false; 3194d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki } 320b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan mActionBar.setTitle(null); 321d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // Since we have the {@link SearchView} in a custom action bar, we must manually handle 322d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // collapsing the {@link SearchView} when search mode is exited. 323d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan mSearchView.onActionViewCollapsed(); 324b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan if (mListener != null) { 325b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan mListener.onAction(Action.STOP_SEARCH_MODE); 3264d788fc27ac855bd6e0cec17dff39a18564f0089Makoto Onuki mListener.onSelectedTabChanged(); 327bee8d3bd37edf44c535c0cc401e8d03f095b8328Dmitri Plotnikov } 32808e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 329e0c66aff27473d0edbd95660b95f0f2a80ab63ecMakoto Onuki updateDisplayOptions(); 33008e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 33108e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 33208e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov @Override 333c5792ae6760291d435e91e0fdd28ece6b3311439Adam Powell public boolean onQueryTextChange(String queryString) { 334b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan // TODO: Clean up SearchView code because it keeps setting the SearchView query, 335b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan // invoking onQueryChanged, setting up the fragment again, invalidating the options menu, 336b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan // storing the SearchView again, and etc... unless we add in the early return statements. 337b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan if (queryString.equals(mQueryString)) { 338b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan return false; 339b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan } 34008e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov mQueryString = queryString; 341ab4d29f6a2edbfae008dbaff35f25baad73aa0dfDmitri Plotnikov if (!mSearchMode) { 342ab4d29f6a2edbfae008dbaff35f25baad73aa0dfDmitri Plotnikov if (!TextUtils.isEmpty(queryString)) { 343ab4d29f6a2edbfae008dbaff35f25baad73aa0dfDmitri Plotnikov setSearchMode(true); 3449806ed099226a19e92c29d9efa791cd187fd2a53Dmitri Plotnikov } 345ab4d29f6a2edbfae008dbaff35f25baad73aa0dfDmitri Plotnikov } else if (mListener != null) { 346b5760b94bbf56ce348876ec36f4669d20a1530f5Katherine Kuan mListener.onAction(Action.CHANGE_SEARCH_QUERY); 3479806ed099226a19e92c29d9efa791cd187fd2a53Dmitri Plotnikov } 348ab4d29f6a2edbfae008dbaff35f25baad73aa0dfDmitri Plotnikov 3491a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov return true; 3501a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov } 3511a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov 3521a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov @Override 353c5792ae6760291d435e91e0fdd28ece6b3311439Adam Powell public boolean onQueryTextSubmit(String query) { 354d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // When the search is "committed" by the user, then hide the keyboard so the user can 355d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan // more easily browse the list of results. 356d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan if (mSearchView != null) { 357d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan InputMethodManager imm = (InputMethodManager) mContext.getSystemService( 358d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan Context.INPUT_METHOD_SERVICE); 359d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan if (imm != null) { 360d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan imm.hideSoftInputFromWindow(mSearchView.getWindowToken(), 0); 361d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan } 362d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan mSearchView.clearFocus(); 363d8a61b7296bf7388505f0211d7ef44700e766193Katherine Kuan } 3641a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov return true; 36508e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 36608e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 36708e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov @Override 3681a59cffd839069ce91a9febb8a136442340ab4c8Dmitri Plotnikov public boolean onClose() { 36915ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov setSearchMode(false); 37015ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov return false; 37108e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov } 37208e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov 37315ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov public void onSaveInstanceState(Bundle outState) { 37415ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov outState.putBoolean(EXTRA_KEY_SEARCH_MODE, mSearchMode); 37515ccbb4d22fd65165cacb7970cbe61de1aa9aac4Dmitri Plotnikov outState.putString(EXTRA_KEY_QUERY, mQueryString); 37652aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann outState.putInt(EXTRA_KEY_SELECTED_TAB, mCurrentTab); 377fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov } 378fa49a66979567894c85fe2489d1375216d67597eDmitri Plotnikov 3795c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan /** 3805c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan * Clears the focus from the {@link SearchView} if we are in search mode. 3815c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan * This will suppress the IME if it is visible. 3825c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan */ 3835c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan public void clearFocusOnSearchView() { 3845c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan if (isSearchMode()) { 3855c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan if (mSearchView != null) { 3865c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan mSearchView.clearFocus(); 3875c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan } 3885c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan } 3895c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan } 3905c3a0a1440625fab859aab420cb08bc0276358a0Katherine Kuan 39109d66a382d1f94618b52fec11ba54d9243a95a18Daniel Lehmann public void setFocusOnSearchView() { 3924ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki mSearchView.requestFocus(); 3934ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki mSearchView.setIconified(false); // Workaround for the "IME not popping up" issue. 3944ba903cdf132a6be2e5efa33187b18ca8446e503Makoto Onuki } 39535d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki 39652aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann private void saveLastTabPreference(int tab) { 39752aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann mPrefs.edit().putInt(PERSISTENT_LAST_TAB, tab).apply(); 39835d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki } 39935d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki 40052aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann private int loadLastTabPreference() { 40135d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki try { 40252aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann return mPrefs.getInt(PERSISTENT_LAST_TAB, TabState.DEFAULT); 40335d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki } catch (IllegalArgumentException e) { 40435d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki // Preference is corrupt? 40552aa32db432085ab2cd4864bd1edd1ef843da867Daniel Lehmann return TabState.DEFAULT; 40635d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki } 40735d4aec605ce915ff5c574892a2069a37e0b9a77Makoto Onuki } 40808e1c4e49947daee26cfc435d01605febb7ae6faDmitri Plotnikov} 409