blob: 945ed3413ab6c964d1cb0218e247751bb9149bf1 [file] [log] [blame]
Aga Wronskaf6a31d32016-01-15 17:30:15 -08001/*
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
17package com.android.documentsui;
18
Ben Kwaffa829f2016-03-22 11:11:46 -070019import static com.android.documentsui.Shared.DEBUG;
20
Aga Wronska893390b2016-02-17 13:50:42 -080021import android.annotation.Nullable;
22import android.os.Bundle;
Aga Wronskaf6a31d32016-01-15 17:30:15 -080023import android.provider.DocumentsContract.Root;
24import android.text.TextUtils;
25import android.util.Log;
Aga Wronskab0998562016-03-24 18:22:09 -070026import android.view.Menu;
Aga Wronskaf6a31d32016-01-15 17:30:15 -080027import android.view.MenuItem;
Aga Wronskab0998562016-03-24 18:22:09 -070028import android.view.MenuItem.OnActionExpandListener;
Aga Wronskaf6a31d32016-01-15 17:30:15 -080029import android.view.View;
30import android.view.View.OnClickListener;
31import android.view.View.OnFocusChangeListener;
32import android.widget.SearchView;
33import android.widget.SearchView.OnQueryTextListener;
34
35import com.android.documentsui.model.RootInfo;
36
37/**
38 * Manages searching UI behavior.
39 */
Aga Wronska893390b2016-02-17 13:50:42 -080040final class SearchViewManager implements
Aga Wronskab0998562016-03-24 18:22:09 -070041 SearchView.OnCloseListener, OnQueryTextListener, OnClickListener, OnFocusChangeListener,
42 OnActionExpandListener {
Aga Wronskaf6a31d32016-01-15 17:30:15 -080043
44 public interface SearchManagerListener {
Aga Wronska893390b2016-02-17 13:50:42 -080045 void onSearchChanged(@Nullable String query);
Aga Wronskab0998562016-03-24 18:22:09 -070046 void onSearchFinished();
Aga Wronskaf6a31d32016-01-15 17:30:15 -080047 }
48
49 public static final String TAG = "SearchManger";
50
51 private SearchManagerListener mListener;
Aga Wronskaf6a31d32016-01-15 17:30:15 -080052 private boolean mSearchExpanded;
Aga Wronska893390b2016-02-17 13:50:42 -080053 private String mCurrentSearch;
Aga Wronskaf6a31d32016-01-15 17:30:15 -080054 private boolean mIgnoreNextClose;
Aga Wronskab0998562016-03-24 18:22:09 -070055 private boolean mFullBar;
Aga Wronskaf6a31d32016-01-15 17:30:15 -080056
Steve McKay1f264a82016-02-03 11:15:57 -080057 private DocumentsToolbar mActionBar;
Aga Wronskab0998562016-03-24 18:22:09 -070058 private MenuItem mMenuItem;
59 private SearchView mSearchView;
Aga Wronskaf6a31d32016-01-15 17:30:15 -080060
Aga Wronska893390b2016-02-17 13:50:42 -080061 public SearchViewManager(SearchManagerListener listener, @Nullable Bundle savedState) {
Aga Wronskaf6a31d32016-01-15 17:30:15 -080062 mListener = listener;
Aga Wronska893390b2016-02-17 13:50:42 -080063 mCurrentSearch = savedState != null ? savedState.getString(Shared.EXTRA_QUERY) : null;
Aga Wronskaf6a31d32016-01-15 17:30:15 -080064 }
65
66 public void setSearchMangerListener(SearchManagerListener listener) {
67 mListener = listener;
68 }
69
Aga Wronskab0998562016-03-24 18:22:09 -070070 public void install(DocumentsToolbar actionBar, boolean isFullBarSearch) {
Aga Wronskaf6a31d32016-01-15 17:30:15 -080071 mActionBar = actionBar;
Aga Wronskab0998562016-03-24 18:22:09 -070072 mMenuItem = actionBar.getSearchMenu();
73 mSearchView = (SearchView) mMenuItem.getActionView();
Aga Wronskaf6a31d32016-01-15 17:30:15 -080074
Aga Wronskab0998562016-03-24 18:22:09 -070075 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 }
Aga Wronska893390b2016-02-17 13:50:42 -080086
87 restoreSearch();
Aga Wronskaf6a31d32016-01-15 17:30:15 -080088 }
89
90 /**
Aga Wronskab0998562016-03-24 18:22:09 -070091 * 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 */
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 /**
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800102 * @param root Info about the current directory.
103 */
104 void update(RootInfo root) {
Aga Wronskab0998562016-03-24 18:22:09 -0700105 if (mMenuItem == null) {
Ben Kwaffa829f2016-03-22 11:11:46 -0700106 if (DEBUG) Log.d(TAG, "update called before Search MenuItem installed.");
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800107 return;
108 }
109
Aga Wronska893390b2016-02-17 13:50:42 -0800110 if (mCurrentSearch != null) {
Aga Wronskab0998562016-03-24 18:22:09 -0700111 mMenuItem.expandActionView();
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800112
Aga Wronskab0998562016-03-24 18:22:09 -0700113 mSearchView.setIconified(false);
114 mSearchView.clearFocus();
115 mSearchView.setQuery(mCurrentSearch, false);
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800116 } else {
Aga Wronskab0998562016-03-24 18:22:09 -0700117 mSearchView.clearFocus();
118 if (!mSearchView.isIconified()) {
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800119 mIgnoreNextClose = true;
Aga Wronskab0998562016-03-24 18:22:09 -0700120 mSearchView.setIconified(true);
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800121 }
122
Aga Wronskab0998562016-03-24 18:22:09 -0700123 if (mMenuItem.isActionViewExpanded()) {
124 mMenuItem.collapseActionView();
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800125 }
126 }
127
128 showMenu(root != null
129 && ((root.flags & Root.FLAG_SUPPORTS_SEARCH) != 0));
130 }
131
132 void showMenu(boolean visible) {
Aga Wronskab0998562016-03-24 18:22:09 -0700133 if (mMenuItem == null) {
Ben Kwaffa829f2016-03-22 11:11:46 -0700134 if (DEBUG) Log.d(TAG, "showMenu called before Search MenuItem installed.");
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800135 return;
136 }
137
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800138 if (!visible) {
Aga Wronska893390b2016-02-17 13:50:42 -0800139 mCurrentSearch = null;
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800140 }
Aga Wronska893390b2016-02-17 13:50:42 -0800141
Aga Wronskab0998562016-03-24 18:22:09 -0700142 mMenuItem.setVisible(visible);
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800143 }
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 */
150 boolean cancelSearch() {
151 if (isExpanded() || isSearching()) {
152 // If the query string is not empty search view won't get iconified
Aga Wronskab0998562016-03-24 18:22:09 -0700153 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 }
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800161 return true;
162 }
163 return false;
164 }
165
Aga Wronskab0998562016-03-24 18:22:09 -0700166 /**
167 * Sets search view into the searching state. Used to restore state after device orientation
168 * change.
169 */
Aga Wronska893390b2016-02-17 13:50:42 -0800170 private void restoreSearch() {
171 if (isSearching()) {
Aga Wronskab0998562016-03-24 18:22:09 -0700172 if(mFullBar) {
173 mMenuItem.expandActionView();
174 } else {
175 mSearchView.setIconified(false);
176 }
Aga Wronska893390b2016-02-17 13:50:42 -0800177 onSearchExpanded();
Aga Wronskab0998562016-03-24 18:22:09 -0700178 mSearchView.setQuery(mCurrentSearch, false);
179 mSearchView.clearFocus();
Aga Wronska893390b2016-02-17 13:50:42 -0800180 }
181 }
182
183 private void onSearchExpanded() {
184 mSearchExpanded = true;
Aga Wronskab0998562016-03-24 18:22:09 -0700185 if(mFullBar) {
186 Menu menu = mActionBar.getMenu();
187 menu.setGroupVisible(R.id.group_hide_when_searching, false);
Aga Wronskacf966ae2016-03-30 13:55:19 -0700188 } else {
189 // If search in full-bar mode it will be logged in FilesActivity#onOptionsItemSelected
190 Metrics.logMenuAction(mActionBar.getContext(), R.id.menu_search);
Aga Wronskab0998562016-03-24 18:22:09 -0700191 }
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800192 }
193
194 /**
Aga Wronskab0998562016-03-24 18:22:09 -0700195 * Clears the search. Triggers refreshing of the directory content.
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800196 * @return True if the default behavior of clearing/dismissing SearchView should be overridden.
197 * False otherwise.
198 */
199 @Override
200 public boolean onClose() {
201 mSearchExpanded = false;
202 if (mIgnoreNextClose) {
203 mIgnoreNextClose = false;
204 return false;
205 }
206
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800207 // Refresh the directory if a search was done
Aga Wronska893390b2016-02-17 13:50:42 -0800208 if (mCurrentSearch != null) {
209 mCurrentSearch = null;
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800210 if (mListener != null) {
Aga Wronska893390b2016-02-17 13:50:42 -0800211 mListener.onSearchChanged(mCurrentSearch);
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800212 }
213 }
Aga Wronskab0998562016-03-24 18:22:09 -0700214
215 if(mFullBar) {
216 mMenuItem.collapseActionView();
217 }
218 mListener.onSearchFinished();
219
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800220 return false;
221 }
222
223 /**
Aga Wronskab0998562016-03-24 18:22:09 -0700224 * Called when owning activity is saving state to be used to restore state during creation.
225 * @param state Bundle to save state too
226 */
227 public void onSaveInstanceState(Bundle state) {
228 state.putString(Shared.EXTRA_QUERY, mCurrentSearch);
229 }
230
231 /**
232 * Sets mSearchExpanded. Called when search icon is clicked to start search for both search view
233 * modes.
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800234 */
235 @Override
236 public void onClick(View v) {
Aga Wronska893390b2016-02-17 13:50:42 -0800237 onSearchExpanded();
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800238 }
239
240 @Override
241 public boolean onQueryTextSubmit(String query) {
Aga Wronska893390b2016-02-17 13:50:42 -0800242 mCurrentSearch = query;
Aga Wronskab0998562016-03-24 18:22:09 -0700243 mSearchView.clearFocus();
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800244 if (mListener != null) {
Aga Wronska893390b2016-02-17 13:50:42 -0800245 mListener.onSearchChanged(mCurrentSearch);
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800246 }
247 return true;
248 }
249
Aga Wronskab0998562016-03-24 18:22:09 -0700250 /**
251 * Used to detect and handle back button pressed event when search is expanded.
252 */
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800253 @Override
254 public void onFocusChange(View v, boolean hasFocus) {
255 if (!hasFocus) {
Aga Wronska893390b2016-02-17 13:50:42 -0800256 if (mCurrentSearch == null) {
Aga Wronskab0998562016-03-24 18:22:09 -0700257 mSearchView.setIconified(true);
258 } else if (TextUtils.isEmpty(mSearchView.getQuery())) {
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800259 cancelSearch();
260 }
261 }
262 }
263
264 @Override
265 public boolean onQueryTextChange(String newText) {
266 return false;
267 }
Aga Wronska893390b2016-02-17 13:50:42 -0800268
Aga Wronskab0998562016-03-24 18:22:09 -0700269 @Override
270 public boolean onMenuItemActionCollapse(MenuItem item) {
271 Menu menu = mActionBar.getMenu();
272 menu.setGroupVisible(R.id.group_hide_when_searching, true);
273
274 // Handles case when search view is collapsed by using the arrow on the left of the bar
275 if (isExpanded() || isSearching()) {
276 cancelSearch();
277 return false;
278 }
279 return true;
280 }
281
282 @Override
283 public boolean onMenuItemActionExpand(MenuItem item) {
284 return true;
285 }
286
Aga Wronska893390b2016-02-17 13:50:42 -0800287 String getCurrentSearch() {
288 return mCurrentSearch;
289 }
290
Aga Wronskab0998562016-03-24 18:22:09 -0700291 boolean isSearching() {
292 return mCurrentSearch != null;
293 }
294
295 boolean isExpanded() {
296 return mSearchExpanded;
297 }
298
Aga Wronskaf6a31d32016-01-15 17:30:15 -0800299}