ActionBarAdapter.java revision 250ce43794cdf6820f7a13ef0195a566bd0c8c64
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.contacts.activities; 18 19import com.android.contacts.R; 20import com.android.contacts.activities.ActionBarAdapter.Listener.Action; 21import com.android.contacts.list.ContactsRequest; 22 23import android.app.ActionBar; 24import android.app.ActionBar.LayoutParams; 25import android.app.ActionBar.Tab; 26import android.app.FragmentTransaction; 27import android.content.Context; 28import android.content.res.TypedArray; 29import android.os.Bundle; 30import android.text.TextUtils; 31import android.view.LayoutInflater; 32import android.view.View; 33import android.widget.SearchView; 34import android.widget.SearchView.OnCloseListener; 35import android.widget.SearchView.OnQueryTextListener; 36 37/** 38 * Adapter for the action bar at the top of the Contacts activity. 39 */ 40public class ActionBarAdapter implements OnQueryTextListener, OnCloseListener { 41 42 public interface Listener { 43 public enum Action { 44 CHANGE_SEARCH_QUERY, START_SEARCH_MODE, STOP_SEARCH_MODE 45 } 46 47 void onAction(Action action); 48 49 /** 50 * Called when the user selects a tab. The new tab can be obtained using 51 * {@link #getCurrentTab}. 52 */ 53 void onSelectedTabChanged(); 54 } 55 56 private static final String EXTRA_KEY_SEARCH_MODE = "navBar.searchMode"; 57 private static final String EXTRA_KEY_QUERY = "navBar.query"; 58 private static final String EXTRA_KEY_SELECTED_TAB = "navBar.selectedTab"; 59 60 private boolean mSearchMode; 61 private String mQueryString; 62 63 private String mSearchLabelText; 64 private SearchView mSearchView; 65 66 private final Context mContext; 67 private final boolean mAlwaysShowSearchView; 68 69 private Listener mListener; 70 71 private final ActionBar mActionBar; 72 private final MyTabListener mTabListener = new MyTabListener(); 73 74 public enum TabState { 75 FAVORITES, ALL, GROUPS; 76 77 public static TabState fromInt(int value) { 78 switch (value) { 79 case 0: 80 return FAVORITES; 81 case 1: 82 return ALL; 83 case 2: 84 return GROUPS; 85 } 86 throw new IllegalArgumentException("Invalid value: " + value); 87 } 88 } 89 90 private TabState mCurrentTab = TabState.FAVORITES; 91 92 public ActionBarAdapter(Context context, Listener listener, ActionBar actionBar) { 93 mContext = context; 94 mListener = listener; 95 mActionBar = actionBar; 96 mSearchLabelText = mContext.getString(R.string.search_label); 97 mAlwaysShowSearchView = mContext.getResources().getBoolean(R.bool.always_show_search_view); 98 99 // Set up search view. 100 View customSearchView = LayoutInflater.from(mContext).inflate(R.layout.custom_action_bar, 101 null); 102 int searchViewWidth = mContext.getResources().getDimensionPixelSize( 103 R.dimen.search_view_width); 104 if (searchViewWidth == 0) { 105 searchViewWidth = LayoutParams.MATCH_PARENT; 106 } 107 LayoutParams layoutParams = new LayoutParams(searchViewWidth, LayoutParams.WRAP_CONTENT); 108 mSearchView = (SearchView) customSearchView.findViewById(R.id.search_view); 109 mSearchView.setQueryHint(mContext.getString(R.string.hint_findContacts)); 110 mSearchView.setOnQueryTextListener(this); 111 mSearchView.setOnCloseListener(this); 112 mSearchView.setQuery(mQueryString, false); 113 mActionBar.setCustomView(customSearchView, layoutParams); 114 115 mActionBar.setDisplayShowTitleEnabled(true); 116 117 // TODO Just use a boolean resource instead of styles. 118 TypedArray array = mContext.obtainStyledAttributes(null, R.styleable.ActionBarHomeIcon); 119 boolean showHomeIcon = array.getBoolean(R.styleable.ActionBarHomeIcon_show_home_icon, true); 120 array.recycle(); 121 mActionBar.setDisplayShowHomeEnabled(showHomeIcon); 122 123 addTab(TabState.FAVORITES, mContext.getString(R.string.contactsFavoritesLabel)); 124 addTab(TabState.ALL, mContext.getString(R.string.contactsAllLabel)); 125 addTab(TabState.GROUPS, mContext.getString(R.string.contactsGroupsLabel)); 126 } 127 128 public void initialize(Bundle savedState, ContactsRequest request) { 129 if (savedState == null) { 130 mSearchMode = request.isSearchMode(); 131 mQueryString = request.getQueryString(); 132 } else { 133 mSearchMode = savedState.getBoolean(EXTRA_KEY_SEARCH_MODE); 134 mQueryString = savedState.getString(EXTRA_KEY_QUERY); 135 136 // Just set to the field here. The listener will be notified by update(). 137 mCurrentTab = TabState.fromInt(savedState.getInt(EXTRA_KEY_SELECTED_TAB)); 138 } 139 update(); 140 } 141 142 public void setListener(Listener listener) { 143 mListener = listener; 144 } 145 146 private void addTab(TabState tabState, String text) { 147 final Tab tab = mActionBar.newTab(); 148 tab.setTag(tabState); 149 tab.setText(text); 150 tab.setTabListener(mTabListener); 151 mActionBar.addTab(tab); 152 } 153 154 private class MyTabListener implements ActionBar.TabListener { 155 /** 156 * If true, it won't call {@link #setCurrentTab} in {@link #onTabSelected}. 157 * This flag is used when we want to programmatically update the current tab without 158 * {@link #onTabSelected} getting called. 159 */ 160 public boolean mIgnoreTabSelected; 161 162 @Override public void onTabReselected(Tab tab, FragmentTransaction ft) { } 163 @Override public void onTabUnselected(Tab tab, FragmentTransaction ft) { } 164 165 @Override public void onTabSelected(Tab tab, FragmentTransaction ft) { 166 if (!mIgnoreTabSelected) { 167 setCurrentTab((TabState)tab.getTag()); 168 } 169 } 170 } 171 172 /** 173 * Change the current tab, and notify the listener. 174 */ 175 public void setCurrentTab(TabState tab) { 176 setCurrentTab(tab, true); 177 } 178 179 /** 180 * Change the current tab 181 */ 182 public void setCurrentTab(TabState tab, boolean notifyListener) { 183 if (tab == null) throw new NullPointerException(); 184 if (tab == mCurrentTab) { 185 return; 186 } 187 mCurrentTab = tab; 188 189 int index = mCurrentTab.ordinal(); 190 if ((mActionBar.getNavigationMode() == ActionBar.NAVIGATION_MODE_TABS) 191 && (index != mActionBar.getSelectedNavigationIndex())) { 192 mActionBar.setSelectedNavigationItem(index); 193 } 194 195 if (notifyListener && mListener != null) mListener.onSelectedTabChanged(); 196 } 197 198 public TabState getCurrentTab() { 199 return mCurrentTab; 200 } 201 202 public boolean isSearchMode() { 203 return mSearchMode; 204 } 205 206 public void setSearchMode(boolean flag) { 207 if (mSearchMode != flag) { 208 mSearchMode = flag; 209 update(); 210 if (mSearchView == null) { 211 return; 212 } 213 if (mSearchMode) { 214 setFocusOnSearchView(); 215 } else { 216 mSearchView.setQuery(null, false); 217 } 218 } 219 } 220 221 public String getQueryString() { 222 return mQueryString; 223 } 224 225 public void setQueryString(String query) { 226 mQueryString = query; 227 if (mSearchView != null) { 228 mSearchView.setQuery(query, false); 229 } 230 } 231 232 private void update() { 233 if (mSearchMode) { 234 mActionBar.setDisplayShowCustomEnabled(true); 235 if (mAlwaysShowSearchView) { 236 // Tablet -- change the app title for the search mode 237 mActionBar.setTitle(mSearchLabelText); 238 } else { 239 // Phone -- search view gets focus 240 setFocusOnSearchView(); 241 } 242 if (mActionBar.getNavigationMode() != ActionBar.NAVIGATION_MODE_STANDARD) { 243 mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); 244 } 245 if (mListener != null) { 246 mListener.onAction(Action.START_SEARCH_MODE); 247 } 248 } else { 249 mActionBar.setDisplayShowCustomEnabled(mAlwaysShowSearchView); 250 if (mActionBar.getNavigationMode() != ActionBar.NAVIGATION_MODE_TABS) { 251 // setNavigationMode will trigger onTabSelected() with the tab which was previously 252 // selected. 253 // The issue is that when we're first switching to the tab navigation mode after 254 // screen orientation changes, onTabSelected() will get called with the first tab 255 // (i.e. favorite), which would results in mCurrentTab getting set to FAVORITES and 256 // we'd lose restored tab. 257 // So let's just disable the callback here temporarily. We'll notify the listener 258 // after this anyway. 259 mTabListener.mIgnoreTabSelected = true; 260 mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); 261 mActionBar.setSelectedNavigationItem(mCurrentTab.ordinal()); 262 mTabListener.mIgnoreTabSelected = false; 263 } 264 mActionBar.setTitle(null); 265 if (mListener != null) { 266 mListener.onAction(Action.STOP_SEARCH_MODE); 267 mListener.onSelectedTabChanged(); 268 } 269 } 270 } 271 272 @Override 273 public boolean onQueryTextChange(String queryString) { 274 // TODO: Clean up SearchView code because it keeps setting the SearchView query, 275 // invoking onQueryChanged, setting up the fragment again, invalidating the options menu, 276 // storing the SearchView again, and etc... unless we add in the early return statements. 277 if (queryString.equals(mQueryString)) { 278 return false; 279 } 280 mQueryString = queryString; 281 if (!mSearchMode) { 282 if (!TextUtils.isEmpty(queryString)) { 283 setSearchMode(true); 284 } 285 } else if (mListener != null) { 286 mListener.onAction(Action.CHANGE_SEARCH_QUERY); 287 } 288 289 return true; 290 } 291 292 @Override 293 public boolean onQueryTextSubmit(String query) { 294 return true; 295 } 296 297 @Override 298 public boolean onClose() { 299 setSearchMode(false); 300 return false; 301 } 302 303 public void onSaveInstanceState(Bundle outState) { 304 outState.putBoolean(EXTRA_KEY_SEARCH_MODE, mSearchMode); 305 outState.putString(EXTRA_KEY_QUERY, mQueryString); 306 outState.putInt(EXTRA_KEY_SELECTED_TAB, mCurrentTab.ordinal()); 307 } 308 309 private void setFocusOnSearchView() { 310 mSearchView.requestFocus(); 311 mSearchView.setIconified(false); // Workaround for the "IME not popping up" issue. 312 } 313} 314