blob: 51db34f38312277246a8eef181ceea26399eed25 [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
Steve McKayd9caa6a2016-09-15 16:36:45 -070019import static com.android.documentsui.base.Shared.DEBUG;
Ben Kwa543a2922016-03-22 11:11:46 -070020
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;
Steve McKayd9caa6a2016-09-15 16:36:45 -070036import com.android.documentsui.base.Shared;
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();
Ben Lin21c54442016-09-21 14:20:12 -070048 void onSearchViewChanged(boolean opened);
Aga Wronska8788dad2016-01-15 17:30:15 -080049 }
50
Ben Lin7c35b032016-05-31 13:24:01 -070051 private static final String TAG = "SearchManager";
Ben Lin21c54442016-09-21 14:20:12 -070052 private final SearchManagerListener mListener;
Aga Wronska8788dad2016-01-15 17:30:15 -080053
Aga Wronska8788dad2016-01-15 17:30:15 -080054 private boolean mSearchExpanded;
Aga Wronskaaf5ace52016-02-17 13:50:42 -080055 private String mCurrentSearch;
Aga Wronska8788dad2016-01-15 17:30:15 -080056 private boolean mIgnoreNextClose;
Aga Wronska8e21daa2016-03-24 18:22:09 -070057 private boolean mFullBar;
Aga Wronska8788dad2016-01-15 17:30:15 -080058
Steve McKay18d01e82016-02-03 11:15:57 -080059 private DocumentsToolbar mActionBar;
Aga Wronska8e21daa2016-03-24 18:22:09 -070060 private MenuItem mMenuItem;
61 private SearchView mSearchView;
Aga Wronska8788dad2016-01-15 17:30:15 -080062
Garfield, Tan11d23482016-08-05 09:33:29 -070063 public SearchViewManager(
Ben Lin21c54442016-09-21 14:20:12 -070064 SearchManagerListener listener,
65 @Nullable Bundle savedState) {
66 assert (listener != null);
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;
Aga Wronska8788dad2016-01-15 17:30:15 -080069 }
70
Aga Wronska8e21daa2016-03-24 18:22:09 -070071 public void install(DocumentsToolbar actionBar, boolean isFullBarSearch) {
Aga Wronska8788dad2016-01-15 17:30:15 -080072 mActionBar = actionBar;
Aga Wronska8e21daa2016-03-24 18:22:09 -070073 mMenuItem = actionBar.getSearchMenu();
74 mSearchView = (SearchView) mMenuItem.getActionView();
Aga Wronska8788dad2016-01-15 17:30:15 -080075
Aga Wronska8e21daa2016-03-24 18:22:09 -070076 mSearchView.setOnQueryTextListener(this);
77 mSearchView.setOnCloseListener(this);
78 mSearchView.setOnSearchClickListener(this);
79 mSearchView.setOnQueryTextFocusChangeListener(this);
80
81 mFullBar = isFullBarSearch;
82 if (mFullBar) {
83 mMenuItem.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW
84 | MenuItem.SHOW_AS_ACTION_ALWAYS);
85 mMenuItem.setOnActionExpandListener(this);
86 }
Aga Wronskaaf5ace52016-02-17 13:50:42 -080087
88 restoreSearch();
Aga Wronska8788dad2016-01-15 17:30:15 -080089 }
90
91 /**
Aga Wronska8e21daa2016-03-24 18:22:09 -070092 * Used to hide menu icons, when the search is being restored. Needed because search restoration
93 * is done before onPrepareOptionsMenu(Menu menu) that is overriding the icons visibility.
94 */
95 public void updateMenu() {
96 if (isSearching() && mFullBar) {
97 Menu menu = mActionBar.getMenu();
98 menu.setGroupVisible(R.id.group_hide_when_searching, false);
99 }
100 }
101
102 /**
Aga Wronska8788dad2016-01-15 17:30:15 -0800103 * @param root Info about the current directory.
104 */
105 void update(RootInfo root) {
Aga Wronska8e21daa2016-03-24 18:22:09 -0700106 if (mMenuItem == null) {
Ben Kwa543a2922016-03-22 11:11:46 -0700107 if (DEBUG) Log.d(TAG, "update called before Search MenuItem installed.");
Aga Wronska8788dad2016-01-15 17:30:15 -0800108 return;
109 }
110
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800111 if (mCurrentSearch != null) {
Aga Wronska8e21daa2016-03-24 18:22:09 -0700112 mMenuItem.expandActionView();
Aga Wronska8788dad2016-01-15 17:30:15 -0800113
Aga Wronska8e21daa2016-03-24 18:22:09 -0700114 mSearchView.setIconified(false);
115 mSearchView.clearFocus();
116 mSearchView.setQuery(mCurrentSearch, false);
Aga Wronska8788dad2016-01-15 17:30:15 -0800117 } else {
Aga Wronska8e21daa2016-03-24 18:22:09 -0700118 mSearchView.clearFocus();
119 if (!mSearchView.isIconified()) {
Aga Wronska8788dad2016-01-15 17:30:15 -0800120 mIgnoreNextClose = true;
Aga Wronska8e21daa2016-03-24 18:22:09 -0700121 mSearchView.setIconified(true);
Aga Wronska8788dad2016-01-15 17:30:15 -0800122 }
123
Aga Wronska8e21daa2016-03-24 18:22:09 -0700124 if (mMenuItem.isActionViewExpanded()) {
125 mMenuItem.collapseActionView();
Aga Wronska8788dad2016-01-15 17:30:15 -0800126 }
127 }
128
129 showMenu(root != null
130 && ((root.flags & Root.FLAG_SUPPORTS_SEARCH) != 0));
131 }
132
Garfield Tan25676032016-09-19 13:52:35 -0700133 public void showMenu(boolean visible) {
Aga Wronska8e21daa2016-03-24 18:22:09 -0700134 if (mMenuItem == null) {
Ben Kwa543a2922016-03-22 11:11:46 -0700135 if (DEBUG) Log.d(TAG, "showMenu called before Search MenuItem installed.");
Aga Wronska8788dad2016-01-15 17:30:15 -0800136 return;
137 }
138
Aga Wronska8788dad2016-01-15 17:30:15 -0800139 if (!visible) {
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800140 mCurrentSearch = null;
Aga Wronska8788dad2016-01-15 17:30:15 -0800141 }
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800142
Aga Wronska8e21daa2016-03-24 18:22:09 -0700143 mMenuItem.setVisible(visible);
Aga Wronska8788dad2016-01-15 17:30:15 -0800144 }
145
146 /**
147 * Cancels current search operation. Triggers clearing and collapsing the SearchView.
148 *
149 * @return True if it cancels search. False if it does not operate search currently.
150 */
151 boolean cancelSearch() {
152 if (isExpanded() || isSearching()) {
153 // If the query string is not empty search view won't get iconified
Aga Wronska8e21daa2016-03-24 18:22:09 -0700154 mSearchView.setQuery("", false);
155
156 if (mFullBar) {
157 onClose();
158 } else {
159 // Causes calling onClose(). onClose() is triggering directory content update.
160 mSearchView.setIconified(true);
161 }
Aga Wronska8788dad2016-01-15 17:30:15 -0800162 return true;
163 }
164 return false;
165 }
166
Aga Wronska8e21daa2016-03-24 18:22:09 -0700167 /**
168 * Sets search view into the searching state. Used to restore state after device orientation
169 * change.
170 */
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800171 private void restoreSearch() {
172 if (isSearching()) {
Aga Wronska8e21daa2016-03-24 18:22:09 -0700173 if(mFullBar) {
174 mMenuItem.expandActionView();
175 } else {
176 mSearchView.setIconified(false);
177 }
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800178 onSearchExpanded();
Aga Wronska8e21daa2016-03-24 18:22:09 -0700179 mSearchView.setQuery(mCurrentSearch, false);
180 mSearchView.clearFocus();
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800181 }
182 }
183
184 private void onSearchExpanded() {
185 mSearchExpanded = true;
Aga Wronska8e21daa2016-03-24 18:22:09 -0700186 if(mFullBar) {
187 Menu menu = mActionBar.getMenu();
188 menu.setGroupVisible(R.id.group_hide_when_searching, false);
189 }
Garfield, Tan11d23482016-08-05 09:33:29 -0700190
Ben Lin21c54442016-09-21 14:20:12 -0700191 mListener.onSearchViewChanged(true);
Aga Wronska8788dad2016-01-15 17:30:15 -0800192 }
193
194 /**
Aga Wronska8e21daa2016-03-24 18:22:09 -0700195 * Clears the search. Triggers refreshing of the directory content.
Aga Wronska8788dad2016-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 Wronska8788dad2016-01-15 17:30:15 -0800207 // Refresh the directory if a search was done
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800208 if (mCurrentSearch != null) {
209 mCurrentSearch = null;
Ben Lin21c54442016-09-21 14:20:12 -0700210 mListener.onSearchChanged(mCurrentSearch);
Aga Wronska8788dad2016-01-15 17:30:15 -0800211 }
Aga Wronska8e21daa2016-03-24 18:22:09 -0700212
213 if(mFullBar) {
214 mMenuItem.collapseActionView();
215 }
216 mListener.onSearchFinished();
217
Ben Lin21c54442016-09-21 14:20:12 -0700218 mListener.onSearchViewChanged(false);
Garfield, Tan11d23482016-08-05 09:33:29 -0700219
Aga Wronska8788dad2016-01-15 17:30:15 -0800220 return false;
221 }
222
223 /**
Aga Wronska8e21daa2016-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 Wronska8788dad2016-01-15 17:30:15 -0800234 */
235 @Override
236 public void onClick(View v) {
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800237 onSearchExpanded();
Aga Wronska8788dad2016-01-15 17:30:15 -0800238 }
239
240 @Override
241 public boolean onQueryTextSubmit(String query) {
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800242 mCurrentSearch = query;
Aga Wronska8e21daa2016-03-24 18:22:09 -0700243 mSearchView.clearFocus();
Ben Lin21c54442016-09-21 14:20:12 -0700244 mListener.onSearchChanged(mCurrentSearch);
Aga Wronska8788dad2016-01-15 17:30:15 -0800245 return true;
246 }
247
Aga Wronska8e21daa2016-03-24 18:22:09 -0700248 /**
249 * Used to detect and handle back button pressed event when search is expanded.
250 */
Aga Wronska8788dad2016-01-15 17:30:15 -0800251 @Override
252 public void onFocusChange(View v, boolean hasFocus) {
253 if (!hasFocus) {
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800254 if (mCurrentSearch == null) {
Aga Wronska8e21daa2016-03-24 18:22:09 -0700255 mSearchView.setIconified(true);
256 } else if (TextUtils.isEmpty(mSearchView.getQuery())) {
Aga Wronska8788dad2016-01-15 17:30:15 -0800257 cancelSearch();
258 }
259 }
260 }
261
262 @Override
263 public boolean onQueryTextChange(String newText) {
264 return false;
265 }
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800266
Aga Wronska8e21daa2016-03-24 18:22:09 -0700267 @Override
268 public boolean onMenuItemActionCollapse(MenuItem item) {
269 Menu menu = mActionBar.getMenu();
270 menu.setGroupVisible(R.id.group_hide_when_searching, true);
271
272 // Handles case when search view is collapsed by using the arrow on the left of the bar
273 if (isExpanded() || isSearching()) {
274 cancelSearch();
275 return false;
276 }
277 return true;
278 }
279
280 @Override
281 public boolean onMenuItemActionExpand(MenuItem item) {
282 return true;
283 }
284
Aga Wronskaaf5ace52016-02-17 13:50:42 -0800285 String getCurrentSearch() {
286 return mCurrentSearch;
287 }
288
Steve McKay16e0c1f2016-09-15 12:41:13 -0700289 public boolean isSearching() {
Aga Wronska8e21daa2016-03-24 18:22:09 -0700290 return mCurrentSearch != null;
291 }
292
293 boolean isExpanded() {
294 return mSearchExpanded;
295 }
Aga Wronska8788dad2016-01-15 17:30:15 -0800296}