blob: 8493fb25b8a5f7f8bf66c11dc4d69a0dfca0a90b [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2008 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 android.app;
18
Amith Yamasani479ae0a2010-02-04 14:52:32 -080019
Mathew Inwood61e8ae62018-08-14 14:17:44 +010020import android.annotation.UnsupportedAppUsage;
Amith Yamasanid25eb352010-03-10 21:09:28 -080021import android.content.BroadcastReceiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.content.ComponentName;
Bjorn Bringert444c7272009-07-06 21:32:50 +010023import android.content.Context;
24import android.content.Intent;
Amith Yamasanid25eb352010-03-10 21:09:28 -080025import android.content.IntentFilter;
Karl Rosaen875d50a2009-04-23 19:00:21 -070026import android.content.pm.ActivityInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.content.pm.PackageManager;
Bjorn Bringertc1f40962009-04-29 13:08:39 +010028import android.content.pm.PackageManager.NameNotFoundException;
Amith Yamasanid25eb352010-03-10 21:09:28 -080029import android.content.res.Configuration;
Bjorn Bringert444c7272009-07-06 21:32:50 +010030import android.graphics.drawable.Drawable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.net.Uri;
32import android.os.Bundle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.speech.RecognizerIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.text.InputType;
35import android.text.TextUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.util.AttributeSet;
37import android.util.Log;
Adam Powell8abebcd2011-05-19 18:46:47 -070038import android.util.TypedValue;
39import android.view.ActionMode;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.view.Gravity;
41import android.view.KeyEvent;
Karl Rosaen875d50a2009-04-23 19:00:21 -070042import android.view.MotionEvent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.view.View;
Karl Rosaen875d50a2009-04-23 19:00:21 -070044import android.view.ViewConfiguration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import android.view.ViewGroup;
46import android.view.Window;
47import android.view.WindowManager;
Dianne Hackborna8f556e2009-03-24 20:47:50 -070048import android.view.inputmethod.InputMethodManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.widget.AutoCompleteTextView;
Ralston Da Silva13b2b972019-02-14 15:43:23 -080050import android.widget.Filterable;
Bjorn Bringertda5c1bd2010-02-04 15:11:25 +000051import android.widget.ImageView;
Bjorn Bringert077357c2009-09-23 13:57:24 +010052import android.widget.LinearLayout;
Ralston Da Silva13b2b972019-02-14 15:43:23 -080053import android.widget.ListPopupWindow;
Amith Yamasani968ec932010-12-02 14:00:47 -080054import android.widget.SearchView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import android.widget.TextView;
Amith Yamasani968ec932010-12-02 14:00:47 -080056
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057/**
Amith Yamasanie9ce3f02010-01-25 09:15:50 -080058 * Search dialog. This is controlled by the
59 * SearchManager and runs in the current foreground process.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060 *
61 * @hide
62 */
Amith Yamasani968ec932010-12-02 14:00:47 -080063public class SearchDialog extends Dialog {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064
65 // Debugging support
Karl Rosaen875d50a2009-04-23 19:00:21 -070066 private static final boolean DBG = false;
67 private static final String LOG_TAG = "SearchDialog";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069 private static final String INSTANCE_KEY_COMPONENT = "comp";
70 private static final String INSTANCE_KEY_APPDATA = "data";
Bjorn Bringertb0ae27f2009-06-23 13:47:31 +010071 private static final String INSTANCE_KEY_USER_QUERY = "uQry";
Amith Yamasani968ec932010-12-02 14:00:47 -080072
Mike LeBeau7df84612009-10-30 16:11:40 -070073 // The string used for privateImeOptions to identify to the IME that it should not show
74 // a microphone button since one already exists in the search dialog.
75 private static final String IME_OPTION_NO_MICROPHONE = "nm";
Bjorn Bringertb0ae27f2009-06-23 13:47:31 +010076
Mike LeBeau1fd73232009-04-27 19:12:05 -070077 private static final int SEARCH_PLATE_LEFT_PADDING_NON_GLOBAL = 7;
Bjorn Bringert444c7272009-07-06 21:32:50 +010078
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079 // views & widgets
80 private TextView mBadgeLabel;
Bjorn Bringertda5c1bd2010-02-04 15:11:25 +000081 private ImageView mAppIcon;
Amith Yamasani968ec932010-12-02 14:00:47 -080082 private AutoCompleteTextView mSearchAutoComplete;
Karl Rosaen875d50a2009-04-23 19:00:21 -070083 private View mSearchPlate;
Amith Yamasani968ec932010-12-02 14:00:47 -080084 private SearchView mSearchView;
Romain Guyf4f70462009-06-26 16:55:54 -070085 private Drawable mWorkingSpinner;
Amith Yamasani24652982011-06-23 16:16:05 -070086 private View mCloseSearch;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087
88 // interaction with searchable application
Karl Rosaen875d50a2009-04-23 19:00:21 -070089 private SearchableInfo mSearchable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090 private ComponentName mLaunchComponent;
91 private Bundle mAppSearchData;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092 private Context mActivityContext;
93
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094 // For voice searching
Bryan Mawhinneyf36740b2009-09-30 15:20:22 +010095 private final Intent mVoiceWebSearchIntent;
96 private final Intent mVoiceAppSearchIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097
Karl Rosaen875d50a2009-04-23 19:00:21 -070098 // The query entered by the user. This is not changed when selecting a suggestion
99 // that modifies the contents of the text field. But if the user then edits
100 // the suggestion, the resulting string is saved.
101 private String mUserQuery;
Satish Sampath662df0b2009-06-22 23:16:07 +0100102
103 // Last known IME options value for the search edit text.
104 private int mSearchAutoCompleteImeOptions;
105
Amith Yamasani49860442010-03-17 20:54:10 -0700106 private BroadcastReceiver mConfChangeListener = new BroadcastReceiver() {
107 @Override
108 public void onReceive(Context context, Intent intent) {
109 if (intent.getAction().equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
110 onConfigurationChanged();
111 }
112 }
113 };
114
Adam Powell8abebcd2011-05-19 18:46:47 -0700115 static int resolveDialogTheme(Context context) {
116 TypedValue outValue = new TypedValue();
117 context.getTheme().resolveAttribute(com.android.internal.R.attr.searchDialogTheme,
118 outValue, true);
119 return outValue.resourceId;
120 }
121
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122 /**
123 * Constructor - fires it up and makes it look like the search UI.
124 *
125 * @param context Application Context we can use for system acess
126 */
Amith Yamasanie9ce3f02010-01-25 09:15:50 -0800127 public SearchDialog(Context context, SearchManager searchManager) {
Adam Powell8abebcd2011-05-19 18:46:47 -0700128 super(context, resolveDialogTheme(context));
Bryan Mawhinneyf36740b2009-09-30 15:20:22 +0100129
130 // Save voice intent for later queries/launching
131 mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
132 mVoiceWebSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
133 mVoiceWebSearchIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
134 RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
135
136 mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
137 mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138 }
139
140 /**
Bryan Mawhinneyf36740b2009-09-30 15:20:22 +0100141 * Create the search dialog and any resources that are used for the
142 * entire lifetime of the dialog.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143 */
144 @Override
145 protected void onCreate(Bundle savedInstanceState) {
146 super.onCreate(savedInstanceState);
147
Bjorn Bringert8d17f3f2009-06-05 13:22:28 +0100148 Window theWindow = getWindow();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149 WindowManager.LayoutParams lp = theWindow.getAttributes();
Romain Guy980a9382010-01-08 15:06:28 -0800150 lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
Bjorn Bringert8d17f3f2009-06-05 13:22:28 +0100151 // taking up the whole window (even when transparent) is less than ideal,
152 // but necessary to show the popup window until the window manager supports
153 // having windows anchored by their parent but not clipped by them.
Romain Guy980a9382010-01-08 15:06:28 -0800154 lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
Bjorn Bringert8d17f3f2009-06-05 13:22:28 +0100155 lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
Mike LeBeau98acd542009-05-07 19:04:39 -0700156 lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800157 theWindow.setAttributes(lp);
158
Bryan Mawhinneyf36740b2009-09-30 15:20:22 +0100159 // Touching outside of the search dialog will dismiss it
160 setCanceledOnTouchOutside(true);
161 }
162
163 /**
164 * We recreate the dialog view each time it becomes visible so as to limit
165 * the scope of any problems with the contained resources.
166 */
167 private void createContentView() {
168 setContentView(com.android.internal.R.layout.search_bar);
169
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 // get the view elements for local access
Alan Viverette51efddb2017-04-05 10:00:01 -0400171 mSearchView = findViewById(com.android.internal.R.id.search_view);
Amith Yamasanib4569fb2011-07-08 15:25:39 -0700172 mSearchView.setIconified(false);
Amith Yamasani968ec932010-12-02 14:00:47 -0800173 mSearchView.setOnCloseListener(mOnCloseListener);
Adam Powell01f21352011-01-20 18:30:10 -0800174 mSearchView.setOnQueryTextListener(mOnQueryChangeListener);
175 mSearchView.setOnSuggestionListener(mOnSuggestionSelectionListener);
Amith Yamasani6a7421b2011-07-27 11:55:53 -0700176 mSearchView.onActionViewExpanded();
Bjorn Bringert077357c2009-09-23 13:57:24 +0100177
Amith Yamasani24652982011-06-23 16:16:05 -0700178 mCloseSearch = findViewById(com.android.internal.R.id.closeButton);
179 mCloseSearch.setOnClickListener(new View.OnClickListener() {
180 @Override
181 public void onClick(View v) {
182 dismiss();
183 }
184 });
185
Amith Yamasani968ec932010-12-02 14:00:47 -0800186 // TODO: Move the badge logic to SearchView or move the badge to search_bar.xml
187 mBadgeLabel = (TextView) mSearchView.findViewById(com.android.internal.R.id.search_badge);
188 mSearchAutoComplete = (AutoCompleteTextView)
189 mSearchView.findViewById(com.android.internal.R.id.search_src_text);
Alan Viverette51efddb2017-04-05 10:00:01 -0400190 mAppIcon = findViewById(com.android.internal.R.id.search_app_icon);
Amith Yamasani968ec932010-12-02 14:00:47 -0800191 mSearchPlate = mSearchView.findViewById(com.android.internal.R.id.search_plate);
Alan Viverette8eea3ea2014-02-03 18:40:20 -0800192 mWorkingSpinner = getContext().getDrawable(com.android.internal.R.drawable.search_spinner);
Amith Yamasania40d7ec2011-07-01 13:50:04 -0700193 // TODO: Restore the spinner for slow suggestion lookups
194 // mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds(
195 // null, null, mWorkingSpinner, null);
Bryan Mawhinneydfcbc042009-09-17 21:31:16 +0100196 setWorking(false);
197
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 // pre-hide all the extraneous elements
199 mBadgeLabel.setVisibility(View.GONE);
200
201 // Additional adjustments to make Dialog work for Search
Satish Sampath662df0b2009-06-22 23:16:07 +0100202 mSearchAutoCompleteImeOptions = mSearchAutoComplete.getImeOptions();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 }
204
205 /**
206 * Set up the search dialog
207 *
Karl Rosaen875d50a2009-04-23 19:00:21 -0700208 * @return true if search dialog launched, false if not
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 */
210 public boolean show(String initialQuery, boolean selectInitialQuery,
Amith Yamasani479ae0a2010-02-04 14:52:32 -0800211 ComponentName componentName, Bundle appSearchData) {
212 boolean success = doShow(initialQuery, selectInitialQuery, componentName, appSearchData);
Satish Sampathfef8d3e2009-07-01 17:48:42 +0100213 if (success) {
214 // Display the drop down as soon as possible instead of waiting for the rest of the
215 // pending UI stuff to get done, so that things appear faster to the user.
216 mSearchAutoComplete.showDropDownAfterLayout();
217 }
218 return success;
Mike LeBeaub3aab692009-04-30 02:09:09 -0700219 }
Bjorn Bringertda1f4b62009-07-28 15:15:26 +0100220
Mike LeBeaub3aab692009-04-30 02:09:09 -0700221 /**
Amith Yamasani479ae0a2010-02-04 14:52:32 -0800222 * Does the rest of the work required to show the search dialog. Called by
223 * {@link #show(String, boolean, ComponentName, Bundle)} and
224 *
Mike LeBeaub3aab692009-04-30 02:09:09 -0700225 * @return true if search dialog showed, false if not
226 */
227 private boolean doShow(String initialQuery, boolean selectInitialQuery,
Amith Yamasani479ae0a2010-02-04 14:52:32 -0800228 ComponentName componentName, Bundle appSearchData) {
Karl Rosaen875d50a2009-04-23 19:00:21 -0700229 // set up the searchable and show the dialog
Amith Yamasani479ae0a2010-02-04 14:52:32 -0800230 if (!show(componentName, appSearchData)) {
Karl Rosaen875d50a2009-04-23 19:00:21 -0700231 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800232 }
Mike LeBeaub3aab692009-04-30 02:09:09 -0700233
Karl Rosaen875d50a2009-04-23 19:00:21 -0700234 // finally, load the user's initial text (which may trigger suggestions)
235 setUserQuery(initialQuery);
236 if (selectInitialQuery) {
237 mSearchAutoComplete.selectAll();
238 }
Mike LeBeaub3aab692009-04-30 02:09:09 -0700239
Karl Rosaen875d50a2009-04-23 19:00:21 -0700240 return true;
241 }
Mike LeBeaub3aab692009-04-30 02:09:09 -0700242
Karl Rosaen875d50a2009-04-23 19:00:21 -0700243 /**
244 * Sets up the search dialog and shows it.
245 *
246 * @return <code>true</code> if search dialog launched
247 */
Amith Yamasani479ae0a2010-02-04 14:52:32 -0800248 private boolean show(ComponentName componentName, Bundle appSearchData) {
Karl Rosaen875d50a2009-04-23 19:00:21 -0700249
250 if (DBG) {
251 Log.d(LOG_TAG, "show(" + componentName + ", "
Amith Yamasani479ae0a2010-02-04 14:52:32 -0800252 + appSearchData + ")");
Karl Rosaen875d50a2009-04-23 19:00:21 -0700253 }
254
Bjorn Bringert8d153822009-06-22 10:31:44 +0100255 SearchManager searchManager = (SearchManager)
256 mContext.getSystemService(Context.SEARCH_SERVICE);
Amith Yamasani479ae0a2010-02-04 14:52:32 -0800257 // Try to get the searchable info for the provided component.
Bjorn Bringert6cf7a322010-02-23 13:17:06 +0000258 mSearchable = searchManager.getSearchableInfo(componentName);
Bjorn Bringertee716fa2009-07-16 09:15:37 +0100259
Bjorn Bringertee716fa2009-07-16 09:15:37 +0100260 if (mSearchable == null) {
Bjorn Bringertee716fa2009-07-16 09:15:37 +0100261 return false;
262 }
263
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800264 mLaunchComponent = componentName;
265 mAppSearchData = appSearchData;
Karl Rosaen875d50a2009-04-23 19:00:21 -0700266 mActivityContext = mSearchable.getActivityContext(getContext());
Bryan Mawhinneyf36740b2009-09-30 15:20:22 +0100267
Karl Rosaen875d50a2009-04-23 19:00:21 -0700268 // show the dialog. this will call onStart().
Bjorn Bringert11f09bb2009-10-05 21:00:53 +0100269 if (!isShowing()) {
270 // Recreate the search bar view every time the dialog is shown, to get rid
271 // of any bad state in the AutoCompleteTextView etc
272 createContentView();
Amith Yamasani968ec932010-12-02 14:00:47 -0800273 mSearchView.setSearchableInfo(mSearchable);
Amith Yamasani940ef382011-03-02 18:43:23 -0800274 mSearchView.setAppSearchData(mAppSearchData);
Bjorn Bringert11f09bb2009-10-05 21:00:53 +0100275
Karl Rosaen875d50a2009-04-23 19:00:21 -0700276 show();
277 }
Karl Rosaen875d50a2009-04-23 19:00:21 -0700278 updateUI();
Amith Yamasani49860442010-03-17 20:54:10 -0700279
Karl Rosaen875d50a2009-04-23 19:00:21 -0700280 return true;
281 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282
Amith Yamasani49860442010-03-17 20:54:10 -0700283 @Override
284 public void onStart() {
285 super.onStart();
286
287 // Register a listener for configuration change events.
288 IntentFilter filter = new IntentFilter();
289 filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
290 getContext().registerReceiver(mConfChangeListener, filter);
291 }
292
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293 /**
294 * The search dialog is being dismissed, so handle all of the local shutdown operations.
295 *
296 * This function is designed to be idempotent so that dismiss() can be safely called at any time
297 * (even if already closed) and more likely to really dump any memory. No leaks!
298 */
299 @Override
300 public void onStop() {
301 super.onStop();
Bjorn Bringert444c7272009-07-06 21:32:50 +0100302
Amith Yamasani49860442010-03-17 20:54:10 -0700303 getContext().unregisterReceiver(mConfChangeListener);
304
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800305 // dump extra memory we're hanging on to
306 mLaunchComponent = null;
307 mAppSearchData = null;
308 mSearchable = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309 mUserQuery = null;
Karl Rosaen875d50a2009-04-23 19:00:21 -0700310 }
Mike LeBeau1c690752009-05-20 20:20:26 -0700311
Mike LeBeau1c690752009-05-20 20:20:26 -0700312 /**
Mike LeBeau1480eb22009-05-20 17:22:13 -0700313 * Sets the search dialog to the 'working' state, which shows a working spinner in the
314 * right hand size of the text field.
315 *
316 * @param working true to show spinner, false to hide spinner
317 */
Mathew Inwood61e8ae62018-08-14 14:17:44 +0100318 @UnsupportedAppUsage
Mike LeBeau1480eb22009-05-20 17:22:13 -0700319 public void setWorking(boolean working) {
Bryan Mawhinneydfcbc042009-09-17 21:31:16 +0100320 mWorkingSpinner.setAlpha(working ? 255 : 0);
321 mWorkingSpinner.setVisible(working, false);
322 mWorkingSpinner.invalidateSelf();
Mike LeBeau1480eb22009-05-20 17:22:13 -0700323 }
Amith Yamasani479ae0a2010-02-04 14:52:32 -0800324
Mike LeBeau1480eb22009-05-20 17:22:13 -0700325 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800326 * Save the minimal set of data necessary to recreate the search
327 *
Bjorn Bringert444c7272009-07-06 21:32:50 +0100328 * @return A bundle with the state of the dialog, or {@code null} if the search
329 * dialog is not showing.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 */
331 @Override
332 public Bundle onSaveInstanceState() {
Bjorn Bringert444c7272009-07-06 21:32:50 +0100333 if (!isShowing()) return null;
334
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800335 Bundle bundle = new Bundle();
Bjorn Bringert444c7272009-07-06 21:32:50 +0100336
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337 // setup info so I can recreate this particular search
338 bundle.putParcelable(INSTANCE_KEY_COMPONENT, mLaunchComponent);
339 bundle.putBundle(INSTANCE_KEY_APPDATA, mAppSearchData);
Bjorn Bringertb0ae27f2009-06-23 13:47:31 +0100340 bundle.putString(INSTANCE_KEY_USER_QUERY, mUserQuery);
341
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800342 return bundle;
343 }
344
345 /**
346 * Restore the state of the dialog from a previously saved bundle.
Karl Rosaen875d50a2009-04-23 19:00:21 -0700347 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348 * @param savedInstanceState The state of the dialog previously saved by
349 * {@link #onSaveInstanceState()}.
350 */
351 @Override
352 public void onRestoreInstanceState(Bundle savedInstanceState) {
Bjorn Bringert444c7272009-07-06 21:32:50 +0100353 if (savedInstanceState == null) return;
354
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800355 ComponentName launchComponent = savedInstanceState.getParcelable(INSTANCE_KEY_COMPONENT);
356 Bundle appSearchData = savedInstanceState.getBundle(INSTANCE_KEY_APPDATA);
Bjorn Bringertb0ae27f2009-06-23 13:47:31 +0100357 String userQuery = savedInstanceState.getString(INSTANCE_KEY_USER_QUERY);
358
Bjorn Bringertb0ae27f2009-06-23 13:47:31 +0100359 // show the dialog.
Amith Yamasani479ae0a2010-02-04 14:52:32 -0800360 if (!doShow(userQuery, false, launchComponent, appSearchData)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800361 // for some reason, we couldn't re-instantiate
362 return;
363 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 }
Amith Yamasani968ec932010-12-02 14:00:47 -0800365
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800366 /**
Karl Rosaen875d50a2009-04-23 19:00:21 -0700367 * Called after resources have changed, e.g. after screen rotation or locale change.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800368 */
Bjorn Bringert444c7272009-07-06 21:32:50 +0100369 public void onConfigurationChanged() {
Amith Yamasani479ae0a2010-02-04 14:52:32 -0800370 if (mSearchable != null && isShowing()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800371 // Redraw (resources may have changed)
Mike LeBeau1fd73232009-04-27 19:12:05 -0700372 updateSearchAppIcon();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800373 updateSearchBadge();
Amith Yamasanid25eb352010-03-10 21:09:28 -0800374 if (isLandscapeMode(getContext())) {
Ralston Da Silva13b2b972019-02-14 15:43:23 -0800375 mSearchAutoComplete.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NEEDED);
376 if (mSearchAutoComplete.isDropDownAlwaysVisible() || enoughToFilter()) {
377 mSearchAutoComplete.showDropDown();
378 }
Amith Yamasanid25eb352010-03-10 21:09:28 -0800379 }
Amith Yamasanid25eb352010-03-10 21:09:28 -0800380 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800381 }
Amith Yamasanid25eb352010-03-10 21:09:28 -0800382
Mathew Inwood61e8ae62018-08-14 14:17:44 +0100383 @UnsupportedAppUsage
Amith Yamasanid25eb352010-03-10 21:09:28 -0800384 static boolean isLandscapeMode(Context context) {
385 return context.getResources().getConfiguration().orientation
386 == Configuration.ORIENTATION_LANDSCAPE;
387 }
388
Ralston Da Silva13b2b972019-02-14 15:43:23 -0800389 private boolean enoughToFilter() {
390 Filterable filterableAdapter = (Filterable) mSearchAutoComplete.getAdapter();
391 if (filterableAdapter == null || filterableAdapter.getFilter() == null) {
392 return false;
393 }
394
395 return mSearchAutoComplete.enoughToFilter();
396 }
397
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 /**
Karl Rosaen875d50a2009-04-23 19:00:21 -0700399 * Update the UI according to the info in the current value of {@link #mSearchable}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 */
Karl Rosaen875d50a2009-04-23 19:00:21 -0700401 private void updateUI() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402 if (mSearchable != null) {
Karl Rosaenea52d292009-07-20 09:26:10 -0700403 mDecor.setVisibility(View.VISIBLE);
Karl Rosaen875d50a2009-04-23 19:00:21 -0700404 updateSearchAutoComplete();
Mike LeBeau1fd73232009-04-27 19:12:05 -0700405 updateSearchAppIcon();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800406 updateSearchBadge();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800407
408 // In order to properly configure the input method (if one is being used), we
409 // need to let it know if we'll be providing suggestions. Although it would be
410 // difficult/expensive to know if every last detail has been configured properly, we
411 // can at least see if a suggestions provider has been configured, and use that
412 // as our trigger.
413 int inputType = mSearchable.getInputType();
414 // We only touch this if the input type is set up for text (which it almost certainly
415 // should be, in the case of search!)
416 if ((inputType & InputType.TYPE_MASK_CLASS) == InputType.TYPE_CLASS_TEXT) {
417 // The existence of a suggestions authority is the proxy for "suggestions
418 // are available here"
419 inputType &= ~InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE;
420 if (mSearchable.getSuggestAuthority() != null) {
421 inputType |= InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE;
422 }
423 }
Karl Rosaen875d50a2009-04-23 19:00:21 -0700424 mSearchAutoComplete.setInputType(inputType);
Satish Sampath662df0b2009-06-22 23:16:07 +0100425 mSearchAutoCompleteImeOptions = mSearchable.getImeOptions();
426 mSearchAutoComplete.setImeOptions(mSearchAutoCompleteImeOptions);
Mike LeBeau7df84612009-10-30 16:11:40 -0700427
428 // If the search dialog is going to show a voice search button, then don't let
429 // the soft keyboard display a microphone button if it would have otherwise.
430 if (mSearchable.getVoiceSearchEnabled()) {
431 mSearchAutoComplete.setPrivateImeOptions(IME_OPTION_NO_MICROPHONE);
432 } else {
433 mSearchAutoComplete.setPrivateImeOptions(null);
434 }
Karl Rosaen875d50a2009-04-23 19:00:21 -0700435 }
436 }
437
438 /**
439 * Updates the auto-complete text view.
440 */
441 private void updateSearchAutoComplete() {
Bjorn Bringert8d17f3f2009-06-05 13:22:28 +0100442 // we dismiss the entire dialog instead
443 mSearchAutoComplete.setDropDownDismissedOnCompletion(false);
Amith Yamasani968ec932010-12-02 14:00:47 -0800444 mSearchAutoComplete.setForceIgnoreOutsideTouch(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800445 }
446
Mike LeBeau1fd73232009-04-27 19:12:05 -0700447 private void updateSearchAppIcon() {
Amith Yamasani968ec932010-12-02 14:00:47 -0800448 PackageManager pm = getContext().getPackageManager();
449 Drawable icon;
450 try {
451 ActivityInfo info = pm.getActivityInfo(mLaunchComponent, 0);
452 icon = pm.getApplicationIcon(info.applicationInfo);
453 if (DBG)
454 Log.d(LOG_TAG, "Using app-specific icon");
455 } catch (NameNotFoundException e) {
456 icon = pm.getDefaultActivityIcon();
457 Log.w(LOG_TAG, mLaunchComponent + " not found, using generic app icon");
Mike LeBeau1fd73232009-04-27 19:12:05 -0700458 }
Amith Yamasani968ec932010-12-02 14:00:47 -0800459 mAppIcon.setImageDrawable(icon);
460 mAppIcon.setVisibility(View.VISIBLE);
461 mSearchPlate.setPadding(SEARCH_PLATE_LEFT_PADDING_NON_GLOBAL, mSearchPlate.getPaddingTop(), mSearchPlate.getPaddingRight(), mSearchPlate.getPaddingBottom());
Mike LeBeau1fd73232009-04-27 19:12:05 -0700462 }
463
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800464 /**
Karl Rosaen875d50a2009-04-23 19:00:21 -0700465 * Setup the search "Badge" if requested by mode flags.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800466 */
467 private void updateSearchBadge() {
468 // assume both hidden
469 int visibility = View.GONE;
470 Drawable icon = null;
Karl Rosaen875d50a2009-04-23 19:00:21 -0700471 CharSequence text = null;
Amith Yamasani968ec932010-12-02 14:00:47 -0800472
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800473 // optionally show one or the other.
Bjorn Bringerta9204132009-05-05 14:06:35 +0100474 if (mSearchable.useBadgeIcon()) {
Alan Viverette8eea3ea2014-02-03 18:40:20 -0800475 icon = mActivityContext.getDrawable(mSearchable.getIconId());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 visibility = View.VISIBLE;
Karl Rosaen875d50a2009-04-23 19:00:21 -0700477 if (DBG) Log.d(LOG_TAG, "Using badge icon: " + mSearchable.getIconId());
Bjorn Bringerta9204132009-05-05 14:06:35 +0100478 } else if (mSearchable.useBadgeLabel()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479 text = mActivityContext.getResources().getText(mSearchable.getLabelId()).toString();
480 visibility = View.VISIBLE;
Karl Rosaen875d50a2009-04-23 19:00:21 -0700481 if (DBG) Log.d(LOG_TAG, "Using badge label: " + mSearchable.getLabelId());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800482 }
Amith Yamasani968ec932010-12-02 14:00:47 -0800483
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800484 mBadgeLabel.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
485 mBadgeLabel.setText(text);
486 mBadgeLabel.setVisibility(visibility);
487 }
488
Amith Yamasani479ae0a2010-02-04 14:52:32 -0800489 /*
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490 * Listeners of various types
491 */
492
493 /**
Karl Rosaen875d50a2009-04-23 19:00:21 -0700494 * {@link Dialog#onTouchEvent(MotionEvent)} will cancel the dialog only when the
495 * touch is outside the window. But the window includes space for the drop-down,
496 * so we also cancel on taps outside the search bar when the drop-down is not showing.
497 */
498 @Override
499 public boolean onTouchEvent(MotionEvent event) {
500 // cancel if the drop-down is not showing and the touch event was outside the search plate
501 if (!mSearchAutoComplete.isPopupShowing() && isOutOfBounds(mSearchPlate, event)) {
502 if (DBG) Log.d(LOG_TAG, "Pop-up not showing and outside of search plate.");
503 cancel();
504 return true;
505 }
506 // Let Dialog handle events outside the window while the pop-up is showing.
507 return super.onTouchEvent(event);
508 }
Amith Yamasani968ec932010-12-02 14:00:47 -0800509
Karl Rosaen875d50a2009-04-23 19:00:21 -0700510 private boolean isOutOfBounds(View v, MotionEvent event) {
511 final int x = (int) event.getX();
512 final int y = (int) event.getY();
513 final int slop = ViewConfiguration.get(mContext).getScaledWindowTouchSlop();
514 return (x < -slop) || (y < -slop)
515 || (x > (v.getWidth()+slop))
516 || (y > (v.getHeight()+slop));
517 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800518
Dianne Hackborna8f556e2009-03-24 20:47:50 -0700519 @Override
Karl Rosaenea52d292009-07-20 09:26:10 -0700520 public void hide() {
Bjorn Bringert444c7272009-07-06 21:32:50 +0100521 if (!isShowing()) return;
522
Dianne Hackborna8f556e2009-03-24 20:47:50 -0700523 // We made sure the IME was displayed, so also make sure it is closed
524 // when we go away.
Yohei Yukawa777ef952015-11-25 20:32:24 -0800525 InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
Dianne Hackborna8f556e2009-03-24 20:47:50 -0700526 if (imm != null) {
527 imm.hideSoftInputFromWindow(
528 getWindow().getDecorView().getWindowToken(), 0);
529 }
Karl Rosaenea52d292009-07-20 09:26:10 -0700530
531 super.hide();
Dianne Hackborna8f556e2009-03-24 20:47:50 -0700532 }
Karl Rosaen875d50a2009-04-23 19:00:21 -0700533
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800534 /**
Karl Rosaen875d50a2009-04-23 19:00:21 -0700535 * Launch a search for the text in the query text field.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536 */
Mathew Inwood61e8ae62018-08-14 14:17:44 +0100537 @UnsupportedAppUsage
Amith Yamasani968ec932010-12-02 14:00:47 -0800538 public void launchQuerySearch() {
Karl Rosaen875d50a2009-04-23 19:00:21 -0700539 launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800540 }
541
542 /**
Karl Rosaen875d50a2009-04-23 19:00:21 -0700543 * Launch a search for the text in the query text field.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800544 *
Karl Rosaen875d50a2009-04-23 19:00:21 -0700545 * @param actionKey The key code of the action key that was pressed,
546 * or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
547 * @param actionMsg The message for the action key that was pressed,
548 * or <code>null</code> if none.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800549 */
Mathew Inwood61e8ae62018-08-14 14:17:44 +0100550 @UnsupportedAppUsage
Amith Yamasani968ec932010-12-02 14:00:47 -0800551 protected void launchQuerySearch(int actionKey, String actionMsg) {
Karl Rosaen875d50a2009-04-23 19:00:21 -0700552 String query = mSearchAutoComplete.getText().toString();
Amith Yamasani479ae0a2010-02-04 14:52:32 -0800553 String action = Intent.ACTION_SEARCH;
Amith Yamasani968ec932010-12-02 14:00:47 -0800554 Intent intent = createIntent(action, null, null, query, actionKey, actionMsg);
Karl Rosaen875d50a2009-04-23 19:00:21 -0700555 launchIntent(intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800556 }
Karl Rosaena058f022009-06-01 23:11:44 +0100557
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800558 /**
Amith Yamasanie9ce3f02010-01-25 09:15:50 -0800559 * Launches an intent, including any special intent handling.
Karl Rosaen875d50a2009-04-23 19:00:21 -0700560 */
561 private void launchIntent(Intent intent) {
562 if (intent == null) {
563 return;
564 }
Karl Rosaenea52d292009-07-20 09:26:10 -0700565 Log.d(LOG_TAG, "launching " + intent);
Bjorn Bringert4899e382009-07-22 10:25:25 +0100566 try {
Amith Yamasani479ae0a2010-02-04 14:52:32 -0800567 // If the intent was created from a suggestion, it will always have an explicit
568 // component here.
Amith Yamasani479ae0a2010-02-04 14:52:32 -0800569 getContext().startActivity(intent);
570 // If the search switches to a different activity,
571 // SearchDialogWrapper#performActivityResuming
572 // will handle hiding the dialog when the next activity starts, but for
573 // real in-app search, we still need to dismiss the dialog.
574 dismiss();
Bjorn Bringert4899e382009-07-22 10:25:25 +0100575 } catch (RuntimeException ex) {
576 Log.e(LOG_TAG, "Failed launch activity: " + intent, ex);
Karl Rosaen876627d2009-07-20 14:30:55 -0700577 }
Karl Rosaen875d50a2009-04-23 19:00:21 -0700578 }
Bjorn Bringert4899e382009-07-22 10:25:25 +0100579
Bjorn Bringert4899e382009-07-22 10:25:25 +0100580 /**
Mike LeBeauae9760b2009-06-01 21:53:09 +0100581 * Sets the list item selection in the AutoCompleteTextView's ListView.
582 */
583 public void setListSelection(int index) {
584 mSearchAutoComplete.setListSelection(index);
585 }
Karl Rosaena058f022009-06-01 23:11:44 +0100586
Mike LeBeauae9760b2009-06-01 21:53:09 +0100587 /**
Karl Rosaen875d50a2009-04-23 19:00:21 -0700588 * Constructs an intent from the given information and the search dialog state.
589 *
590 * @param action Intent action.
591 * @param data Intent data, or <code>null</code>.
Karl Rosaen875d50a2009-04-23 19:00:21 -0700592 * @param extraData Data for {@link SearchManager#EXTRA_DATA_KEY} or <code>null</code>.
Satish Sampathbf23fe02009-06-15 23:47:56 +0100593 * @param query Intent query, or <code>null</code>.
Karl Rosaen875d50a2009-04-23 19:00:21 -0700594 * @param actionKey The key code of the action key that was pressed,
595 * or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
596 * @param actionMsg The message for the action key that was pressed,
597 * or <code>null</code> if none.
Bjorn Bringertbe5b73c2009-09-21 23:05:23 +0100598 * @param mode The search mode, one of the acceptable values for
599 * {@link SearchManager#SEARCH_MODE}, or {@code null}.
Karl Rosaen875d50a2009-04-23 19:00:21 -0700600 * @return The intent.
601 */
Satish Sampathbf23fe02009-06-15 23:47:56 +0100602 private Intent createIntent(String action, Uri data, String extraData, String query,
Amith Yamasanie678f462010-09-15 16:13:43 -0700603 int actionKey, String actionMsg) {
Karl Rosaen875d50a2009-04-23 19:00:21 -0700604 // Now build the Intent
605 Intent intent = new Intent(action);
Bjorn Bringert8d17f3f2009-06-05 13:22:28 +0100606 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Bjorn Bringert442da482009-09-22 13:15:33 +0100607 // We need CLEAR_TOP to avoid reusing an old task that has other activities
Evan Millardc1396c2009-11-06 11:59:39 -0800608 // on top of the one we want. We don't want to do this in in-app search though,
609 // as it can be destructive to the activity stack.
Karl Rosaen875d50a2009-04-23 19:00:21 -0700610 if (data != null) {
611 intent.setData(data);
612 }
Bjorn Bringert5f806052009-06-24 12:02:26 +0100613 intent.putExtra(SearchManager.USER_QUERY, mUserQuery);
Karl Rosaen875d50a2009-04-23 19:00:21 -0700614 if (query != null) {
615 intent.putExtra(SearchManager.QUERY, query);
616 }
617 if (extraData != null) {
618 intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData);
619 }
620 if (mAppSearchData != null) {
621 intent.putExtra(SearchManager.APP_DATA, mAppSearchData);
622 }
623 if (actionKey != KeyEvent.KEYCODE_UNKNOWN) {
624 intent.putExtra(SearchManager.ACTION_KEY, actionKey);
625 intent.putExtra(SearchManager.ACTION_MSG, actionMsg);
626 }
Amith Yamasani479ae0a2010-02-04 14:52:32 -0800627 intent.setComponent(mSearchable.getSearchActivity());
Karl Rosaen875d50a2009-04-23 19:00:21 -0700628 return intent;
629 }
Amith Yamasani479ae0a2010-02-04 14:52:32 -0800630
Karl Rosaen875d50a2009-04-23 19:00:21 -0700631 /**
Bjorn Bringert077357c2009-09-23 13:57:24 +0100632 * The root element in the search bar layout. This is a custom view just to override
633 * the handling of the back button.
634 */
635 public static class SearchBar extends LinearLayout {
636
Bjorn Bringert077357c2009-09-23 13:57:24 +0100637 public SearchBar(Context context, AttributeSet attrs) {
638 super(context, attrs);
639 }
640
641 public SearchBar(Context context) {
642 super(context);
643 }
644
Clara Bayarri4423d912015-03-02 19:42:48 +0000645 @Override
646 public ActionMode startActionModeForChild(
647 View child, ActionMode.Callback callback, int type) {
Clara Bayarri0433ae62015-04-14 15:51:38 +0100648 // Disable Primary Action Modes in the SearchBar, as they overlap.
649 if (type != ActionMode.TYPE_PRIMARY) {
650 return super.startActionModeForChild(child, callback, type);
651 }
Clara Bayarri4423d912015-03-02 19:42:48 +0000652 return null;
653 }
Bjorn Bringert077357c2009-09-23 13:57:24 +0100654 }
655
Amith Yamasani968ec932010-12-02 14:00:47 -0800656 private boolean isEmpty(AutoCompleteTextView actv) {
657 return TextUtils.getTrimmedLength(actv.getText()) == 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658 }
Bjorn Bringert87d61f22009-09-16 23:17:16 +0100659
660 @Override
661 public void onBackPressed() {
Bjorn Bringerta6309c32009-10-01 10:30:25 +0100662 // If the input method is covering the search dialog completely,
663 // e.g. in landscape mode with no hard keyboard, dismiss just the input method
Yohei Yukawa777ef952015-11-25 20:32:24 -0800664 InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
Bjorn Bringerta6309c32009-10-01 10:30:25 +0100665 if (imm != null && imm.isFullscreenMode() &&
666 imm.hideSoftInputFromWindow(getWindow().getDecorView().getWindowToken(), 0)) {
667 return;
668 }
Amith Yamasani479ae0a2010-02-04 14:52:32 -0800669 // Close search dialog
670 cancel();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671 }
Bjorn Bringert87d61f22009-09-16 23:17:16 +0100672
Amith Yamasani968ec932010-12-02 14:00:47 -0800673 private boolean onClosePressed() {
674 // Dismiss the dialog if close button is pressed when there's no query text
675 if (isEmpty(mSearchAutoComplete)) {
676 dismiss();
677 return true;
678 }
679
680 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800681 }
Karl Rosaen875d50a2009-04-23 19:00:21 -0700682
Amith Yamasani968ec932010-12-02 14:00:47 -0800683 private final SearchView.OnCloseListener mOnCloseListener = new SearchView.OnCloseListener() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800684
Amith Yamasani968ec932010-12-02 14:00:47 -0800685 public boolean onClose() {
686 return onClosePressed();
687 }
688 };
Karl Rosaen875d50a2009-04-23 19:00:21 -0700689
Adam Powell01f21352011-01-20 18:30:10 -0800690 private final SearchView.OnQueryTextListener mOnQueryChangeListener =
691 new SearchView.OnQueryTextListener() {
Amith Yamasani968ec932010-12-02 14:00:47 -0800692
Adam Powell01f21352011-01-20 18:30:10 -0800693 public boolean onQueryTextSubmit(String query) {
Amith Yamasani968ec932010-12-02 14:00:47 -0800694 dismiss();
695 return false;
696 }
697
Adam Powell01f21352011-01-20 18:30:10 -0800698 public boolean onQueryTextChange(String newText) {
Amith Yamasani968ec932010-12-02 14:00:47 -0800699 return false;
700 }
701 };
702
Adam Powell01f21352011-01-20 18:30:10 -0800703 private final SearchView.OnSuggestionListener mOnSuggestionSelectionListener =
704 new SearchView.OnSuggestionListener() {
Amith Yamasani968ec932010-12-02 14:00:47 -0800705
Adam Powell01f21352011-01-20 18:30:10 -0800706 public boolean onSuggestionSelect(int position) {
Amith Yamasani968ec932010-12-02 14:00:47 -0800707 return false;
708 }
709
Adam Powell01f21352011-01-20 18:30:10 -0800710 public boolean onSuggestionClick(int position) {
Amith Yamasani968ec932010-12-02 14:00:47 -0800711 dismiss();
712 return false;
713 }
714 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800715
716 /**
Amith Yamasani968ec932010-12-02 14:00:47 -0800717 * Sets the text in the query box, updating the suggestions.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800718 */
Amith Yamasani968ec932010-12-02 14:00:47 -0800719 private void setUserQuery(String query) {
720 if (query == null) {
721 query = "";
722 }
723 mUserQuery = query;
724 mSearchAutoComplete.setText(query);
725 mSearchAutoComplete.setSelection(query.length());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800726 }
727}