1 /* 2 * Copyright (C) 2013 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 17 package com.android.documentsui; 18 19 import static com.android.documentsui.Shared.DEBUG; 20 21 import android.annotation.Nullable; 22 import android.os.Bundle; 23 import android.provider.DocumentsContract.Root; 24 import android.text.TextUtils; 25 import android.util.Log; 26 import android.view.Menu; 27 import android.view.MenuItem; 28 import android.view.MenuItem.OnActionExpandListener; 29 import android.view.View; 30 import android.view.View.OnClickListener; 31 import android.view.View.OnFocusChangeListener; 32 import android.widget.SearchView; 33 import android.widget.SearchView.OnQueryTextListener; 34 35 import com.android.documentsui.model.RootInfo; 36 37 /** 38 * Manages searching UI behavior. 39 */ 40 final class SearchViewManager implements 41 SearchView.OnCloseListener, OnQueryTextListener, OnClickListener, OnFocusChangeListener, 42 OnActionExpandListener { 43 44 public interface SearchManagerListener { onSearchChanged(@ullable String query)45 void onSearchChanged(@Nullable String query); onSearchFinished()46 void onSearchFinished(); 47 } 48 49 public static final String TAG = "SearchManger"; 50 51 private SearchManagerListener mListener; 52 private boolean mSearchExpanded; 53 private String mCurrentSearch; 54 private boolean mIgnoreNextClose; 55 private boolean mFullBar; 56 57 private DocumentsToolbar mActionBar; 58 private MenuItem mMenuItem; 59 private SearchView mSearchView; 60 SearchViewManager(SearchManagerListener listener, @Nullable Bundle savedState)61 public SearchViewManager(SearchManagerListener listener, @Nullable Bundle savedState) { 62 mListener = listener; 63 mCurrentSearch = savedState != null ? savedState.getString(Shared.EXTRA_QUERY) : null; 64 } 65 setSearchMangerListener(SearchManagerListener listener)66 public void setSearchMangerListener(SearchManagerListener listener) { 67 mListener = listener; 68 } 69 install(DocumentsToolbar actionBar, boolean isFullBarSearch)70 public void install(DocumentsToolbar actionBar, boolean isFullBarSearch) { 71 mActionBar = actionBar; 72 mMenuItem = actionBar.getSearchMenu(); 73 mSearchView = (SearchView) mMenuItem.getActionView(); 74 75 mSearchView.setOnQueryTextListener(this); 76 mSearchView.setOnCloseListener(this); 77 mSearchView.setOnSearchClickListener(this); 78 mSearchView.setOnQueryTextFocusChangeListener(this); 79 80 mFullBar = isFullBarSearch; 81 if (mFullBar) { 82 mMenuItem.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW 83 | MenuItem.SHOW_AS_ACTION_ALWAYS); 84 mMenuItem.setOnActionExpandListener(this); 85 } 86 87 restoreSearch(); 88 } 89 90 /** 91 * Used to hide menu icons, when the search is being restored. Needed because search restoration 92 * is done before onPrepareOptionsMenu(Menu menu) that is overriding the icons visibility. 93 */ updateMenu()94 public void updateMenu() { 95 if (isSearching() && mFullBar) { 96 Menu menu = mActionBar.getMenu(); 97 menu.setGroupVisible(R.id.group_hide_when_searching, false); 98 } 99 } 100 101 /** 102 * @param root Info about the current directory. 103 */ update(RootInfo root)104 void update(RootInfo root) { 105 if (mMenuItem == null) { 106 if (DEBUG) Log.d(TAG, "update called before Search MenuItem installed."); 107 return; 108 } 109 110 if (mCurrentSearch != null) { 111 mMenuItem.expandActionView(); 112 113 mSearchView.setIconified(false); 114 mSearchView.clearFocus(); 115 mSearchView.setQuery(mCurrentSearch, false); 116 } else { 117 mSearchView.clearFocus(); 118 if (!mSearchView.isIconified()) { 119 mIgnoreNextClose = true; 120 mSearchView.setIconified(true); 121 } 122 123 if (mMenuItem.isActionViewExpanded()) { 124 mMenuItem.collapseActionView(); 125 } 126 } 127 128 showMenu(root != null 129 && ((root.flags & Root.FLAG_SUPPORTS_SEARCH) != 0)); 130 } 131 showMenu(boolean visible)132 void showMenu(boolean visible) { 133 if (mMenuItem == null) { 134 if (DEBUG) Log.d(TAG, "showMenu called before Search MenuItem installed."); 135 return; 136 } 137 138 if (!visible) { 139 mCurrentSearch = null; 140 } 141 142 mMenuItem.setVisible(visible); 143 } 144 145 /** 146 * Cancels current search operation. Triggers clearing and collapsing the SearchView. 147 * 148 * @return True if it cancels search. False if it does not operate search currently. 149 */ cancelSearch()150 boolean cancelSearch() { 151 if (isExpanded() || isSearching()) { 152 // If the query string is not empty search view won't get iconified 153 mSearchView.setQuery("", false); 154 155 if (mFullBar) { 156 onClose(); 157 } else { 158 // Causes calling onClose(). onClose() is triggering directory content update. 159 mSearchView.setIconified(true); 160 } 161 return true; 162 } 163 return false; 164 } 165 166 /** 167 * Sets search view into the searching state. Used to restore state after device orientation 168 * change. 169 */ restoreSearch()170 private void restoreSearch() { 171 if (isSearching()) { 172 if(mFullBar) { 173 mMenuItem.expandActionView(); 174 } else { 175 mSearchView.setIconified(false); 176 } 177 onSearchExpanded(); 178 mSearchView.setQuery(mCurrentSearch, false); 179 mSearchView.clearFocus(); 180 } 181 } 182 onSearchExpanded()183 private void onSearchExpanded() { 184 mSearchExpanded = true; 185 if(mFullBar) { 186 Menu menu = mActionBar.getMenu(); 187 menu.setGroupVisible(R.id.group_hide_when_searching, false); 188 } 189 } 190 191 /** 192 * Clears the search. Triggers refreshing of the directory content. 193 * @return True if the default behavior of clearing/dismissing SearchView should be overridden. 194 * False otherwise. 195 */ 196 @Override onClose()197 public boolean onClose() { 198 mSearchExpanded = false; 199 if (mIgnoreNextClose) { 200 mIgnoreNextClose = false; 201 return false; 202 } 203 204 // Refresh the directory if a search was done 205 if (mCurrentSearch != null) { 206 mCurrentSearch = null; 207 if (mListener != null) { 208 mListener.onSearchChanged(mCurrentSearch); 209 } 210 } 211 212 if(mFullBar) { 213 mMenuItem.collapseActionView(); 214 } 215 mListener.onSearchFinished(); 216 217 return false; 218 } 219 220 /** 221 * Called when owning activity is saving state to be used to restore state during creation. 222 * @param state Bundle to save state too 223 */ onSaveInstanceState(Bundle state)224 public void onSaveInstanceState(Bundle state) { 225 state.putString(Shared.EXTRA_QUERY, mCurrentSearch); 226 } 227 228 /** 229 * Sets mSearchExpanded. Called when search icon is clicked to start search for both search view 230 * modes. 231 */ 232 @Override onClick(View v)233 public void onClick(View v) { 234 onSearchExpanded(); 235 } 236 237 @Override onQueryTextSubmit(String query)238 public boolean onQueryTextSubmit(String query) { 239 mCurrentSearch = query; 240 mSearchView.clearFocus(); 241 if (mListener != null) { 242 mListener.onSearchChanged(mCurrentSearch); 243 } 244 return true; 245 } 246 247 /** 248 * Used to detect and handle back button pressed event when search is expanded. 249 */ 250 @Override onFocusChange(View v, boolean hasFocus)251 public void onFocusChange(View v, boolean hasFocus) { 252 if (!hasFocus) { 253 if (mCurrentSearch == null) { 254 mSearchView.setIconified(true); 255 } else if (TextUtils.isEmpty(mSearchView.getQuery())) { 256 cancelSearch(); 257 } 258 } 259 } 260 261 @Override onQueryTextChange(String newText)262 public boolean onQueryTextChange(String newText) { 263 return false; 264 } 265 266 @Override onMenuItemActionCollapse(MenuItem item)267 public boolean onMenuItemActionCollapse(MenuItem item) { 268 Menu menu = mActionBar.getMenu(); 269 menu.setGroupVisible(R.id.group_hide_when_searching, true); 270 271 // Handles case when search view is collapsed by using the arrow on the left of the bar 272 if (isExpanded() || isSearching()) { 273 cancelSearch(); 274 return false; 275 } 276 return true; 277 } 278 279 @Override onMenuItemActionExpand(MenuItem item)280 public boolean onMenuItemActionExpand(MenuItem item) { 281 return true; 282 } 283 getCurrentSearch()284 String getCurrentSearch() { 285 return mCurrentSearch; 286 } 287 isSearching()288 boolean isSearching() { 289 return mCurrentSearch != null; 290 } 291 isExpanded()292 boolean isExpanded() { 293 return mSearchExpanded; 294 } 295 296 } 297