1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
|
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.widget;
import static android.widget.SuggestionsAdapter.getColumnString;
import com.android.internal.R;
import android.app.SearchManager;
import android.app.SearchableInfo;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.database.Cursor;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.TextView.OnEditorActionListener;
import java.util.WeakHashMap;
/**
* Provides the user interface elements for the user to enter a search query and submit a
* request to a search provider. Shows a list of query suggestions or results, if
* available and allows the user to pick a suggestion or result to launch into.
*/
public class SearchView extends LinearLayout {
private static final boolean DBG = false;
private static final String LOG_TAG = "SearchView";
private OnQueryChangeListener mOnQueryChangeListener;
private OnCloseListener mOnCloseListener;
private boolean mIconifiedByDefault;
private boolean mIconified;
private CursorAdapter mSuggestionsAdapter;
private View mSearchButton;
private View mSubmitButton;
private View mCloseButton;
private View mSearchEditFrame;
private AutoCompleteTextView mQueryTextView;
private boolean mSubmitButtonEnabled;
private CharSequence mQueryHint;
private boolean mQueryRefinement;
private SearchableInfo mSearchable;
// A weak map of drawables we've gotten from other packages, so we don't load them
// more than once.
private final WeakHashMap<String, Drawable.ConstantState> mOutsideDrawablesCache =
new WeakHashMap<String, Drawable.ConstantState>();
/**
* Callbacks for changes to the query text.
*/
public interface OnQueryChangeListener {
/**
* Called when the user submits the query. This could be due to a key press on the
* keyboard or due to pressing a submit button.
* The listener can override the standard behavior by returning true
* to indicate that it has handled the submit request. Otherwise return false to
* let the SearchView handle the submission by launching any associated intent.
*
* @param query the query text that is to be submitted
*
* @return true if the query has been handled by the listener, false to let the
* SearchView perform the default action.
*/
boolean onSubmitQuery(String query);
/**
* Called when the query text is changed by the user.
*
* @param newText the new content of the query text field.
*
* @return false if the SearchView should perform the default action of showing any
* suggestions if available, true if the action was handled by the listener.
*/
boolean onQueryTextChanged(String newText);
}
public interface OnCloseListener {
/**
* The user is attempting to close the SearchView.
*
* @return true if the listener wants to override the default behavior of clearing the
* text field and dismissing it, false otherwise.
*/
boolean onClose();
}
public SearchView(Context context) {
this(context, null);
}
public SearchView(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.search_view, this, true);
mSearchButton = findViewById(R.id.search_button);
mQueryTextView = (AutoCompleteTextView) findViewById(R.id.search_src_text);
mSearchEditFrame = findViewById(R.id.search_edit_frame);
mSubmitButton = findViewById(R.id.search_go_btn);
mCloseButton = findViewById(R.id.search_close_btn);
mSearchButton.setOnClickListener(mOnClickListener);
mCloseButton.setOnClickListener(mOnClickListener);
mSubmitButton.setOnClickListener(mOnClickListener);
mQueryTextView.addTextChangedListener(mTextWatcher);
mQueryTextView.setOnEditorActionListener(mOnEditorActionListener);
mQueryTextView.setOnItemClickListener(mOnItemClickListener);
mQueryTextView.setOnItemSelectedListener(mOnItemSelectedListener);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SearchView, 0, 0);
setIconifiedByDefault(a.getBoolean(R.styleable.SearchView_iconifiedByDefault, true));
a.recycle();
updateViewsVisibility(mIconifiedByDefault);
}
/**
* Sets the SearchableInfo for this SearchView. Properties in the SearchableInfo are used
* to display labels, hints, suggestions, create intents for launching search results screens
* and controlling other affordances such as a voice button.
*
* @param searchable a SearchableInfo can be retrieved from the SearchManager, for a specific
* activity or a global search provider.
*/
public void setSearchableInfo(SearchableInfo searchable) {
mSearchable = searchable;
if (mSearchable != null) {
updateSearchAutoComplete();
}
updateViewsVisibility(mIconifiedByDefault);
}
/**
* Sets a listener for user actions within the SearchView.
*
* @param listener the listener object that receives callbacks when the user performs
* actions in the SearchView such as clicking on buttons or typing a query.
*/
public void setOnQueryChangeListener(OnQueryChangeListener listener) {
mOnQueryChangeListener = listener;
}
/**
* Sets a listener to inform when the user closes the SearchView.
*
* @param listener the listener to call when the user closes the SearchView.
*/
public void setOnCloseListener(OnCloseListener listener) {
mOnCloseListener = listener;
}
/**
* Sets a query string in the text field and optionally submits the query as well.
*
* @param query the query string. This replaces any query text already present in the
* text field.
* @param submit whether to submit the query right now or only update the contents of
* text field.
*/
public void setQuery(CharSequence query, boolean submit) {
mQueryTextView.setText(query);
// If the query is not empty and submit is requested, submit the query
if (submit && !TextUtils.isEmpty(query)) {
onSubmitQuery();
}
}
/**
* Sets the hint text to display in the query text field. This overrides any hint specified
* in the SearchableInfo.
*
* @param hint the hint text to display
*/
public void setQueryHint(CharSequence hint) {
mQueryHint = hint;
updateQueryHint();
}
/**
* Sets the default or resting state of the search field. If true, a single search icon is
* shown by default and expands to show the text field and other buttons when pressed. Also,
* if the default state is iconified, then it collapses to that state when the close button
* is pressed. Changes to this property will take effect immediately.
*
* <p>The default value is false.</p>
*
* @param iconified whether the search field should be iconified by default
*/
public void setIconifiedByDefault(boolean iconified) {
mIconifiedByDefault = iconified;
updateViewsVisibility(iconified);
}
/**
* Returns the default iconified state of the search field.
* @return
*/
public boolean isIconfiedByDefault() {
return mIconifiedByDefault;
}
/**
* Iconifies or expands the SearchView. Any query text is cleared when iconified. This is
* a temporary state and does not override the default iconified state set by
* {@link #setIconifiedByDefault(boolean)}. If the default state is iconified, then
* a false here will only be valid until the user closes the field. And if the default
* state is expanded, then a true here will only clear the text field and not close it.
*
* @param iconify a true value will collapse the SearchView to an icon, while a false will
* expand it.
*/
public void setIconified(boolean iconify) {
if (iconify) {
onCloseClicked();
} else {
onSearchClicked();
}
}
/**
* Returns the current iconified state of the SearchView.
*
* @return true if the SearchView is currently iconified, false if the search field is
* fully visible.
*/
public boolean isIconified() {
return mIconified;
}
/**
* Enables showing a submit button when the query is non-empty. In cases where the SearchView
* is being used to filter the contents of the current activity and doesn't launch a separate
* results activity, then the submit button should be disabled.
*
* @param enabled true to show a submit button for submitting queries, false if a submit
* button is not required.
*/
public void setSubmitButtonEnabled(boolean enabled) {
mSubmitButton.setVisibility(enabled ? VISIBLE : GONE);
mSubmitButtonEnabled = enabled;
}
/**
* Returns whether the submit button is enabled when necessary or never displayed.
*
* @return whether the submit button is enabled automatically when necessary
*/
public boolean isSubmitButtonEnabled() {
return mSubmitButtonEnabled;
}
/**
* Specifies if a query refinement button should be displayed alongside each suggestion
* or if it should depend on the flags set in the individual items retrieved from the
* suggestions provider. Clicking on the query refinement button will replace the text
* in the query text field with the text from the suggestion. This flag only takes effect
* if a SearchableInfo has been specified with {@link #setSearchableInfo(SearchableInfo)}
* and not when using a custom adapter.
*
* @param enable true if all items should have a query refinement button, false if only
* those items that have a query refinement flag set should have the button.
*
* @see SearchManager#SUGGEST_COLUMN_FLAGS
* @see SearchManager#FLAG_QUERY_REFINEMENT
*/
public void setQueryRefinementEnabled(boolean enable) {
mQueryRefinement = enable;
if (mSuggestionsAdapter instanceof SuggestionsAdapter) {
((SuggestionsAdapter) mSuggestionsAdapter).setQueryRefinement(
enable ? SuggestionsAdapter.REFINE_ALL : SuggestionsAdapter.REFINE_BY_ENTRY);
}
}
/**
* Returns whether query refinement is enabled for all items or only specific ones.
* @return true if enabled for all items, false otherwise.
*/
public boolean isQueryRefinementEnabled() {
return mQueryRefinement;
}
/**
* You can set a custom adapter if you wish. Otherwise the default adapter is used to
* display the suggestions from the suggestions provider associated with the SearchableInfo.
*
* @see #setSearchableInfo(SearchableInfo)
*/
public void setSuggestionsAdapter(CursorAdapter adapter) {
mSuggestionsAdapter = adapter;
mQueryTextView.setAdapter(mSuggestionsAdapter);
}
/**
* Returns the adapter used for suggestions, if any.
* @return the suggestions adapter
*/
public CursorAdapter getSuggestionsAdapter() {
return mSuggestionsAdapter;
}
private void updateViewsVisibility(final boolean collapsed) {
mIconified = collapsed;
// Visibility of views that are visible when collapsed
final int visCollapsed = collapsed ? VISIBLE : GONE;
// Visibility of views that are visible when expanded
final int visExpanded = collapsed ? GONE : VISIBLE;
mSearchButton.setVisibility(visCollapsed);
mSubmitButton.setVisibility(mSubmitButtonEnabled ? visExpanded : GONE);
mSearchEditFrame.setVisibility(visExpanded);
setImeVisibility(!collapsed);
}
private void setImeVisibility(boolean visible) {
// don't mess with the soft input if we're not iconified by default
if (mIconifiedByDefault) {
InputMethodManager imm = (InputMethodManager)
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
// We made sure the IME was displayed, so also make sure it is closed
// when we go away.
if (imm != null) {
if (visible) {
imm.showSoftInputUnchecked(0, null);
} else {
imm.hideSoftInputFromWindow(getWindowToken(), 0);
}
}
}
}
/**
* Called by the SuggestionsAdapter
* @hide
*/
/* package */void onQueryRefine(CharSequence queryText) {
setQuery(queryText);
}
private final OnClickListener mOnClickListener = new OnClickListener() {
public void onClick(View v) {
if (v == mSearchButton) {
onSearchClicked();
} else if (v == mCloseButton) {
onCloseClicked();
} else if (v == mSubmitButton) {
onSubmitQuery();
}
}
};
/**
* Handles the key down event for dealing with action keys.
*
* @param keyCode This is the keycode of the typed key, and is the same value as
* found in the KeyEvent parameter.
* @param event The complete event record for the typed key
*
* @return true if the event was handled here, or false if not.
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (mSearchable == null) {
return false;
}
// if it's an action specified by the searchable activity, launch the
// entered query with the action key
SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) {
launchQuerySearch(keyCode, actionKey.getQueryActionMsg(), mQueryTextView.getText()
.toString());
return true;
}
return super.onKeyDown(keyCode, event);
}
private void updateQueryHint() {
if (mQueryHint != null) {
mQueryTextView.setHint(mQueryHint);
} else if (mSearchable != null) {
CharSequence hint = null;
int hintId = mSearchable.getHintId();
if (hintId != 0) {
hint = getContext().getString(hintId);
}
if (hint != null) {
mQueryTextView.setHint(hint);
}
}
}
/**
* Updates the auto-complete text view.
*/
private void updateSearchAutoComplete() {
// close any existing suggestions adapter
//closeSuggestionsAdapter();
mQueryTextView.setDropDownAnimationStyle(0); // no animation
// attach the suggestions adapter, if suggestions are available
// The existence of a suggestions authority is the proxy for "suggestions available here"
if (mSearchable.getSuggestAuthority() != null) {
mSuggestionsAdapter = new SuggestionsAdapter(getContext(),
this, mSearchable, mOutsideDrawablesCache);
mQueryTextView.setAdapter(mSuggestionsAdapter);
((SuggestionsAdapter) mSuggestionsAdapter).setQueryRefinement(
mQueryRefinement ? SuggestionsAdapter.REFINE_ALL
: SuggestionsAdapter.REFINE_BY_ENTRY);
}
}
private final OnEditorActionListener mOnEditorActionListener = new OnEditorActionListener() {
/**
* Called when the input method default action key is pressed.
*/
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
onSubmitQuery();
return true;
}
};
private void onTextChanged(CharSequence newText) {
CharSequence text = mQueryTextView.getText();
boolean hasText = !TextUtils.isEmpty(text);
if (isSubmitButtonEnabled()) {
mSubmitButton.setVisibility(hasText ? VISIBLE : GONE);
}
if (mOnQueryChangeListener != null)
mOnQueryChangeListener.onQueryTextChanged(newText.toString());
}
private void onSubmitQuery() {
CharSequence query = mQueryTextView.getText();
if (!TextUtils.isEmpty(query)) {
if (mOnQueryChangeListener == null
|| !mOnQueryChangeListener.onSubmitQuery(query.toString())) {
if (mSearchable != null) {
launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, query.toString());
}
}
}
}
private void onCloseClicked() {
if (mOnCloseListener == null || !mOnCloseListener.onClose()) {
mQueryTextView.setText("");
updateViewsVisibility(mIconifiedByDefault);
}
}
private void onSearchClicked() {
mQueryTextView.requestFocus();
updateViewsVisibility(false);
}
private final OnItemClickListener mOnItemClickListener = new OnItemClickListener() {
/**
* Implements OnItemClickListener
*/
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (DBG)
Log.d(LOG_TAG, "onItemClick() position " + position);
launchSuggestion(position, KeyEvent.KEYCODE_UNKNOWN, null);
}
};
private final OnItemSelectedListener mOnItemSelectedListener = new OnItemSelectedListener() {
/**
* Implements OnItemSelectedListener
*/
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (DBG)
Log.d(LOG_TAG, "onItemSelected() position " + position);
// A suggestion has been selected, rewrite the query if possible,
// otherwise the restore the original query.
rewriteQueryFromSuggestion(position);
}
/**
* Implements OnItemSelectedListener
*/
public void onNothingSelected(AdapterView<?> parent) {
if (DBG)
Log.d(LOG_TAG, "onNothingSelected()");
}
};
/**
* Query rewriting.
*/
private void rewriteQueryFromSuggestion(int position) {
CharSequence oldQuery = mQueryTextView.getText();
Cursor c = mSuggestionsAdapter.getCursor();
if (c == null) {
return;
}
if (c.moveToPosition(position)) {
// Get the new query from the suggestion.
CharSequence newQuery = mSuggestionsAdapter.convertToString(c);
if (newQuery != null) {
// The suggestion rewrites the query.
// Update the text field, without getting new suggestions.
setQuery(newQuery);
} else {
// The suggestion does not rewrite the query, restore the user's query.
setQuery(oldQuery);
}
} else {
// We got a bad position, restore the user's query.
setQuery(oldQuery);
}
}
/**
* Launches an intent based on a suggestion.
*
* @param position The index of the suggestion to create the intent from.
* @param actionKey The key code of the action key that was pressed,
* or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
* @param actionMsg The message for the action key that was pressed,
* or <code>null</code> if none.
* @return true if a successful launch, false if could not (e.g. bad position).
*/
private boolean launchSuggestion(int position, int actionKey, String actionMsg) {
Cursor c = mSuggestionsAdapter.getCursor();
if ((c != null) && c.moveToPosition(position)) {
Intent intent = createIntentFromSuggestion(c, actionKey, actionMsg);
// launch the intent
launchIntent(intent);
return true;
}
return false;
}
/**
* Launches an intent, including any special intent handling.
*/
private void launchIntent(Intent intent) {
if (intent == null) {
return;
}
try {
// If the intent was created from a suggestion, it will always have an explicit
// component here.
getContext().startActivity(intent);
} catch (RuntimeException ex) {
Log.e(LOG_TAG, "Failed launch activity: " + intent, ex);
}
}
/**
* Sets the text in the query box, without updating the suggestions.
*/
private void setQuery(CharSequence query) {
mQueryTextView.setText(query, true);
// Move the cursor to the end
mQueryTextView.setSelection(TextUtils.isEmpty(query) ? 0 : query.length());
}
private void launchQuerySearch(int actionKey, String actionMsg, String query) {
String action = Intent.ACTION_SEARCH;
Intent intent = createIntent(action, null, null, query, actionKey, actionMsg);
getContext().startActivity(intent);
}
/**
* Constructs an intent from the given information and the search dialog state.
*
* @param action Intent action.
* @param data Intent data, or <code>null</code>.
* @param extraData Data for {@link SearchManager#EXTRA_DATA_KEY} or <code>null</code>.
* @param query Intent query, or <code>null</code>.
* @param actionKey The key code of the action key that was pressed,
* or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
* @param actionMsg The message for the action key that was pressed,
* or <code>null</code> if none.
* @param mode The search mode, one of the acceptable values for
* {@link SearchManager#SEARCH_MODE}, or {@code null}.
* @return The intent.
*/
private Intent createIntent(String action, Uri data, String extraData, String query,
int actionKey, String actionMsg) {
// Now build the Intent
Intent intent = new Intent(action);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// We need CLEAR_TOP to avoid reusing an old task that has other activities
// on top of the one we want. We don't want to do this in in-app search though,
// as it can be destructive to the activity stack.
if (data != null) {
intent.setData(data);
}
intent.putExtra(SearchManager.USER_QUERY, query);
if (query != null) {
intent.putExtra(SearchManager.QUERY, query);
}
if (extraData != null) {
intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData);
}
if (actionKey != KeyEvent.KEYCODE_UNKNOWN) {
intent.putExtra(SearchManager.ACTION_KEY, actionKey);
intent.putExtra(SearchManager.ACTION_MSG, actionMsg);
}
intent.setComponent(mSearchable.getSearchActivity());
return intent;
}
/**
* When a particular suggestion has been selected, perform the various lookups required
* to use the suggestion. This includes checking the cursor for suggestion-specific data,
* and/or falling back to the XML for defaults; It also creates REST style Uri data when
* the suggestion includes a data id.
*
* @param c The suggestions cursor, moved to the row of the user's selection
* @param actionKey The key code of the action key that was pressed,
* or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
* @param actionMsg The message for the action key that was pressed,
* or <code>null</code> if none.
* @return An intent for the suggestion at the cursor's position.
*/
private Intent createIntentFromSuggestion(Cursor c, int actionKey, String actionMsg) {
try {
// use specific action if supplied, or default action if supplied, or fixed default
String action = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_ACTION);
if (action == null) {
action = mSearchable.getSuggestIntentAction();
}
if (action == null) {
action = Intent.ACTION_SEARCH;
}
// use specific data if supplied, or default data if supplied
String data = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_DATA);
if (data == null) {
data = mSearchable.getSuggestIntentData();
}
// then, if an ID was provided, append it.
if (data != null) {
String id = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID);
if (id != null) {
data = data + "/" + Uri.encode(id);
}
}
Uri dataUri = (data == null) ? null : Uri.parse(data);
String query = getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY);
String extraData = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
return createIntent(action, dataUri, extraData, query, actionKey, actionMsg);
} catch (RuntimeException e ) {
int rowNum;
try { // be really paranoid now
rowNum = c.getPosition();
} catch (RuntimeException e2 ) {
rowNum = -1;
}
Log.w(LOG_TAG, "Search Suggestions cursor at row " + rowNum +
" returned exception" + e.toString());
return null;
}
}
/**
* Callback to watch the text field for empty/non-empty
*/
private TextWatcher mTextWatcher = new TextWatcher() {
public void beforeTextChanged(CharSequence s, int start, int before, int after) { }
public void onTextChanged(CharSequence s, int start,
int before, int after) {
SearchView.this.onTextChanged(s);
}
public void afterTextChanged(Editable s) {
}
};
/*
* Avoid getting focus when searching for something to focus on.
* The user will have to touch the text view to get focus.
*/
protected boolean onRequestFocusInDescendants(int direction,
Rect previouslyFocusedRect) {
return false;
}
}
|