/*
* 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 com.smartandroid.sa.sherlock.widget;
import android.app.PendingIntent;
import android.app.SearchManager;
import android.app.SearchableInfo;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.database.Cursor;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.ResultReceiver;
import android.speech.RecognizerIntent;
import android.support.v4.view.KeyEventCompat;
import android.support.v4.widget.CursorAdapter;
import android.text.Editable;
import android.text.InputType;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.style.ImageSpan;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.AutoCompleteTextView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import com.smartandroid.sa.sherlock.view.CollapsibleActionView;
import java.lang.reflect.Method;
import java.util.WeakHashMap;
import static com.smartandroid.sa.sherlock.widget.SuggestionsAdapter.getColumnString;
/**
* A widget that provides a user interface 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.
*
* <p>
* When the SearchView is used in an ActionBar as an action view for a
* collapsible menu item, it needs to be set to iconified by default using
* {@link #setIconifiedByDefault(boolean) setIconifiedByDefault(true)}. This is
* the default, so nothing needs to be done.
* </p>
* <p>
* If you want the search field to always be visible, then call
* setIconifiedByDefault(false).
* </p>
*
* <div class="special reference"> <h3>Developer Guides</h3>
* <p>
* For information about using {@code SearchView}, read the
* <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot},
* read the <a href="{@docRoot} , read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot},
* read the <a href="{@docRoot} , read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot},
* read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot},
* read the <a href="{@docRoot} , read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot} , read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot},
* read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot},
* read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot},
* read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot},
* read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot},
* read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot},
* read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot},
* read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot},
* read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot},
* read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot},
* read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot},
* read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot},
* read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot},
* read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot},
* read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot},
* read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot} , read the <a
* href="{@docRoot} , read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot}, read the <a href="{@docRoot} , read the <a href="{@docRoot}
* , read the <a href="{@docRoot}, read the <a href="{@docRoot}, read the <a
* href="{@docRoot} , read the <a href="{@docRoot} , read the <a
* href="{@docRoot}, read the <a href="{@docRoot}, read the <a href="{@docRoot}
* guide/topics/search/index.html">Search</a> developer guide.
* </p>
* </div>
*
* @see android.view.MenuItem#SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW
* @attr ref android.R.styleable#SearchView_iconifiedByDefault
* @attr ref android.R.styleable#SearchView_imeOptions
* @attr ref android.R.styleable#SearchView_inputType
* @attr ref android.R.styleable#SearchView_maxWidth
* @attr ref android.R.styleable#SearchView_queryHint
*/
public class SearchView extends LinearLayout implements CollapsibleActionView {
private static final boolean DBG = false;
private static final String LOG_TAG = "SearchView";
/**
* Private constant for removing the microphone in the keyboard.
*/
private static final String IME_OPTION_NO_MICROPHONE = "nm";
private OnQueryTextListener mOnQueryChangeListener;
private OnCloseListener mOnCloseListener;
private OnFocusChangeListener mOnQueryTextFocusChangeListener;
private OnSuggestionListener mOnSuggestionListener;
private OnClickListener mOnSearchClickListener;
private boolean mIconifiedByDefault;
private boolean mIconified;
private CursorAdapter mSuggestionsAdapter;
private View mSearchButton;
private View mSubmitButton;
private View mSearchPlate;
private View mSubmitArea;
private ImageView mCloseButton;
private View mSearchEditFrame;
private View mVoiceButton;
private SearchAutoComplete mQueryTextView;
private View mDropDownAnchor;
private ImageView mSearchHintIcon;
private boolean mSubmitButtonEnabled;
private CharSequence mQueryHint;
private boolean mQueryRefinement;
private boolean mClearingFocus;
private int mMaxWidth;
private boolean mVoiceButtonEnabled;
private CharSequence mOldQueryText;
private CharSequence mUserQuery;
private boolean mExpandedInActionView;
private int mCollapsedImeOptions;
private SearchableInfo mSearchable;
private Bundle mAppSearchData;
private Context c;
/*
* SearchView can be set expanded before the IME is ready to be shown during
* initial UI setup. The show operation is asynchronous to account for this.
*/
private Runnable mShowImeRunnable = new Runnable() {
public void run() {
InputMethodManager imm = (InputMethodManager) getContext()
.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
showSoftInputUnchecked(SearchView.this, imm, 0);
}
}
};
private Runnable mUpdateDrawableStateRunnable = new Runnable() {
public void run() {
updateFocusedState();
}
};
private Runnable mReleaseCursorRunnable = new Runnable() {
public void run() {
if (mSuggestionsAdapter != null
&& mSuggestionsAdapter instanceof SuggestionsAdapter) {
mSuggestionsAdapter.changeCursor(null);
}
}
};
// For voice searching
private final Intent mVoiceWebSearchIntent;
private final Intent mVoiceAppSearchIntent;
// 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 OnQueryTextListener {
/**
* 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 onQueryTextSubmit(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 onQueryTextChange(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();
}
/**
* Callback interface for selection events on suggestions. These callbacks
* are only relevant when a SearchableInfo has been specified by
* {@link #setSearchableInfo}.
*/
public interface OnSuggestionListener {
/**
* Called when a suggestion was selected by navigating to it.
*
* @param position
* the absolute position in the list of suggestions.
*
* @return true if the listener handles the event and wants to override
* the default behavior of possibly rewriting the query based on
* the selected item, false otherwise.
*/
boolean onSuggestionSelect(int position);
/**
* Called when a suggestion was clicked.
*
* @param position
* the absolute position of the clicked item in the list of
* suggestions.
*
* @return true if the listener handles the event and wants to override
* the default behavior of launching any intent or submitting a
* search query specified on that item. Return false otherwise.
*/
boolean onSuggestionClick(int position);
}
public SearchView(Context context) {
this(context, null);
this.c = context;
}
public SearchView(Context context, AttributeSet attrs) {
super(context, attrs);
this.c = context;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) {
throw new IllegalStateException("SearchView is API 8+ only.");
}
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(
getResources().getIdentifier("abs__search_view", "layout",
context.getPackageName()), this, true);
// inflater.inflate(R.layout.abs__search_view, this, true);
mSearchButton = findViewById(getResources().getIdentifier(
"abs__search_button", "id", context.getPackageName()));
// mSearchButton = findViewById(R.id.abs__search_button);
mQueryTextView = (SearchAutoComplete) findViewById(getResources()
.getIdentifier("abs__search_src_text", "id",
context.getPackageName()));
// mQueryTextView = (SearchAutoComplete)
// findViewById(R.id.abs__search_src_text);
mQueryTextView.setSearchView(this);
mSearchEditFrame = findViewById(getResources().getIdentifier(
"abs__search_edit_frame", "id", context.getPackageName()));
// mSearchEditFrame = findViewById(R.id.abs__search_edit_frame);
mSearchPlate = findViewById(getResources().getIdentifier(
"abs__search_plate", "id", context.getPackageName()));
// mSearchPlate = findViewById(R.id.abs__search_plate);
mSubmitArea = findViewById(getResources().getIdentifier(
"abs__submit_area", "id", context.getPackageName()));
// mSubmitArea = findViewById(R.id.abs__submit_area);
mSubmitButton = findViewById(getResources().getIdentifier(
"abs__search_go_btn", "id", context.getPackageName()));
// mSubmitButton = findViewById(R.id.abs__search_go_btn);
mCloseButton = (ImageView) findViewById(getResources().getIdentifier(
"abs__search_close_btn", "id", context.getPackageName()));
// mCloseButton = (ImageView) findViewById(R.id.abs__search_close_btn);
mVoiceButton = findViewById(getResources().getIdentifier(
"abs__search_voice_btn", "id", context.getPackageName()));
// mVoiceButton = findViewById(R.id.abs__search_voice_btn);
mSearchHintIcon = (ImageView) findViewById(getResources()
.getIdentifier("abs__search_mag_icon", "id",
context.getPackageName()));
// mSearchHintIcon = (ImageView)
// findViewById(R.id.abs__search_mag_icon);
mSearchButton.setOnClickListener(mOnClickListener);
mCloseButton.setOnClickListener(mOnClickListener);
mSubmitButton.setOnClickListener(mOnClickListener);
mVoiceButton.setOnClickListener(mOnClickListener);
mQueryTextView.setOnClickListener(mOnClickListener);
mQueryTextView.addTextChangedListener(mTextWatcher);
mQueryTextView.setOnEditorActionListener(mOnEditorActionListener);
mQueryTextView.setOnItemClickListener(mOnItemClickListener);
mQueryTextView.setOnItemSelectedListener(mOnItemSelectedListener);
mQueryTextView.setOnKeyListener(mTextKeyListener);
// Inform any listener of focus changes
mQueryTextView.setOnFocusChangeListener(new OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
if (mOnQueryTextFocusChangeListener != null) {
mOnQueryTextFocusChangeListener.onFocusChange(
SearchView.this, hasFocus);
}
}
});
TypedArray a = context.obtainStyledAttributes(
attrs,
new int[] { getResources().getIdentifier("SherlockSearchView",
"styleable", context.getPackageName()) }, 0, 0);
// R.styleable.SherlockSearchView, 0, 0);
setIconifiedByDefault(a.getBoolean(
getResources().getIdentifier(
"SherlockSearchView_iconifiedByDefault", "styleable",
context.getPackageName()), true));
// R.styleable.SherlockSearchView_iconifiedByDefault, true));
int maxWidth = a.getDimensionPixelSize(
getResources().getIdentifier(
"SherlockSearchView_android_maxWidth", "styleable",
context.getPackageName()), -1);
// R.styleable.SherlockSearchView_android_maxWidth, -1);
if (maxWidth != -1) {
setMaxWidth(maxWidth);
}
CharSequence queryHint = a.getText(getResources().getIdentifier(
"SherlockSearchView_queryHint", "styleable",
context.getPackageName()));
// .getText(R.styleable.SherlockSearchView_queryHint);
if (!TextUtils.isEmpty(queryHint)) {
setQueryHint(queryHint);
}
int imeOptions = a.getInt(
getResources().getIdentifier(
"SherlockSearchView_android_imeOptions", "styleable",
context.getPackageName()), -1);
// R.styleable.SherlockSearchView_android_imeOptions, -1);
if (imeOptions != -1) {
setImeOptions(imeOptions);
}
int inputType = a.getInt(
getResources().getIdentifier(
"SherlockSearchView_android_inputType", "styleable",
context.getPackageName()), -1);
// R.styleable.SherlockSearchView_android_inputType, -1);
if (inputType != -1) {
setInputType(inputType);
}
a.recycle();
boolean focusable = true;
a = context.obtainStyledAttributes(
attrs,
new int[] { getResources().getIdentifier("SherlockView",
"styleable", context.getPackageName()) }, 0,
// a = context.obtainStyledAttributes(attrs,
// R.styleable.SherlockView, 0,
0);
focusable = a.getBoolean(
getResources().getIdentifier("SherlockView_android_focusable",
"styleable", context.getPackageName()),
// focusable =
// a.getBoolean(R.styleable.SherlockView_android_focusable,
focusable);
a.recycle();
setFocusable(focusable);
// Save voice intent for later queries/launching
mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
mVoiceWebSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mVoiceWebSearchIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
mVoiceAppSearchIntent = new Intent(
RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mDropDownAnchor = findViewById(mQueryTextView.getDropDownAnchor());
if (mDropDownAnchor != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
mDropDownAnchor
.addOnLayoutChangeListener(new OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left,
int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight,
int oldBottom) {
adjustDropDownSizeAndPosition();
}
});
} else {
mDropDownAnchor.getViewTreeObserver()
.addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
adjustDropDownSizeAndPosition();
}
});
}
}
updateViewsVisibility(mIconifiedByDefault);
updateQueryHint();
}
/**
* 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();
updateQueryHint();
}
// Cache the voice search capability
mVoiceButtonEnabled = hasVoiceSearch();
if (mVoiceButtonEnabled) {
// Disable the microphone on the keyboard, as a mic is displayed
// near the text box
// TODO: use imeOptions to disable voice input when the new API will
// be available
mQueryTextView.setPrivateImeOptions(IME_OPTION_NO_MICROPHONE);
}
updateViewsVisibility(isIconified());
}
/**
* Sets the APP_DATA for legacy SearchDialog use.
*
* @param appSearchData
* bundle provided by the app when launching the search dialog
* @hide
*/
public void setAppSearchData(Bundle appSearchData) {
mAppSearchData = appSearchData;
}
/**
* Sets the IME options on the query text field.
*
* @see TextView#setImeOptions(int)
* @param imeOptions
* the options to set on the query text field
*
* @attr ref android.R.styleable#SearchView_imeOptions
*/
public void setImeOptions(int imeOptions) {
mQueryTextView.setImeOptions(imeOptions);
}
/**
* Returns the IME options set on the query text field.
*
* @return the ime options
* @see TextView#setImeOptions(int)
*
* @attr ref android.R.styleable#SearchView_imeOptions
*/
public int getImeOptions() {
return mQueryTextView.getImeOptions();
}
/**
* Sets the input type on the query text field.
*
* @see TextView#setInputType(int)
* @param inputType
* the input type to set on the query text field
*
* @attr ref android.R.styleable#SearchView_inputType
*/
public void setInputType(int inputType) {
mQueryTextView.setInputType(inputType);
}
/**
* Returns the input type set on the query text field.
*
* @return the input type
*
* @attr ref android.R.styleable#SearchView_inputType
*/
public int getInputType() {
return mQueryTextView.getInputType();
}
/** @hide */
@Override
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
// Don't accept focus if in the middle of clearing focus
if (mClearingFocus)
return false;
// Check if SearchView is focusable.
if (!isFocusable())
return false;
// If it is not iconified, then give the focus to the text field
if (!isIconified()) {
boolean result = mQueryTextView.requestFocus(direction,
previouslyFocusedRect);
if (result) {
updateViewsVisibility(false);
}
return result;
} else {
return super.requestFocus(direction, previouslyFocusedRect);
}
}
/** @hide */
@Override
public void clearFocus() {
mClearingFocus = true;
setImeVisibility(false);
super.clearFocus();
mQueryTextView.clearFocus();
mClearingFocus = false;
}
/**
* 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 setOnQueryTextListener(OnQueryTextListener 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 listener to inform when the focus of the query text field changes.
*
* @param listener
* the listener to inform of focus changes.
*/
public void setOnQueryTextFocusChangeListener(OnFocusChangeListener listener) {
mOnQueryTextFocusChangeListener = listener;
}
/**
* Sets a listener to inform when a suggestion is focused or clicked.
*
* @param listener
* the listener to inform of suggestion selection events.
*/
public void setOnSuggestionListener(OnSuggestionListener listener) {
mOnSuggestionListener = listener;
}
/**
* Sets a listener to inform when the search button is pressed. This is only
* relevant when the text field is not visible by default. Calling
* {@link #setIconified setIconified(false)} can also cause this listener to
* be informed.
*
* @param listener
* the listener to inform when the search button is clicked or
* the text field is programmatically de-iconified.
*/
public void setOnSearchClickListener(OnClickListener listener) {
mOnSearchClickListener = listener;
}
/**
* Returns the query string currently in the text field.
*
* @return the query string
*/
public CharSequence getQuery() {
return mQueryTextView.getText();
}
/**
* 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 (query != null) {
mQueryTextView.setSelection(mQueryTextView.length());
mUserQuery = 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
*
* @attr ref android.R.styleable#SearchView_queryHint
*/
public void setQueryHint(CharSequence hint) {
mQueryHint = hint;
updateQueryHint();
}
/**
* Gets the hint text to display in the query text field.
*
* @return the query hint text, if specified, null otherwise.
*
* @attr ref android.R.styleable#SearchView_queryHint
*/
public CharSequence getQueryHint() {
if (mQueryHint != null) {
return mQueryHint;
} else if (mSearchable != null) {
CharSequence hint = null;
int hintId = mSearchable.getHintId();
if (hintId != 0) {
hint = getContext().getString(hintId);
}
return hint;
}
return null;
}
/**
* 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 true.
* </p>
*
* @param iconified
* whether the search field should be iconified by default
*
* @attr ref android.R.styleable#SearchView_iconifiedByDefault
*/
public void setIconifiedByDefault(boolean iconified) {
if (mIconifiedByDefault == iconified)
return;
mIconifiedByDefault = iconified;
updateViewsVisibility(iconified);
updateQueryHint();
}
/**
* Returns the default iconified state of the search field.
*
* @return
*
* @attr ref android.R.styleable#SearchView_iconifiedByDefault
*/
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) {
mSubmitButtonEnabled = enabled;
updateViewsVisibility(isIconified());
}
/**
* 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;
}
/**
* Makes the view at most this many pixels wide
*
* @attr ref android.R.styleable#SearchView_maxWidth
*/
public void setMaxWidth(int maxpixels) {
mMaxWidth = maxpixels;
requestLayout();
}
/**
* Gets the specified maximum width in pixels, if set. Returns zero if no
* maximum width was specified.
*
* @return the maximum width of the view
*
* @attr ref android.R.styleable#SearchView_maxWidth
*/
public int getMaxWidth() {
return mMaxWidth;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Let the standard measurements take effect in iconified state.
if (isIconified()) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
return;
}
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
switch (widthMode) {
case MeasureSpec.AT_MOST:
// If there is an upper limit, don't exceed maximum width (explicit
// or implicit)
if (mMaxWidth > 0) {
width = Math.min(mMaxWidth, width);
} else {
width = Math.min(getPreferredWidth(), width);
}
break;
case MeasureSpec.EXACTLY:
// If an exact width is specified, still don't exceed any specified
// maximum width
if (mMaxWidth > 0) {
width = Math.min(mMaxWidth, width);
}
break;
case MeasureSpec.UNSPECIFIED:
// Use maximum width, if specified, else preferred width
width = mMaxWidth > 0 ? mMaxWidth : getPreferredWidth();
break;
}
widthMode = MeasureSpec.EXACTLY;
super.onMeasure(MeasureSpec.makeMeasureSpec(width, widthMode),
heightMeasureSpec);
}
private int getPreferredWidth() {
return getContext().getResources().getDimensionPixelSize(
getResources().getIdentifier(
"abs__search_view_preferred_width", "dimen",
c.getPackageName()));
// R.dimen.abs__search_view_preferred_width);
}
private void updateViewsVisibility(final boolean collapsed) {
mIconified = collapsed;
// Visibility of views that are visible when collapsed
final int visCollapsed = collapsed ? VISIBLE : GONE;
// Is there text in the query
final boolean hasText = !TextUtils.isEmpty(mQueryTextView.getText());
mSearchButton.setVisibility(visCollapsed);
updateSubmitButton(hasText);
mSearchEditFrame.setVisibility(collapsed ? GONE : VISIBLE);
mSearchHintIcon.setVisibility(mIconifiedByDefault ? GONE : VISIBLE);
updateCloseButton();
updateVoiceButton(!hasText);
updateSubmitArea();
}
private boolean hasVoiceSearch() {
if (mSearchable != null && mSearchable.getVoiceSearchEnabled()) {
Intent testIntent = null;
if (mSearchable.getVoiceSearchLaunchWebSearch()) {
testIntent = mVoiceWebSearchIntent;
} else if (mSearchable.getVoiceSearchLaunchRecognizer()) {
testIntent = mVoiceAppSearchIntent;
}
if (testIntent != null) {
ResolveInfo ri = getContext().getPackageManager()
.resolveActivity(testIntent,
PackageManager.MATCH_DEFAULT_ONLY);
return ri != null;
}
}
return false;
}
private boolean isSubmitAreaEnabled() {
return (mSubmitButtonEnabled || mVoiceButtonEnabled) && !isIconified();
}
private void updateSubmitButton(boolean hasText) {
int visibility = GONE;
if (mSubmitButtonEnabled && isSubmitAreaEnabled() && hasFocus()
&& (hasText || !mVoiceButtonEnabled)) {
visibility = VISIBLE;
}
mSubmitButton.setVisibility(visibility);
}
private void updateSubmitArea() {
int visibility = GONE;
if (isSubmitAreaEnabled()
&& (mSubmitButton.getVisibility() == VISIBLE || mVoiceButton
.getVisibility() == VISIBLE)) {
visibility = VISIBLE;
}
mSubmitArea.setVisibility(visibility);
}
private void updateCloseButton() {
final boolean hasText = !TextUtils.isEmpty(mQueryTextView.getText());
// Should we show the close button? It is not shown if there's no focus,
// field is not iconified by default and there is no text in it.
final boolean showClose = hasText
|| (mIconifiedByDefault && !mExpandedInActionView);
mCloseButton.setVisibility(showClose ? VISIBLE : GONE);
mCloseButton.getDrawable().setState(
hasText ? ENABLED_STATE_SET : EMPTY_STATE_SET);
}
private void postUpdateFocusedState() {
post(mUpdateDrawableStateRunnable);
}
private void updateFocusedState() {
boolean focused = mQueryTextView.hasFocus();
mSearchPlate.getBackground().setState(
focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET);
mSubmitArea.getBackground().setState(
focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET);
invalidate();
}
@Override
protected void onDetachedFromWindow() {
removeCallbacks(mUpdateDrawableStateRunnable);
post(mReleaseCursorRunnable);
super.onDetachedFromWindow();
}
private void setImeVisibility(final boolean visible) {
if (visible) {
post(mShowImeRunnable);
} else {
removeCallbacks(mShowImeRunnable);
InputMethodManager imm = (InputMethodManager) getContext()
.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
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();
} else if (v == mVoiceButton) {
onVoiceClicked();
} else if (v == mQueryTextView) {
forceSuggestionQuery();
}
}
};
/**
* 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
// TODO SearchableInfo.ActionKeyInfo actionKey =
// mSearchable.findActionKey(keyCode);
// TODO if ((actionKey != null) && (actionKey.getQueryActionMsg() !=
// null)) {
// TODO launchQuerySearch(keyCode, actionKey.getQueryActionMsg(),
// mQueryTextView.getText()
// TODO .toString());
// TODO return true;
// TODO }
return super.onKeyDown(keyCode, event);
}
/**
* React to the user typing "enter" or other hardwired keys while typing in
* the search box. This handles these special keys while the edit box has
* focus.
*/
View.OnKeyListener mTextKeyListener = new View.OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
// guard against possible race conditions
if (mSearchable == null) {
return false;
}
if (DBG) {
Log.d(LOG_TAG, "mTextListener.onKey(" + keyCode + "," + event
+ "), selection: " + mQueryTextView.getListSelection());
}
// If a suggestion is selected, handle enter, search key, and action
// keys
// as presses on the selected suggestion
if (mQueryTextView.isPopupShowing()
&& mQueryTextView.getListSelection() != ListView.INVALID_POSITION) {
return onSuggestionsKey(v, keyCode, event);
}
// If there is text in the query box, handle enter, and action keys
// The search key is handled by the dialog's onKeyDown().
if (!mQueryTextView.isEmpty()
&& KeyEventCompat.hasNoModifiers(event)) {
if (event.getAction() == KeyEvent.ACTION_UP) {
if (keyCode == KeyEvent.KEYCODE_ENTER) {
v.cancelLongPress();
// Launch as a regular search.
launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null,
mQueryTextView.getText().toString());
return true;
}
}
if (event.getAction() == KeyEvent.ACTION_DOWN) {
// TODO SearchableInfo.ActionKeyInfo actionKey =
// mSearchable.findActionKey(keyCode);
// TODO if ((actionKey != null) &&
// (actionKey.getQueryActionMsg() != null)) {
// TODO launchQuerySearch(keyCode,
// actionKey.getQueryActionMsg(), mQueryTextView
// TODO .getText().toString());
// TODO return true;
// TODO }
}
}
return false;
}
};
/**
* React to the user typing while in the suggestions list. First, check for
* action keys. If not handled, try refocusing regular characters into the
* EditText.
*/
private boolean onSuggestionsKey(View v, int keyCode, KeyEvent event) {
// guard against possible race conditions (late arrival after dismiss)
if (mSearchable == null) {
return false;
}
if (mSuggestionsAdapter == null) {
return false;
}
if (event.getAction() == KeyEvent.ACTION_DOWN
&& KeyEventCompat.hasNoModifiers(event)) {
// First, check for enter or search (both of which we'll treat as a
// "click")
if (keyCode == KeyEvent.KEYCODE_ENTER
|| keyCode == KeyEvent.KEYCODE_SEARCH
|| keyCode == KeyEvent.KEYCODE_TAB) {
int position = mQueryTextView.getListSelection();
return onItemClicked(position, KeyEvent.KEYCODE_UNKNOWN, null);
}
// Next, check for left/right moves, which we use to "return" the
// user to the edit view
if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT
|| keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
// give "focus" to text editor, with cursor at the beginning if
// left key, at end if right key
// TODO: Reverse left/right for right-to-left languages, e.g.
// Arabic
int selPoint = (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) ? 0
: mQueryTextView.length();
mQueryTextView.setSelection(selPoint);
mQueryTextView.setListSelection(0);
mQueryTextView.clearListSelection();
ensureImeVisible(mQueryTextView, true);
return true;
}
// Next, check for an "up and out" move
if (keyCode == KeyEvent.KEYCODE_DPAD_UP
&& 0 == mQueryTextView.getListSelection()) {
// TODO: restoreUserQuery();
// let ACTV complete the move
return false;
}
// Next, check for an "action key"
// TODO SearchableInfo.ActionKeyInfo actionKey =
// mSearchable.findActionKey(keyCode);
// TODO if ((actionKey != null)
// TODO && ((actionKey.getSuggestActionMsg() != null) || (actionKey
// TODO .getSuggestActionMsgColumn() != null))) {
// TODO // launch suggestion using action key column
// TODO int position = mQueryTextView.getListSelection();
// TODO if (position != ListView.INVALID_POSITION) {
// TODO Cursor c = mSuggestionsAdapter.getCursor();
// TODO if (c.moveToPosition(position)) {
// TODO final String actionMsg = getActionKeyMessage(c, actionKey);
// TODO if (actionMsg != null && (actionMsg.length() > 0)) {
// TODO return onItemClicked(position, keyCode, actionMsg);
// TODO }
// TODO }
// TODO }
// TODO }
}
return false;
}
/**
* For a given suggestion and a given cursor row, get the action message. If
* not provided by the specific row/column, also check for a single
* definition (for the action key).
*
* @param c
* The cursor providing suggestions
* @param actionKey
* The actionkey record being examined
*
* @return Returns a string, or null if no action key message for this
* suggestion
*/
// TODO private static String getActionKeyMessage(Cursor c,
// SearchableInfo.ActionKeyInfo actionKey) {
// TODO String result = null;
// TODO // check first in the cursor data, for a suggestion-specific message
// TODO final String column = actionKey.getSuggestActionMsgColumn();
// TODO if (column != null) {
// TODO result = SuggestionsAdapter.getColumnString(c, column);
// TODO }
// TODO // If the cursor didn't give us a message, see if there's a single
// TODO // message defined
// TODO // for the actionkey (for all suggestions)
// TODO if (result == null) {
// TODO result = actionKey.getSuggestActionMsg();
// TODO }
// TODO return result;
// TODO }
private int getSearchIconId() {
TypedValue outValue = new TypedValue();
getContext().getTheme().resolveAttribute(
getResources().getIdentifier("searchViewSearchIcon", "attr",
c.getPackageName()),
// getContext().getTheme().resolveAttribute(R.attr.searchViewSearchIcon,
outValue, true);
return outValue.resourceId;
}
private CharSequence getDecoratedHint(CharSequence hintText) {
// If the field is always expanded, then don't add the search icon to
// the hint
if (!mIconifiedByDefault)
return hintText;
SpannableStringBuilder ssb = new SpannableStringBuilder(" "); // for
// the
// icon
ssb.append(hintText);
Drawable searchIcon = getContext().getResources().getDrawable(
getSearchIconId());
int textSize = (int) (mQueryTextView.getTextSize() * 1.25);
searchIcon.setBounds(0, 0, textSize, textSize);
ssb.setSpan(new ImageSpan(searchIcon), 1, 2,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return ssb;
}
private void updateQueryHint() {
if (mQueryHint != null) {
mQueryTextView.setHint(getDecoratedHint(mQueryHint));
} else if (mSearchable != null) {
CharSequence hint = null;
int hintId = mSearchable.getHintId();
if (hintId != 0) {
hint = getContext().getString(hintId);
}
if (hint != null) {
mQueryTextView.setHint(getDecoratedHint(hint));
}
} else {
mQueryTextView.setHint(getDecoratedHint(""));
}
}
/**
* Updates the auto-complete text view.
*/
private void updateSearchAutoComplete() {
// TODO mQueryTextView.setDropDownAnimationStyle(0); // no animation
mQueryTextView.setThreshold(mSearchable.getSuggestThreshold());
mQueryTextView.setImeOptions(mSearchable.getImeOptions());
int inputType = mSearchable.getInputType();
// We only touch this if the input type is set up for text (which it
// almost certainly
// should be, in the case of search!)
if ((inputType & InputType.TYPE_MASK_CLASS) == InputType.TYPE_CLASS_TEXT) {
// The existence of a suggestions authority is the proxy for
// "suggestions
// are available here"
inputType &= ~InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE;
if (mSearchable.getSuggestAuthority() != null) {
inputType |= InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE;
// TYPE_TEXT_FLAG_AUTO_COMPLETE means that the text editor is
// performing
// auto-completion based on its own semantics, which it will
// present to the user
// as they type. This generally means that the input method
// should not show its
// own candidates, and the spell checker should not be in
// action. The text editor
// supplies its candidates by calling
// InputMethodManager.displayCompletions(),
// which in turn will call
// InputMethodSession.displayCompletions().
inputType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
}
}
mQueryTextView.setInputType(inputType);
if (mSuggestionsAdapter != null) {
mSuggestionsAdapter.changeCursor(null);
}
// 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);
}
}
/**
* Update the visibility of the voice button. There are actually two voice
* search modes, either of which will activate the button.
*
* @param empty
* whether the search query text field is empty. If it is, then
* the other criteria apply to make the voice button visible.
*/
private void updateVoiceButton(boolean empty) {
int visibility = GONE;
if (mVoiceButtonEnabled && !isIconified() && empty) {
visibility = VISIBLE;
mSubmitButton.setVisibility(GONE);
}
mVoiceButton.setVisibility(visibility);
}
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();
mUserQuery = text;
boolean hasText = !TextUtils.isEmpty(text);
updateSubmitButton(hasText);
updateVoiceButton(!hasText);
updateCloseButton();
updateSubmitArea();
if (mOnQueryChangeListener != null
&& !TextUtils.equals(newText, mOldQueryText)) {
mOnQueryChangeListener.onQueryTextChange(newText.toString());
}
mOldQueryText = newText.toString();
}
private void onSubmitQuery() {
CharSequence query = mQueryTextView.getText();
if (query != null && TextUtils.getTrimmedLength(query) > 0) {
if (mOnQueryChangeListener == null
|| !mOnQueryChangeListener.onQueryTextSubmit(query
.toString())) {
if (mSearchable != null) {
launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null,
query.toString());
setImeVisibility(false);
}
dismissSuggestions();
}
}
}
private void dismissSuggestions() {
mQueryTextView.dismissDropDown();
}
private void onCloseClicked() {
CharSequence text = mQueryTextView.getText();
if (TextUtils.isEmpty(text)) {
if (mIconifiedByDefault) {
// If the app doesn't override the close behavior
if (mOnCloseListener == null || !mOnCloseListener.onClose()) {
// hide the keyboard and remove focus
clearFocus();
// collapse the search field
updateViewsVisibility(true);
}
}
} else {
mQueryTextView.setText("");
mQueryTextView.requestFocus();
setImeVisibility(true);
}
}
private void onSearchClicked() {
updateViewsVisibility(false);
mQueryTextView.requestFocus();
setImeVisibility(true);
if (mOnSearchClickListener != null) {
mOnSearchClickListener.onClick(this);
}
}
private void onVoiceClicked() {
// guard against possible race conditions
if (mSearchable == null) {
return;
}
SearchableInfo searchable = mSearchable;
try {
if (searchable.getVoiceSearchLaunchWebSearch()) {
Intent webSearchIntent = createVoiceWebSearchIntent(
mVoiceWebSearchIntent, searchable);
getContext().startActivity(webSearchIntent);
} else if (searchable.getVoiceSearchLaunchRecognizer()) {
Intent appSearchIntent = createVoiceAppSearchIntent(
mVoiceAppSearchIntent, searchable);
getContext().startActivity(appSearchIntent);
}
} catch (ActivityNotFoundException e) {
// Should not happen, since we check the availability of
// voice search before showing the button. But just in case...
Log.w(LOG_TAG, "Could not find voice search activity");
}
}
void onTextFocusChanged() {
updateViewsVisibility(isIconified());
// Delayed update to make sure that the focus has settled down and
// window focus changes
// don't affect it. A synchronous update was not working.
postUpdateFocusedState();
if (mQueryTextView.hasFocus()) {
forceSuggestionQuery();
}
}
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
postUpdateFocusedState();
}
/**
* {@inheritDoc}
*/
@Override
public void onActionViewCollapsed() {
clearFocus();
updateViewsVisibility(true);
mQueryTextView.setImeOptions(mCollapsedImeOptions);
mExpandedInActionView = false;
}
/**
* {@inheritDoc}
*/
@Override
public void onActionViewExpanded() {
if (mExpandedInActionView)
return;
mExpandedInActionView = true;
mCollapsedImeOptions = mQueryTextView.getImeOptions();
mQueryTextView.setImeOptions(mCollapsedImeOptions
| EditorInfo.IME_FLAG_NO_FULLSCREEN);
mQueryTextView.setText("");
setIconified(false);
}
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
event.setClassName(SearchView.class.getName());
}
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
info.setClassName(SearchView.class.getName());
}
private void adjustDropDownSizeAndPosition() {
if (mDropDownAnchor.getWidth() > 1) {
Resources res = getContext().getResources();
int anchorPadding = mSearchPlate.getPaddingLeft();
Rect dropDownPadding = new Rect();
int iconOffset = mIconifiedByDefault ? res
.getDimensionPixelSize(getResources().getIdentifier(
"abs__dropdownitem_icon_width", "dimen",
c.getPackageName()))
// .getDimensionPixelSize(R.dimen.abs__dropdownitem_icon_width)
+ res.getDimensionPixelSize(getResources().getIdentifier(
"abs__dropdownitem_text_padding_left", "dimen",
c.getPackageName()))
// +
// res.getDimensionPixelSize(R.dimen.abs__dropdownitem_text_padding_left)
: 0;
mQueryTextView.getDropDownBackground().getPadding(dropDownPadding);
mQueryTextView
.setDropDownHorizontalOffset(-(dropDownPadding.left + iconOffset)
+ anchorPadding);
mQueryTextView.setDropDownWidth(mDropDownAnchor.getWidth()
+ dropDownPadding.left + dropDownPadding.right + iconOffset
- (anchorPadding));
}
}
private boolean onItemClicked(int position, int actionKey, String actionMsg) {
if (mOnSuggestionListener == null
|| !mOnSuggestionListener.onSuggestionClick(position)) {
launchSuggestion(position, KeyEvent.KEYCODE_UNKNOWN, null);
setImeVisibility(false);
dismissSuggestions();
return true;
}
return false;
}
private boolean onItemSelected(int position) {
if (mOnSuggestionListener == null
|| !mOnSuggestionListener.onSuggestionSelect(position)) {
rewriteQueryFromSuggestion(position);
return true;
}
return 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);
onItemClicked(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);
SearchView.this.onItemSelected(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) {
setText(mQueryTextView, 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.
* @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, mUserQuery);
if (query != null) {
intent.putExtra(SearchManager.QUERY, query);
}
if (extraData != null) {
intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData);
}
if (mAppSearchData != null) {
intent.putExtra(SearchManager.APP_DATA, mAppSearchData);
}
if (actionKey != KeyEvent.KEYCODE_UNKNOWN) {
intent.putExtra(SearchManager.ACTION_KEY, actionKey);
intent.putExtra(SearchManager.ACTION_MSG, actionMsg);
}
intent.setComponent(mSearchable.getSearchActivity());
return intent;
}
/**
* Create and return an Intent that can launch the voice search activity for
* web search.
*/
private Intent createVoiceWebSearchIntent(Intent baseIntent,
SearchableInfo searchable) {
Intent voiceIntent = new Intent(baseIntent);
ComponentName searchActivity = searchable.getSearchActivity();
voiceIntent.putExtra(
RecognizerIntent.EXTRA_CALLING_PACKAGE,
searchActivity == null ? null : searchActivity
.flattenToShortString());
return voiceIntent;
}
/**
* Create and return an Intent that can launch the voice search activity,
* perform a specific voice transcription, and forward the results to the
* searchable activity.
*
* @param baseIntent
* The voice app search intent to start from
* @return A completely-configured intent ready to send to the voice search
* activity
*/
private Intent createVoiceAppSearchIntent(Intent baseIntent,
SearchableInfo searchable) {
ComponentName searchActivity = searchable.getSearchActivity();
// create the necessary intent to set up a search-and-forward operation
// in the voice search system. We have to keep the bundle separate,
// because it becomes immutable once it enters the PendingIntent
Intent queryIntent = new Intent(Intent.ACTION_SEARCH);
queryIntent.setComponent(searchActivity);
PendingIntent pending = PendingIntent.getActivity(getContext(), 0,
queryIntent, PendingIntent.FLAG_ONE_SHOT);
// Now set up the bundle that will be inserted into the pending intent
// when it's time to do the search. We always build it here (even if
// empty)
// because the voice search activity will always need to insert "QUERY"
// into
// it anyway.
Bundle queryExtras = new Bundle();
// Now build the intent to launch the voice search. Add all necessary
// extras to launch the voice recognizer, and then all the necessary
// extras
// to forward the results to the searchable activity
Intent voiceIntent = new Intent(baseIntent);
// Add all of the configuration options supplied by the searchable's
// metadata
String languageModel = RecognizerIntent.LANGUAGE_MODEL_FREE_FORM;
String prompt = null;
String language = null;
int maxResults = 1;
Resources resources = getResources();
if (searchable.getVoiceLanguageModeId() != 0) {
languageModel = resources.getString(searchable
.getVoiceLanguageModeId());
}
if (searchable.getVoicePromptTextId() != 0) {
prompt = resources.getString(searchable.getVoicePromptTextId());
}
if (searchable.getVoiceLanguageId() != 0) {
language = resources.getString(searchable.getVoiceLanguageId());
}
if (searchable.getVoiceMaxResults() != 0) {
maxResults = searchable.getVoiceMaxResults();
}
voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
languageModel);
voiceIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt);
voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language);
voiceIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, maxResults);
voiceIntent.putExtra(
RecognizerIntent.EXTRA_CALLING_PACKAGE,
searchActivity == null ? null : searchActivity
.flattenToShortString());
// Add the values that configure forwarding the results
voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT,
pending);
voiceIntent.putExtra(
RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT_BUNDLE,
queryExtras);
return voiceIntent;
}
/**
* 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);
return null;
}
}
private void forceSuggestionQuery() {
try {
Method before = AutoCompleteTextView.class
.getDeclaredMethod("doBeforeTextChanged");
Method after = AutoCompleteTextView.class
.getDeclaredMethod("doAfterTextChanged");
before.setAccessible(true);
after.setAccessible(true);
before.invoke(mQueryTextView);
after.invoke(mQueryTextView);
} catch (Exception e) {
// Oh well...
}
}
static boolean isLandscapeMode(Context context) {
return context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
}
/**
* 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) {
}
};
/**
* Local subclass for AutoCompleteTextView.
*
* @hide
*/
public static class SearchAutoComplete extends AutoCompleteTextView {
private int mThreshold;
private SearchView mSearchView;
public SearchAutoComplete(Context context) {
super(context);
mThreshold = getThreshold();
}
public SearchAutoComplete(Context context, AttributeSet attrs) {
super(context, attrs);
mThreshold = getThreshold();
}
public SearchAutoComplete(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
mThreshold = getThreshold();
}
void setSearchView(SearchView searchView) {
mSearchView = searchView;
}
@Override
public void setThreshold(int threshold) {
super.setThreshold(threshold);
mThreshold = threshold;
}
/**
* Returns true if the text field is empty, or contains only whitespace.
*/
private boolean isEmpty() {
return TextUtils.getTrimmedLength(getText()) == 0;
}
/**
* We override this method to avoid replacing the query box text when a
* suggestion is clicked.
*/
@Override
protected void replaceText(CharSequence text) {
}
/**
* We override this method to avoid an extra onItemClick being called on
* the drop-down's OnItemClickListener by
* {@link AutoCompleteTextView#onKeyUp(int, KeyEvent)} when an item is
* clicked with the trackball.
*/
@Override
public void performCompletion() {
}
/**
* We override this method to be sure and show the soft keyboard if
* appropriate when the TextView has focus.
*/
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
if (hasWindowFocus && mSearchView.hasFocus()
&& getVisibility() == VISIBLE) {
InputMethodManager inputManager = (InputMethodManager) getContext()
.getSystemService(Context.INPUT_METHOD_SERVICE);
inputManager.showSoftInput(this, 0);
// If in landscape mode, then make sure that
// the ime is in front of the dropdown.
if (isLandscapeMode(getContext())) {
ensureImeVisible(this, true);
}
}
}
@Override
protected void onFocusChanged(boolean focused, int direction,
Rect previouslyFocusedRect) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
mSearchView.onTextFocusChanged();
}
/**
* We override this method so that we can allow a threshold of zero,
* which ACTV does not.
*/
@Override
public boolean enoughToFilter() {
return mThreshold <= 0 || super.enoughToFilter();
}
@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
// special case for the back key, we do not even try to send it
// to the drop down list but instead, consume it immediately
if (event.getAction() == KeyEvent.ACTION_DOWN
&& event.getRepeatCount() == 0) {
KeyEvent.DispatcherState state = getKeyDispatcherState();
if (state != null) {
state.startTracking(event, this);
}
return true;
} else if (event.getAction() == KeyEvent.ACTION_UP) {
KeyEvent.DispatcherState state = getKeyDispatcherState();
if (state != null) {
state.handleUpEvent(event);
}
if (event.isTracking() && !event.isCanceled()) {
mSearchView.clearFocus();
mSearchView.setImeVisibility(false);
return true;
}
}
}
return super.onKeyPreIme(keyCode, event);
}
}
private static void ensureImeVisible(AutoCompleteTextView view,
boolean visible) {
try {
Method method = AutoCompleteTextView.class.getMethod(
"ensureImeVisible", boolean.class);
method.setAccessible(true);
method.invoke(view, visible);
} catch (Exception e) {
// Oh well...
}
}
private static void showSoftInputUnchecked(View view,
InputMethodManager imm, int flags) {
try {
Method method = imm.getClass().getMethod("showSoftInputUnchecked",
int.class, ResultReceiver.class);
method.setAccessible(true);
method.invoke(imm, flags, null);
} catch (Exception e) {
// Fallback to public API which hopefully does mostly the same thing
imm.showSoftInput(view, flags);
}
}
private static void setText(AutoCompleteTextView view, CharSequence text,
boolean filter) {
try {
Method method = AutoCompleteTextView.class.getMethod("setText",
CharSequence.class, boolean.class);
method.setAccessible(true);
method.invoke(view, text, filter);
} catch (Exception e) {
// Fallback to public API which hopefully does mostly the same thing
view.setText(text);
}
}
}