blob: 6d627b32ed715bf08ac186792eeebe18fff0fe5e [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package android.support.v17.leanback.widget;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.speech.RecognitionListener;
import android.speech.RecognizerIntent;
import android.speech.SpeechRecognizer;
import android.support.v17.leanback.R;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseIntArray;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
/**
* A search widget containing a search orb and a text entry view.
*
* <p>
* Note: When {@link SpeechRecognitionCallback} is not used, i.e. using {@link SpeechRecognizer},
* your application will need to declare android.permission.RECORD_AUDIO in manifest file.
* If your application target >= 23 and the device is running >= 23, it needs implement
* {@link SearchBarPermissionListener} where requests runtime permission.
* </p>
*/
public class SearchBar extends RelativeLayout {
static final String TAG = SearchBar.class.getSimpleName();
static final boolean DEBUG = false;
static final float FULL_LEFT_VOLUME = 1.0f;
static final float FULL_RIGHT_VOLUME = 1.0f;
static final int DEFAULT_PRIORITY = 1;
static final int DO_NOT_LOOP = 0;
static final float DEFAULT_RATE = 1.0f;
/**
* Interface for receiving notification of search query changes.
*/
public interface SearchBarListener {
/**
* Method invoked when the search bar detects a change in the query.
*
* @param query The current full query.
*/
public void onSearchQueryChange(String query);
/**
* <p>Method invoked when the search query is submitted.</p>
*
* <p>This method can be called without a preceeding onSearchQueryChange,
* in particular in the case of a voice input.</p>
*
* @param query The query being submitted.
*/
public void onSearchQuerySubmit(String query);
/**
* Method invoked when the IME is being dismissed.
*
* @param query The query set in the search bar at the time the IME is being dismissed.
*/
public void onKeyboardDismiss(String query);
}
/**
* Interface that handles runtime permissions requests. App sets listener on SearchBar via
* {@link #setPermissionListener(SearchBarPermissionListener)}.
*/
public interface SearchBarPermissionListener {
/**
* Method invoked when SearchBar asks for "android.permission.RECORD_AUDIO" runtime
* permission.
*/
void requestAudioPermission();
}
private AudioManager.OnAudioFocusChangeListener mAudioFocusChangeListener =
new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int focusChange) {
stopRecognition();
}
};
SearchBarListener mSearchBarListener;
SearchEditText mSearchTextEditor;
SpeechOrbView mSpeechOrbView;
private ImageView mBadgeView;
String mSearchQuery;
private String mHint;
private String mTitle;
private Drawable mBadgeDrawable;
final Handler mHandler = new Handler();
private final InputMethodManager mInputMethodManager;
boolean mAutoStartRecognition = false;
private 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 SpeechRecognizer mSpeechRecognizer;
private SpeechRecognitionCallback mSpeechRecognitionCallback;
private boolean mListening;
SoundPool mSoundPool;
SparseIntArray mSoundMap = new SparseIntArray();
boolean mRecognizing = false;
private final Context mContext;
private AudioManager mAudioManager;
private SearchBarPermissionListener mPermissionListener;
public SearchBar(Context context) {
this(context, null);
}
public SearchBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SearchBar(Context context, 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);
}
@Override
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();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (DEBUG) Log.v(TAG, "Loading soundPool");
mSoundPool = new SoundPool(2, AudioManager.STREAM_SYSTEM, 0);
loadSounds(mContext);
}
@Override
protected void onDetachedFromWindow() {
stopRecognition();
if (DEBUG) Log.v(TAG, "Releasing SoundPool");
mSoundPool.release();
super.onDetachedFromWindow();
}
/**
* Sets a listener for when the term search changes
* @param listener
*/
public void setSearchBarListener(SearchBarListener listener) {
mSearchBarListener = listener;
}
/**
* Sets the search query
* @param query the search query to use
*/
public void setSearchQuery(String query) {
stopRecognition();
mSearchTextEditor.setText(query);
setSearchQueryInternal(query);
}
void setSearchQueryInternal(String query) {
if (DEBUG) Log.v(TAG, "setSearchQueryInternal " + query);
if (TextUtils.equals(mSearchQuery, query)) {
return;
}
mSearchQuery = query;
if (null != mSearchBarListener) {
mSearchBarListener.onSearchQueryChange(mSearchQuery);
}
}
/**
* Sets the title text used in the hint shown in the search bar.
* @param title The hint to use.
*/
public void setTitle(String title) {
mTitle = title;
updateHint();
}
/**
* Sets background color of not-listening state search orb.
*
* @param colors SearchOrbView.Colors.
*/
public void setSearchAffordanceColors(SearchOrbView.Colors colors) {
if (mSpeechOrbView != null) {
mSpeechOrbView.setNotListeningOrbColors(colors);
}
}
/**
* Sets background color of listening state search orb.
*
* @param colors SearchOrbView.Colors.
*/
public void setSearchAffordanceColorsInListening(SearchOrbView.Colors colors) {
if (mSpeechOrbView != null) {
mSpeechOrbView.setListeningOrbColors(colors);
}
}
/**
* Returns the current title
*/
public String getTitle() {
return mTitle;
}
/**
* Returns the current search bar hint text.
*/
public CharSequence getHint() {
return mHint;
}
/**
* Sets the badge drawable showing inside the search bar.
* @param drawable The drawable to be used in the search bar.
*/
public void setBadgeDrawable(Drawable drawable) {
mBadgeDrawable = drawable;
if (null != mBadgeView) {
mBadgeView.setImageDrawable(drawable);
if (null != drawable) {
mBadgeView.setVisibility(View.VISIBLE);
} else {
mBadgeView.setVisibility(View.GONE);
}
}
}
/**
* Returns the badge drawable
*/
public Drawable getBadgeDrawable() {
return mBadgeDrawable;
}
/**
* Updates the completion list shown by the IME
*
* @param completions list of completions shown in the IME, can be null or empty to clear them
*/
public void displayCompletions(List<String> completions) {
List<CompletionInfo> infos = new ArrayList<>();
if (null != completions) {
for (String completion : completions) {
infos.add(new CompletionInfo(infos.size(), infos.size(), completion));
}
}
CompletionInfo[] array = new CompletionInfo[infos.size()];
displayCompletions(infos.toArray(array));
}
/**
* Updates the completion list shown by the IME
*
* @param completions list of completions shown in the IME, can be null or empty to clear them
*/
public void displayCompletions(CompletionInfo[] completions) {
mInputMethodManager.displayCompletions(mSearchTextEditor, completions);
}
/**
* Sets 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.
*
* @param recognizer a SpeechRecognizer
*/
public void setSpeechRecognizer(SpeechRecognizer recognizer) {
stopRecognition();
if (null != mSpeechRecognizer) {
mSpeechRecognizer.setRecognitionListener(null);
if (mListening) {
mSpeechRecognizer.cancel();
mListening = false;
}
}
mSpeechRecognizer = recognizer;
if (mSpeechRecognitionCallback != null && mSpeechRecognizer != null) {
throw new IllegalStateException("Can't have speech recognizer and request");
}
}
/**
* Sets the speech recognition callback.
*/
public void setSpeechRecognitionCallback(SpeechRecognitionCallback request) {
mSpeechRecognitionCallback = request;
if (mSpeechRecognitionCallback != null && mSpeechRecognizer != null) {
throw new IllegalStateException("Can't have speech recognizer and request");
}
}
void hideNativeKeyboard() {
mInputMethodManager.hideSoftInputFromWindow(mSearchTextEditor.getWindowToken(),
InputMethodManager.RESULT_UNCHANGED_SHOWN);
}
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));
}
});
}
/**
* This will update the hint for the search bar properly depending on state and provided title
*/
private void updateHint() {
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);
}
}
void toggleRecognition() {
if (mRecognizing) {
stopRecognition();
} else {
startRecognition();
}
}
/**
* Returns true if is not running Recognizer, false otherwise.
* @return True if is not running Recognizer, false otherwise.
*/
public boolean isRecognizing() {
return mRecognizing;
}
/**
* Stops the speech recognition, if already started.
*/
public void stopRecognition() {
if (DEBUG) Log.v(TAG, String.format("stopRecognition (listening: %s, recognizing: %s)",
mListening, mRecognizing));
if (!mRecognizing) return;
// Edit text content was cleared when starting recognition; 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);
}
/**
* Sets listener that handles runtime permission requests.
* @param listener Listener that handles runtime permission requests.
*/
public void setPermissionListener(SearchBarPermissionListener listener) {
mPermissionListener = listener;
}
public void startRecognition() {
if (DEBUG) Log.v(TAG, String.format("startRecognition (listening: %s, recognizing: %s)",
mListening, mRecognizing));
if (mRecognizing) return;
if (!hasFocus()) {
requestFocus();
}
if (mSpeechRecognitionCallback != null) {
mSearchTextEditor.setText("");
mSearchTextEditor.setHint("");
mSpeechRecognitionCallback.recognizeSpeech();
mRecognizing = true;
return;
}
if (null == mSpeechRecognizer) return;
int res = getContext().checkCallingOrSelfPermission(Manifest.permission.RECORD_AUDIO);
if (PackageManager.PERMISSION_GRANTED != res) {
if (Build.VERSION.SDK_INT >= 23 && mPermissionListener != null) {
mPermissionListener.requestAudioPermission();
return;
} else {
throw new IllegalStateException(Manifest.permission.RECORD_AUDIO
+ " required for search");
}
}
mRecognizing = true;
// 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);
}
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();
}
private boolean isVoiceMode() {
return mSpeechOrbView.isFocused();
}
void submitQuery() {
if (!TextUtils.isEmpty(mSearchQuery) && null != mSearchBarListener) {
mSearchBarListener.onSearchQuerySubmit(mSearchQuery);
}
}
private void loadSounds(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));
}
}
private void play(final 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);
}
});
}
void playSearchOpen() {
play(R.raw.lb_voice_open);
}
void playSearchFailure() {
play(R.raw.lb_voice_failure);
}
private void playSearchNoInput() {
play(R.raw.lb_voice_no_input);
}
void playSearchSuccess() {
play(R.raw.lb_voice_success);
}
@Override
public void setNextFocusDownId(int viewId) {
mSpeechOrbView.setNextFocusDownId(viewId);
mSearchTextEditor.setNextFocusDownId(viewId);
}
}