blob: 1bbc439e9dc8dcc763598438765cf5b5cebc9ad4 [file] [log] [blame]
Aga Wronska8788dad2016-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 Kwa543a2922016-03-22 11:11:46 -070019import static com.android.documentsui.Shared.DEBUG;
20
Aga Wronskaaf5ace52016-02-17 13:50:42 -080021import android.annotation.Nullable;
22import android.os.Bundle;
Aga Wronska8788dad2016-01-15 17:30:15 -080023import android.provider.DocumentsContract.Root;
24import android.text.TextUtils;
25import android.util.Log;
Aga Wronska8e21daa2016-03-24 18:22:09 -070026import android.view.Menu;
Aga Wronska8788dad2016-01-15 17:30:15 -080027import android.view.MenuItem;
Aga Wronska8e21daa2016-03-24 18:22:09 -070028import android.view.MenuItem.OnActionExpandListener;
Aga Wronska8788dad2016-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
Steve McKayd0805062016-09-15 14:30:38 -070035import com.android.documentsui.base.RootInfo;
Garfield, Tan11d23482016-08-05 09:33:29 -070036import com.android.documentsui.sorting.SortModel;
Aga Wronska8788dad2016-01-15 17:30:15 -080037
38/**
39 * Manages searching UI behavior.
40 */
Ben Lin7c35b032016-05-31 13:24:01 -070041public class SearchViewManager implements
Aga Wronska8e21daa2016-03-24 18:22:09 -070042 SearchView.OnCloseListener, OnQueryTextListener, OnClickListener, OnFocusChangeListener,
43 OnActionExpandListener {
Aga Wronska8788dad2016-01-15 17:30:15 -080044
45 public interface SearchManagerListener {
Aga Wronskaaf5ace52016-02-17 13:50:42 -080046 void onSearchChanged(@Nullable String query);
Aga Wronska8e21daa2016-03-24 18:22:09 -070047 void onSearchFinished();
Aga Wronska8788dad2016-01-15 17:30:15 -080048 }
49
Ben Lin7c35b032016-05-31 13:24:01 -070050 private static final String TAG = "SearchManager";
Aga Wronska8788dad2016-01-15 17:30:15 -080051
52 private SearchManagerListener mListener;
Aga Wronska8788dad2016-01-15 17:30:15 -080053 private boolean mSearchExpanded;
Aga Wronskaaf5ace52016-02-17 13:50:42 -080054 private String mCurrentSearch;
Aga Wronska8788dad2016-01-15 17:30:15 -080055 private boolean mIgnoreNextClose;
Aga Wronska8e21daa2016-03-24 18:22:09 -070056 private boolean mFullBar;
Aga Wronska8788dad2016-01-15 17:30:15 -080057
Steve McKay18d01e82016-02-03 11:15:57 -080058 private DocumentsToolbar mActionBar;
Aga Wronska8e21daa2016-03-24 18:22:09 -070059 private MenuItem mMenuItem;
60 private SearchView mSearchView;
Aga Wronska8788dad2016-01-15 17:30:15 -080061
Garfield, Tan11d23482016-08-05 09:33:29 -070062 // We need to disable sorting during search.
63 private SortModel mSortModel;
64
65 public SearchViewManager(
66 SearchManagerListener listener, @Nullable Bundle savedState, SortModel sortModel) {
Aga Wronska8788dad2016-01-15 17:30:15 -080067 mListener = listener;
Aga Wronskaaf5ace52016-02-17 13:50:42 -080068 mCurrentSearch = savedState != null ? savedState.getString(Shared.EXTRA_QUERY) : null;
Garfield, Tan11d23482016-08-05 09:33:29 -070069 mSortModel = sortModel;
Aga Wronska8788dad2016-01-15 17:30:15 -080070 }
71
72 public void setSearchMangerListener(SearchManagerListener listener) {
73 mListener = listener;
74 }
75
Aga Wronska8e21daa2016-03-24 18:22:09 -070076 public void install(DocumentsToolbar actionBar, boolean isFullBarSearch) {
Aga Wronska8788dad2016-01-15 17:30:15 -080077 mActionBar = actionBar;
Aga Wronska8e21daa2016-03-24 18:22:09 -070078 mMenuItem = actionBar.getSearchMenu();
79 mSearchView = (SearchView) mMenuItem.getActionView();
Aga Wronska8788dad2016-01-15 17:30:15 -080080
Aga Wronska8e21daa2016-03-24 18:22:09 -070081 mSearchView.setOnQueryTextListener(this);
82 mSearchView.setOnCloseListener(this);
83 mSearchView.setOnSearchClickListener(this);
84 mSearchView.setOnQueryTextFocusChangeListener(this);
85
86 mFullBar = isFullBarSearch;
87 if (mFullBar) {
88 mMenuItem.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW
89 | MenuItem.SHOW_AS_ACTION_ALWAYS);
90 mMenuItem.setOnActionExpandListener(this);
91 }
Aga Wronskaaf5ace52016-02-17 13:50:42 -080092
93 restoreSearch();
Aga Wronska8788dad2016-01-15 17:30:15 -080094 }
95
96 /**
Aga Wronska8e21daa2016-03-24 18:22:09 -070097 * Used to hide menu icons, when the search is being restored. Needed because search restoration
98 * is done before onPrepareOptionsMenu(Menu menu) that is overriding the icons visibility.
99 */
100 public void updateMenu() {
101 if (isSearching() && mFullBar) {
102 Menu menu = mActionBar.getMenu();
103 menu.setGroupVisible(R.id.group_hide_when_searching, false);
104 }
105 }
106
107 /**
Aga Wronska8788dad2016-01-15 17:30:15 -0800108 * @param root Info about the current directory.
109 */
110 void update(RootInfo root) {
Aga Wronska8e21daa2016-03-24 18:22:09 -0700111 if (mMenuItem == null) {
Ben Kwa543a2922016-03-22 11:11:46 -0700112 if (DEBUG) Log.d(TAG, "update called before Search MenuItem installed.");
Aga Wronska8788dad2016-01-15 17:30:15 -0800113 return;
114 }
115
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800116 if (mCurrentSearch != null) {
Aga Wronska8e21daa2016-03-24 18:22:09 -0700117 mMenuItem.expandActionView();
Aga Wronska8788dad2016-01-15 17:30:15 -0800118
Aga Wronska8e21daa2016-03-24 18:22:09 -0700119 mSearchView.setIconified(false);
120 mSearchView.clearFocus();
121 mSearchView.setQuery(mCurrentSearch, false);
Aga Wronska8788dad2016-01-15 17:30:15 -0800122 } else {
Aga Wronska8e21daa2016-03-24 18:22:09 -0700123 mSearchView.clearFocus();
124 if (!mSearchView.isIconified()) {
Aga Wronska8788dad2016-01-15 17:30:15 -0800125 mIgnoreNextClose = true;
Aga Wronska8e21daa2016-03-24 18:22:09 -0700126 mSearchView.setIconified(true);
Aga Wronska8788dad2016-01-15 17:30:15 -0800127 }
128
Aga Wronska8e21daa2016-03-24 18:22:09 -0700129 if (mMenuItem.isActionViewExpanded()) {
130 mMenuItem.collapseActionView();
Aga Wronska8788dad2016-01-15 17:30:15 -0800131 }
132 }
133
134 showMenu(root != null
135 && ((root.flags & Root.FLAG_SUPPORTS_SEARCH) != 0));
136 }
137
Ben Lin7c35b032016-05-31 13:24:01 -0700138 protected void showMenu(boolean visible) {
Aga Wronska8e21daa2016-03-24 18:22:09 -0700139 if (mMenuItem == null) {
Ben Kwa543a2922016-03-22 11:11:46 -0700140 if (DEBUG) Log.d(TAG, "showMenu called before Search MenuItem installed.");
Aga Wronska8788dad2016-01-15 17:30:15 -0800141 return;
142 }
143
Aga Wronska8788dad2016-01-15 17:30:15 -0800144 if (!visible) {
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800145 mCurrentSearch = null;
Aga Wronska8788dad2016-01-15 17:30:15 -0800146 }
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800147
Aga Wronska8e21daa2016-03-24 18:22:09 -0700148 mMenuItem.setVisible(visible);
Aga Wronska8788dad2016-01-15 17:30:15 -0800149 }
150
151 /**
152 * Cancels current search operation. Triggers clearing and collapsing the SearchView.
153 *
154 * @return True if it cancels search. False if it does not operate search currently.
155 */
156 boolean cancelSearch() {
157 if (isExpanded() || isSearching()) {
158 // If the query string is not empty search view won't get iconified
Aga Wronska8e21daa2016-03-24 18:22:09 -0700159 mSearchView.setQuery("", false);
160
161 if (mFullBar) {
162 onClose();
163 } else {
164 // Causes calling onClose(). onClose() is triggering directory content update.
165 mSearchView.setIconified(true);
166 }
Aga Wronska8788dad2016-01-15 17:30:15 -0800167 return true;
168 }
169 return false;
170 }
171
Aga Wronska8e21daa2016-03-24 18:22:09 -0700172 /**
173 * Sets search view into the searching state. Used to restore state after device orientation
174 * change.
175 */
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800176 private void restoreSearch() {
177 if (isSearching()) {
Aga Wronska8e21daa2016-03-24 18:22:09 -0700178 if(mFullBar) {
179 mMenuItem.expandActionView();
180 } else {
181 mSearchView.setIconified(false);
182 }
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800183 onSearchExpanded();
Aga Wronska8e21daa2016-03-24 18:22:09 -0700184 mSearchView.setQuery(mCurrentSearch, false);
185 mSearchView.clearFocus();
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800186 }
187 }
188
189 private void onSearchExpanded() {
190 mSearchExpanded = true;
Aga Wronska8e21daa2016-03-24 18:22:09 -0700191 if(mFullBar) {
192 Menu menu = mActionBar.getMenu();
193 menu.setGroupVisible(R.id.group_hide_when_searching, false);
194 }
Garfield, Tan11d23482016-08-05 09:33:29 -0700195
196 mSortModel.setSortEnabled(false);
Aga Wronska8788dad2016-01-15 17:30:15 -0800197 }
198
199 /**
Aga Wronska8e21daa2016-03-24 18:22:09 -0700200 * Clears the search. Triggers refreshing of the directory content.
Aga Wronska8788dad2016-01-15 17:30:15 -0800201 * @return True if the default behavior of clearing/dismissing SearchView should be overridden.
202 * False otherwise.
203 */
204 @Override
205 public boolean onClose() {
206 mSearchExpanded = false;
207 if (mIgnoreNextClose) {
208 mIgnoreNextClose = false;
209 return false;
210 }
211
Aga Wronska8788dad2016-01-15 17:30:15 -0800212 // Refresh the directory if a search was done
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800213 if (mCurrentSearch != null) {
214 mCurrentSearch = null;
Aga Wronska8788dad2016-01-15 17:30:15 -0800215 if (mListener != null) {
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800216 mListener.onSearchChanged(mCurrentSearch);
Aga Wronska8788dad2016-01-15 17:30:15 -0800217 }
218 }
Aga Wronska8e21daa2016-03-24 18:22:09 -0700219
220 if(mFullBar) {
221 mMenuItem.collapseActionView();
222 }
223 mListener.onSearchFinished();
224
Garfield, Tan11d23482016-08-05 09:33:29 -0700225 mSortModel.setSortEnabled(true);
226
Aga Wronska8788dad2016-01-15 17:30:15 -0800227 return false;
228 }
229
230 /**
Aga Wronska8e21daa2016-03-24 18:22:09 -0700231 * Called when owning activity is saving state to be used to restore state during creation.
232 * @param state Bundle to save state too
233 */
234 public void onSaveInstanceState(Bundle state) {
235 state.putString(Shared.EXTRA_QUERY, mCurrentSearch);
236 }
237
238 /**
239 * Sets mSearchExpanded. Called when search icon is clicked to start search for both search view
240 * modes.
Aga Wronska8788dad2016-01-15 17:30:15 -0800241 */
242 @Override
243 public void onClick(View v) {
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800244 onSearchExpanded();
Aga Wronska8788dad2016-01-15 17:30:15 -0800245 }
246
247 @Override
248 public boolean onQueryTextSubmit(String query) {
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800249 mCurrentSearch = query;
Aga Wronska8e21daa2016-03-24 18:22:09 -0700250 mSearchView.clearFocus();
Aga Wronska8788dad2016-01-15 17:30:15 -0800251 if (mListener != null) {
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800252 mListener.onSearchChanged(mCurrentSearch);
Aga Wronska8788dad2016-01-15 17:30:15 -0800253 }
254 return true;
255 }
256
Aga Wronska8e21daa2016-03-24 18:22:09 -0700257 /**
258 * Used to detect and handle back button pressed event when search is expanded.
259 */
Aga Wronska8788dad2016-01-15 17:30:15 -0800260 @Override
261 public void onFocusChange(View v, boolean hasFocus) {
262 if (!hasFocus) {
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800263 if (mCurrentSearch == null) {
Aga Wronska8e21daa2016-03-24 18:22:09 -0700264 mSearchView.setIconified(true);
265 } else if (TextUtils.isEmpty(mSearchView.getQuery())) {
Aga Wronska8788dad2016-01-15 17:30:15 -0800266 cancelSearch();
267 }
268 }
269 }
270
271 @Override
272 public boolean onQueryTextChange(String newText) {
273 return false;
274 }
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800275
Aga Wronska8e21daa2016-03-24 18:22:09 -0700276 @Override
277 public boolean onMenuItemActionCollapse(MenuItem item) {
278 Menu menu = mActionBar.getMenu();
279 menu.setGroupVisible(R.id.group_hide_when_searching, true);
280
281 // Handles case when search view is collapsed by using the arrow on the left of the bar
282 if (isExpanded() || isSearching()) {
283 cancelSearch();
284 return false;
285 }
286 return true;
287 }
288
289 @Override
290 public boolean onMenuItemActionExpand(MenuItem item) {
291 return true;
292 }
293
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800294 String getCurrentSearch() {
295 return mCurrentSearch;
296 }
297
Steve McKay16e0c1f2016-09-15 12:41:13 -0700298 public boolean isSearching() {
Aga Wronska8e21daa2016-03-24 18:22:09 -0700299 return mCurrentSearch != null;
300 }
301
302 boolean isExpanded() {
303 return mSearchExpanded;
304 }
Aga Wronska8788dad2016-01-15 17:30:15 -0800305}