SearchBarpublic class SearchBar extends android.widget.RelativeLayout SearchBar is a search widget.
Note: Your application will need to request android.permission.RECORD_AUDIO |
Fields Summary |
---|
private static final String | TAG | private static final boolean | DEBUG | private static final float | FULL_LEFT_VOLUME | private static final float | FULL_RIGHT_VOLUME | private static final int | DEFAULT_PRIORITY | private static final int | DO_NOT_LOOP | private static final float | DEFAULT_RATE | private AudioManager.OnAudioFocusChangeListener | mAudioFocusChangeListener | private SearchBarListener | mSearchBarListener | private SearchEditText | mSearchTextEditor | private SpeechOrbView | mSpeechOrbView | private android.widget.ImageView | mBadgeView | private String | mSearchQuery | private String | mHint | private String | mTitle | private android.graphics.drawable.Drawable | mBadgeDrawable | private final android.os.Handler | mHandler | private final android.view.inputmethod.InputMethodManager | mInputMethodManager | private boolean | mAutoStartRecognition | private android.graphics.drawable.Drawable | mBarBackground | private final int | mTextColor | private final int | mTextColorSpeechMode | private final int | mTextHintColor | private final int | mTextHintColorSpeechMode | private int | mBackgroundAlpha | private int | mBackgroundSpeechAlpha | private int | mBarHeight | private android.speech.SpeechRecognizer | mSpeechRecognizer | private SpeechRecognitionCallback | mSpeechRecognitionCallback | private boolean | mListening | private android.media.SoundPool | mSoundPool | private android.util.SparseIntArray | mSoundMap | private boolean | mRecognizing | private final android.content.Context | mContext | private android.media.AudioManager | mAudioManager |
Constructors Summary |
---|
public SearchBar(android.content.Context context)
this(context, null);
| public SearchBar(android.content.Context context, android.util.AttributeSet attrs)
this(context, attrs, 0);
| public SearchBar(android.content.Context context, android.util.AttributeSet attrs, int defStyle)
super(context, attrs, defStyle);
mContext = context;
Resources r = getResources();
LayoutInflater inflater = LayoutInflater.from(getContext());
inflater.inflate(R.layout.lb_search_bar, this, true);
mBarHeight = getResources().getDimensionPixelSize(R.dimen.lb_search_bar_height);
RelativeLayout.LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
mBarHeight);
params.addRule(ALIGN_PARENT_TOP, RelativeLayout.TRUE);
setLayoutParams(params);
setBackgroundColor(Color.TRANSPARENT);
setClipChildren(false);
mSearchQuery = "";
mInputMethodManager =
(InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE);
mTextColorSpeechMode = r.getColor(R.color.lb_search_bar_text_speech_mode);
mTextColor = r.getColor(R.color.lb_search_bar_text);
mBackgroundSpeechAlpha = r.getInteger(R.integer.lb_search_bar_speech_mode_background_alpha);
mBackgroundAlpha = r.getInteger(R.integer.lb_search_bar_text_mode_background_alpha);
mTextHintColorSpeechMode = r.getColor(R.color.lb_search_bar_hint_speech_mode);
mTextHintColor = r.getColor(R.color.lb_search_bar_hint);
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
Methods Summary |
---|
public void | displayCompletions(java.util.List completions)Update the completion list shown by the IME
List<CompletionInfo> infos = new ArrayList<CompletionInfo>();
if (null != completions) {
for (String completion : completions) {
infos.add(new CompletionInfo(infos.size(), infos.size(), completion));
}
}
mInputMethodManager.displayCompletions(mSearchTextEditor,
infos.toArray(new CompletionInfo[] {}));
| private void | enforceAudioRecordPermission()
String permission = "android.permission.RECORD_AUDIO";
int res = getContext().checkCallingOrSelfPermission(permission);
if (PackageManager.PERMISSION_GRANTED != res) {
throw new IllegalStateException("android.permission.RECORD_AUDIO required for search");
}
| public android.graphics.drawable.Drawable | getBadgeDrawable()Returns the badge drawable
return mBadgeDrawable;
| public java.lang.CharSequence | getHint()Returns the current search bar hint text.
return mHint;
| public java.lang.String | getTitle()Returns the current title
return mTitle;
| private void | hideNativeKeyboard()
mInputMethodManager.hideSoftInputFromWindow(mSearchTextEditor.getWindowToken(),
InputMethodManager.RESULT_UNCHANGED_SHOWN);
| private boolean | isVoiceMode()
return mSpeechOrbView.isFocused();
| private void | loadSounds(android.content.Context context)
int[] sounds = {
R.raw.lb_voice_failure,
R.raw.lb_voice_open,
R.raw.lb_voice_no_input,
R.raw.lb_voice_success,
};
for (int sound : sounds) {
mSoundMap.put(sound, mSoundPool.load(context, sound, 1));
}
| protected void | onAttachedToWindow()
super.onAttachedToWindow();
if (DEBUG) Log.v(TAG, "Loading soundPool");
mSoundPool = new SoundPool(2, AudioManager.STREAM_SYSTEM, 0);
loadSounds(mContext);
| protected void | onDetachedFromWindow()
if (DEBUG) Log.v(TAG, "Releasing SoundPool");
mSoundPool.release();
super.onDetachedFromWindow();
| protected void | onFinishInflate()
super.onFinishInflate();
RelativeLayout items = (RelativeLayout)findViewById(R.id.lb_search_bar_items);
mBarBackground = items.getBackground();
mSearchTextEditor = (SearchEditText)findViewById(R.id.lb_search_text_editor);
mBadgeView = (ImageView)findViewById(R.id.lb_search_bar_badge);
if (null != mBadgeDrawable) {
mBadgeView.setImageDrawable(mBadgeDrawable);
}
mSearchTextEditor.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View view, boolean hasFocus) {
if (DEBUG) Log.v(TAG, "EditText.onFocusChange " + hasFocus);
if (hasFocus) {
showNativeKeyboard();
}
updateUi(hasFocus);
}
});
final Runnable mOnTextChangedRunnable = new Runnable() {
@Override
public void run() {
setSearchQueryInternal(mSearchTextEditor.getText().toString());
}
};
mSearchTextEditor.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
// don't propagate event during speech recognition.
if (mRecognizing) {
return;
}
// while IME opens, text editor becomes "" then restores to current value
mHandler.removeCallbacks(mOnTextChangedRunnable);
mHandler.post(mOnTextChangedRunnable);
}
@Override
public void afterTextChanged(Editable editable) {
}
});
mSearchTextEditor.setOnKeyboardDismissListener(
new SearchEditText.OnKeyboardDismissListener() {
@Override
public void onKeyboardDismiss() {
if (null != mSearchBarListener) {
mSearchBarListener.onKeyboardDismiss(mSearchQuery);
}
}
});
mSearchTextEditor.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView textView, int action, KeyEvent keyEvent) {
if (DEBUG) Log.v(TAG, "onEditorAction: " + action + " event: " + keyEvent);
boolean handled = true;
if ((EditorInfo.IME_ACTION_SEARCH == action ||
EditorInfo.IME_NULL == action) && null != mSearchBarListener) {
if (DEBUG) Log.v(TAG, "Action or enter pressed");
hideNativeKeyboard();
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (DEBUG) Log.v(TAG, "Delayed action handling (search)");
submitQuery();
}
}, 500);
} else if (EditorInfo.IME_ACTION_NONE == action && null != mSearchBarListener) {
if (DEBUG) Log.v(TAG, "Escaped North");
hideNativeKeyboard();
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (DEBUG) Log.v(TAG, "Delayed action handling (escape_north)");
mSearchBarListener.onKeyboardDismiss(mSearchQuery);
}
}, 500);
} else if (EditorInfo.IME_ACTION_GO == action) {
if (DEBUG) Log.v(TAG, "Voice Clicked");
hideNativeKeyboard();
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (DEBUG) Log.v(TAG, "Delayed action handling (voice_mode)");
mAutoStartRecognition = true;
mSpeechOrbView.requestFocus();
}
}, 500);
} else {
handled = false;
}
return handled;
}
});
mSearchTextEditor.setPrivateImeOptions("EscapeNorth=1;VoiceDismiss=1;");
mSpeechOrbView = (SpeechOrbView)findViewById(R.id.lb_search_bar_speech_orb);
mSpeechOrbView.setOnOrbClickedListener(new OnClickListener() {
@Override
public void onClick(View view) {
toggleRecognition();
}
});
mSpeechOrbView.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View view, boolean hasFocus) {
if (DEBUG) Log.v(TAG, "SpeechOrb.onFocusChange " + hasFocus);
if (hasFocus) {
hideNativeKeyboard();
if (mAutoStartRecognition) {
startRecognition();
mAutoStartRecognition = false;
}
} else {
stopRecognition();
}
updateUi(hasFocus);
}
});
updateUi(hasFocus());
updateHint();
| private void | play(int resId)
mHandler.post(new Runnable() {
@Override
public void run() {
int sound = mSoundMap.get(resId);
mSoundPool.play(sound, FULL_LEFT_VOLUME, FULL_RIGHT_VOLUME, DEFAULT_PRIORITY,
DO_NOT_LOOP, DEFAULT_RATE);
}
});
| private void | playSearchFailure()
play(R.raw.lb_voice_failure);
| private void | playSearchNoInput()
play(R.raw.lb_voice_no_input);
| private void | playSearchOpen()
play(R.raw.lb_voice_open);
| private void | playSearchSuccess()
play(R.raw.lb_voice_success);
| public void | setBadgeDrawable(android.graphics.drawable.Drawable drawable)Set the badge drawable showing inside the search bar.
mBadgeDrawable = drawable;
if (null != mBadgeView) {
mBadgeView.setImageDrawable(drawable);
if (null != drawable) {
mBadgeView.setVisibility(View.VISIBLE);
} else {
mBadgeView.setVisibility(View.GONE);
}
}
| public void | setNextFocusDownId(int viewId)
mSpeechOrbView.setNextFocusDownId(viewId);
mSearchTextEditor.setNextFocusDownId(viewId);
| public void | setSearchBarListener(android.support.v17.leanback.widget.SearchBar$SearchBarListener listener)Set a listener for when the term search changes
mSearchBarListener = listener;
| public void | setSearchQuery(java.lang.String query)Set the search query
stopRecognition();
mSearchTextEditor.setText(query);
setSearchQueryInternal(query);
| private void | setSearchQueryInternal(java.lang.String query)
if (DEBUG) Log.v(TAG, "setSearchQueryInternal " + query);
if (TextUtils.equals(mSearchQuery, query)) {
return;
}
mSearchQuery = query;
if (null != mSearchBarListener) {
mSearchBarListener.onSearchQueryChange(mSearchQuery);
}
| public void | setSpeechRecognitionCallback(SpeechRecognitionCallback request)
mSpeechRecognitionCallback = request;
if (mSpeechRecognitionCallback != null && mSpeechRecognizer != null) {
throw new IllegalStateException("Can't have speech recognizer and request");
}
| public void | setSpeechRecognizer(android.speech.SpeechRecognizer recognizer)Set the speech recognizer to be used when doing voice search. The Activity/Fragment is in
charge of creating and destroying the recognizer with its own lifecycle.
if (null != mSpeechRecognizer) {
mSpeechRecognizer.setRecognitionListener(null);
if (mListening) {
mSpeechRecognizer.cancel();
mListening = false;
}
}
mSpeechRecognizer = recognizer;
if (mSpeechRecognizer != null) {
enforceAudioRecordPermission();
}
if (mSpeechRecognitionCallback != null && mSpeechRecognizer != null) {
throw new IllegalStateException("Can't have speech recognizer and request");
}
| public void | setTitle(java.lang.String title)Set the title text used in the hint shown in the search bar.
mTitle = title;
updateHint();
| private void | showNativeKeyboard()
mHandler.post(new Runnable() {
@Override
public void run() {
mSearchTextEditor.requestFocusFromTouch();
mSearchTextEditor.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN,
mSearchTextEditor.getWidth(), mSearchTextEditor.getHeight(), 0));
mSearchTextEditor.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(), MotionEvent.ACTION_UP,
mSearchTextEditor.getWidth(), mSearchTextEditor.getHeight(), 0));
}
});
| public void | startRecognition()Start the voice recognition
if (DEBUG) Log.v(TAG, String.format("startRecognition (listening: %s, recognizing: %s)",
mListening, mRecognizing));
if (mRecognizing) return;
mRecognizing = true;
if (!hasFocus()) {
requestFocus();
}
if (mSpeechRecognitionCallback != null) {
mSearchTextEditor.setText("");
mSearchTextEditor.setHint("");
mSpeechRecognitionCallback.recognizeSpeech();
return;
}
if (null == mSpeechRecognizer) return;
// Request audio focus
int result = mAudioManager.requestAudioFocus(mAudioFocusChangeListener,
// Use the music stream.
AudioManager.STREAM_MUSIC,
// Request exclusive transient focus.
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
Log.w(TAG, "Could not get audio focus");
}
mSearchTextEditor.setText("");
Intent recognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
recognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
recognizerIntent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true);
mSpeechRecognizer.setRecognitionListener(new RecognitionListener() {
@Override
public void onReadyForSpeech(Bundle bundle) {
if (DEBUG) Log.v(TAG, "onReadyForSpeech");
mSpeechOrbView.showListening();
playSearchOpen();
}
@Override
public void onBeginningOfSpeech() {
if (DEBUG) Log.v(TAG, "onBeginningOfSpeech");
}
@Override
public void onRmsChanged(float rmsdB) {
if (DEBUG) Log.v(TAG, "onRmsChanged " + rmsdB);
int level = rmsdB < 0 ? 0 : (int)(10 * rmsdB);
mSpeechOrbView.setSoundLevel(level);
}
@Override
public void onBufferReceived(byte[] bytes) {
if (DEBUG) Log.v(TAG, "onBufferReceived " + bytes.length);
}
@Override
public void onEndOfSpeech() {
if (DEBUG) Log.v(TAG, "onEndOfSpeech");
}
@Override
public void onError(int error) {
if (DEBUG) Log.v(TAG, "onError " + error);
switch (error) {
case SpeechRecognizer.ERROR_NETWORK_TIMEOUT:
Log.w(TAG, "recognizer network timeout");
break;
case SpeechRecognizer.ERROR_NETWORK:
Log.w(TAG, "recognizer network error");
break;
case SpeechRecognizer.ERROR_AUDIO:
Log.w(TAG, "recognizer audio error");
break;
case SpeechRecognizer.ERROR_SERVER:
Log.w(TAG, "recognizer server error");
break;
case SpeechRecognizer.ERROR_CLIENT:
Log.w(TAG, "recognizer client error");
break;
case SpeechRecognizer.ERROR_SPEECH_TIMEOUT:
Log.w(TAG, "recognizer speech timeout");
break;
case SpeechRecognizer.ERROR_NO_MATCH:
Log.w(TAG, "recognizer no match");
break;
case SpeechRecognizer.ERROR_RECOGNIZER_BUSY:
Log.w(TAG, "recognizer busy");
break;
case SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS:
Log.w(TAG, "recognizer insufficient permissions");
break;
default:
Log.d(TAG, "recognizer other error");
break;
}
stopRecognition();
playSearchFailure();
}
@Override
public void onResults(Bundle bundle) {
if (DEBUG) Log.v(TAG, "onResults");
final ArrayList<String> matches =
bundle.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
if (matches != null) {
if (DEBUG) Log.v(TAG, "Got results" + matches);
mSearchQuery = matches.get(0);
mSearchTextEditor.setText(mSearchQuery);
submitQuery();
}
stopRecognition();
playSearchSuccess();
}
@Override
public void onPartialResults(Bundle bundle) {
ArrayList<String> results = bundle.getStringArrayList(
SpeechRecognizer.RESULTS_RECOGNITION);
if (DEBUG) Log.v(TAG, "onPartialResults " + bundle + " results " +
(results == null ? results : results.size()));
if (results == null || results.size() == 0) {
return;
}
// stableText: high confidence text from PartialResults, if any.
// Otherwise, existing stable text.
final String stableText = results.get(0);
if (DEBUG) Log.v(TAG, "onPartialResults stableText " + stableText);
// pendingText: low confidence text from PartialResults, if any.
// Otherwise, empty string.
final String pendingText = results.size() > 1 ? results.get(1) : null;
if (DEBUG) Log.v(TAG, "onPartialResults pendingText " + pendingText);
mSearchTextEditor.updateRecognizedText(stableText, pendingText);
}
@Override
public void onEvent(int i, Bundle bundle) {
}
});
mListening = true;
mSpeechRecognizer.startListening(recognizerIntent);
| public void | stopRecognition()Stop the recognition if already started
if (DEBUG) Log.v(TAG, String.format("stopRecognition (listening: %s, recognizing: %s)",
mListening, mRecognizing));
if (!mRecognizing) return;
// Edit text content was cleared when starting recogition; ensure the content is restored
// in error cases
mSearchTextEditor.setText(mSearchQuery);
mSearchTextEditor.setHint(mHint);
mRecognizing = false;
if (mSpeechRecognitionCallback != null || null == mSpeechRecognizer) return;
mSpeechOrbView.showNotListening();
if (mListening) {
mSpeechRecognizer.cancel();
mListening = false;
mAudioManager.abandonAudioFocus(mAudioFocusChangeListener);
}
mSpeechRecognizer.setRecognitionListener(null);
| private void | submitQuery()
if (!TextUtils.isEmpty(mSearchQuery) && null != mSearchBarListener) {
mSearchBarListener.onSearchQuerySubmit(mSearchQuery);
}
| private void | toggleRecognition()
if (mRecognizing) {
stopRecognition();
} else {
startRecognition();
}
| private void | updateHint()This will update the hint for the search bar properly depending on state and provided title
String title = getResources().getString(R.string.lb_search_bar_hint);
if (!TextUtils.isEmpty(mTitle)) {
if (isVoiceMode()) {
title = getResources().getString(R.string.lb_search_bar_hint_with_title_speech, mTitle);
} else {
title = getResources().getString(R.string.lb_search_bar_hint_with_title, mTitle);
}
} else if (isVoiceMode()) {
title = getResources().getString(R.string.lb_search_bar_hint_speech);
}
mHint = title;
if (mSearchTextEditor != null) {
mSearchTextEditor.setHint(mHint);
}
| private void | updateUi(boolean hasFocus)
if (hasFocus) {
mBarBackground.setAlpha(mBackgroundSpeechAlpha);
if (isVoiceMode()) {
mSearchTextEditor.setTextColor(mTextHintColorSpeechMode);
mSearchTextEditor.setHintTextColor(mTextHintColorSpeechMode);
} else {
mSearchTextEditor.setTextColor(mTextColorSpeechMode);
mSearchTextEditor.setHintTextColor(mTextHintColorSpeechMode);
}
} else {
mBarBackground.setAlpha(mBackgroundAlpha);
mSearchTextEditor.setTextColor(mTextColor);
mSearchTextEditor.setHintTextColor(mTextHintColor);
}
updateHint();
|
|