ActionBarAdapter.java revision d84ea5c8b0a8376e7cf80b0e15b65a72fd99d7a9
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 private boolean mShowHomeIcon; 75 76 public enum TabState { 77 FAVORITES, ALL, GROUPS; 78 79 public static TabState fromInt(int value) { 80 switch (value) { 81 case 0: 82 return FAVORITES; 83 case 1: 84 return ALL; 85 case 2: 86 return GROUPS; 87 } 88 throw new IllegalArgumentException("Invalid value: " + value); 89 } 90 } 91 92 private TabState mCurrentTab = TabState.FAVORITES; 93 94 public ActionBarAdapter(Context context, Listener listener, ActionBar actionBar) { 95 mContext = context; 96 mListener = listener; 97 mActionBar = actionBar; 98 mSearchLabelText = mContext.getString(R.string.search_label); 99 mAlwaysShowSearchView = mContext.getResources().getBoolean(R.bool.always_show_search_view); 100 101 mShowHomeIcon = mContext.getResources().getBoolean(R.bool.show_home_icon); 102 103 // Set up search view. 104 View customSearchView = LayoutInflater.from(mContext).inflate(R.layout.custom_action_bar, 105 null); 106 int searchViewWidth = mContext.getResources().getDimensionPixelSize( 107 R.dimen.search_view_width); 108 if (searchViewWidth == 0) { 109 searchViewWidth = LayoutParams.MATCH_PARENT; 110 } 111 LayoutParams layoutParams = new LayoutParams(searchViewWidth, LayoutParams.WRAP_CONTENT); 112 mSearchView = (SearchView) customSearchView.findViewById(R.id.search_view); 113 mSearchView.setQueryHint(mContext.getString(R.string.hint_findContacts)); 114 mSearchView.setOnQueryTextListener(this); 115 mSearchView.setOnCloseListener(this); 116 mSearchView.setQuery(mQueryString, false); 117 mActionBar.setCustomView(customSearchView, layoutParams); 118 119 // Set up tabs 120 addTab(TabState.FAVORITES, mContext.getString(R.string.contactsFavoritesLabel)); 121 addTab(TabState.ALL, mContext.getString(R.string.contactsAllLabel)); 122 addTab(TabState.GROUPS, mContext.getString(R.string.contactsGroupsLabel)); 123 } 124 125 public void initialize(Bundle savedState, ContactsRequest request) { 126 if (savedState == null) { 127 mSearchMode = request.isSearchMode(); 128 mQueryString = request.getQueryString(); 129 } else { 130 mSearchMode = savedState.getBoolean(EXTRA_KEY_SEARCH_MODE); 131 mQueryString = savedState.getString(EXTRA_KEY_QUERY); 132 133 // Just set to the field here. The listener will be notified by update(). 134 mCurrentTab = TabState.fromInt(savedState.getInt(EXTRA_KEY_SELECTED_TAB)); 135 } 136 update(); 137 } 138 139 public void setListener(Listener listener) { 140 mListener = listener; 141 } 142 143 private void addTab(TabState tabState, String text) { 144 final Tab tab = mActionBar.newTab(); 145 tab.setTag(tabState); 146 tab.setText(text); 147 tab.setTabListener(mTabListener); 148 mActionBar.addTab(tab); 149 } 150 151 private class MyTabListener implements ActionBar.TabListener { 152 /** 153 * If true, it won't call {@link #setCurrentTab} in {@link #onTabSelected}. 154 * This flag is used when we want to programmatically update the current tab without 155 * {@link #onTabSelected} getting called. 156 */ 157 public boolean mIgnoreTabSelected; 158 159 @Override public void onTabReselected(Tab tab, FragmentTransaction ft) { } 160 @Override public void onTabUnselected(Tab tab, FragmentTransaction ft) { } 161 162 @Override public void onTabSelected(Tab tab, FragmentTransaction ft) { 163 if (!mIgnoreTabSelected) { 164 setCurrentTab((TabState)tab.getTag()); 165 } 166 } 167 } 168 169 /** 170 * Change the current tab, and notify the listener. 171 */ 172 public void setCurrentTab(TabState tab) { 173 setCurrentTab(tab, true); 174 } 175 176 /** 177 * Change the current tab 178 */ 179 public void setCurrentTab(TabState tab, boolean notifyListener) { 180 if (tab == null) throw new NullPointerException(); 181 if (tab == mCurrentTab) { 182 return; 183 } 184 mCurrentTab = tab; 185 186 int index = mCurrentTab.ordinal(); 187 if ((mActionBar.getNavigationMode() == ActionBar.NAVIGATION_MODE_TABS) 188 && (index != mActionBar.getSelectedNavigationIndex())) { 189 mActionBar.setSelectedNavigationItem(index); 190 } 191 192 if (notifyListener && mListener != null) mListener.onSelectedTabChanged(); 193 } 194 195 public TabState getCurrentTab() { 196 return mCurrentTab; 197 } 198 199 public boolean isSearchMode() { 200 return mSearchMode; 201 } 202 203 public void setSearchMode(boolean flag) { 204 if (mSearchMode != flag) { 205 mSearchMode = flag; 206 update(); 207 if (mSearchView == null) { 208 return; 209 } 210 if (mSearchMode) { 211 setFocusOnSearchView(); 212 } else { 213 mSearchView.setQuery(null, false); 214 } 215 } 216 } 217 218 public String getQueryString() { 219 return mQueryString; 220 } 221 222 public void setQueryString(String query) { 223 mQueryString = query; 224 if (mSearchView != null) { 225 mSearchView.setQuery(query, false); 226 } 227 } 228 229 /** @return true if the "UP" icon is showing. */ 230 public boolean isUpShowing() { 231 return mSearchMode; // Only shown on the search mode. 232 } 233 234 private void updateDisplayOptions() { 235 // All the flags we may change in this method. 236 final int MASK = ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_HOME 237 | ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_CUSTOM; 238 239 // The current flags set to the action bar. (only the ones that we may change here) 240 final int current = mActionBar.getDisplayOptions() & MASK; 241 242 // Build the new flags... 243 int newFlags = 0; 244 newFlags |= ActionBar.DISPLAY_SHOW_TITLE; 245 if (mShowHomeIcon) { 246 newFlags |= ActionBar.DISPLAY_SHOW_HOME; 247 } 248 if (mSearchMode) { 249 newFlags |= ActionBar.DISPLAY_SHOW_HOME; 250 newFlags |= ActionBar.DISPLAY_HOME_AS_UP; 251 newFlags |= ActionBar.DISPLAY_SHOW_CUSTOM; 252 } else if (mAlwaysShowSearchView) { 253 newFlags |= ActionBar.DISPLAY_SHOW_CUSTOM; 254 } 255 mActionBar.setHomeButtonEnabled(mSearchMode); 256 257 if (current != newFlags) { 258 // Pass the mask here to preserve other flags that we're not interested here. 259 mActionBar.setDisplayOptions(newFlags, MASK); 260 } 261 } 262 263 private void update() { 264 if (mSearchMode) { 265 if (mAlwaysShowSearchView) { 266 // Tablet -- change the app title for the search mode 267 mActionBar.setTitle(mSearchLabelText); 268 } else { 269 // Phone -- search view gets focus 270 setFocusOnSearchView(); 271 } 272 if (mActionBar.getNavigationMode() != ActionBar.NAVIGATION_MODE_STANDARD) { 273 mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); 274 } 275 if (mListener != null) { 276 mListener.onAction(Action.START_SEARCH_MODE); 277 } 278 } else { 279 if (mActionBar.getNavigationMode() != ActionBar.NAVIGATION_MODE_TABS) { 280 // setNavigationMode will trigger onTabSelected() with the tab which was previously 281 // selected. 282 // The issue is that when we're first switching to the tab navigation mode after 283 // screen orientation changes, onTabSelected() will get called with the first tab 284 // (i.e. favorite), which would results in mCurrentTab getting set to FAVORITES and 285 // we'd lose restored tab. 286 // So let's just disable the callback here temporarily. We'll notify the listener 287 // after this anyway. 288 mTabListener.mIgnoreTabSelected = true; 289 mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); 290 mActionBar.setSelectedNavigationItem(mCurrentTab.ordinal()); 291 mTabListener.mIgnoreTabSelected = false; 292 } 293 mActionBar.setTitle(null); 294 if (mListener != null) { 295 mListener.onAction(Action.STOP_SEARCH_MODE); 296 mListener.onSelectedTabChanged(); 297 } 298 } 299 updateDisplayOptions(); 300 } 301 302 @Override 303 public boolean onQueryTextChange(String queryString) { 304 // TODO: Clean up SearchView code because it keeps setting the SearchView query, 305 // invoking onQueryChanged, setting up the fragment again, invalidating the options menu, 306 // storing the SearchView again, and etc... unless we add in the early return statements. 307 if (queryString.equals(mQueryString)) { 308 return false; 309 } 310 mQueryString = queryString; 311 if (!mSearchMode) { 312 if (!TextUtils.isEmpty(queryString)) { 313 setSearchMode(true); 314 } 315 } else if (mListener != null) { 316 mListener.onAction(Action.CHANGE_SEARCH_QUERY); 317 } 318 319 return true; 320 } 321 322 @Override 323 public boolean onQueryTextSubmit(String query) { 324 return true; 325 } 326 327 @Override 328 public boolean onClose() { 329 setSearchMode(false); 330 return false; 331 } 332 333 public void onSaveInstanceState(Bundle outState) { 334 outState.putBoolean(EXTRA_KEY_SEARCH_MODE, mSearchMode); 335 outState.putString(EXTRA_KEY_QUERY, mQueryString); 336 outState.putInt(EXTRA_KEY_SELECTED_TAB, mCurrentTab.ordinal()); 337 } 338 339 private void setFocusOnSearchView() { 340 mSearchView.requestFocus(); 341 mSearchView.setIconified(false); // Workaround for the "IME not popping up" issue. 342 } 343} 344