185c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet/*
285c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet * Copyright (C) 2014 The Android Open Source Project
385c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet *
485c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
585c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet * in compliance with the License. You may obtain a copy of the License at
685c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet *
785c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet * http://www.apache.org/licenses/LICENSE-2.0
885c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet *
985c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet * Unless required by applicable law or agreed to in writing, software distributed under the License
1085c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
1185c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet * or implied. See the License for the specific language governing permissions and limitations under
1285c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet * the License.
1385c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet */
1485c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichetpackage android.support.v17.leanback.widget;
1585c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet
16ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Guimport android.Manifest;
1785c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichetimport android.content.Context;
1825aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichetimport android.content.Intent;
1910960072d3c1f9c7f42f9ae77adbfb12f9aed138Jerome Poichetimport android.content.pm.PackageManager;
20a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichetimport android.content.res.Resources;
21a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichetimport android.graphics.Color;
22514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichetimport android.graphics.drawable.Drawable;
23a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichetimport android.media.AudioManager;
24a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichetimport android.media.SoundPool;
25ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Guimport android.os.Build;
2625aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichetimport android.os.Bundle;
2785c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichetimport android.os.Handler;
2885c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichetimport android.os.SystemClock;
2925aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichetimport android.speech.RecognitionListener;
3025aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichetimport android.speech.RecognizerIntent;
3125aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichetimport android.speech.SpeechRecognizer;
3285c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichetimport android.text.Editable;
33514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichetimport android.text.TextUtils;
3485c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichetimport android.text.TextWatcher;
3585c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichetimport android.util.AttributeSet;
360075dfe4e812a633be39018e1d6a85e5c6e58de5Jerome Poichetimport android.util.Log;
37a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichetimport android.util.SparseIntArray;
3825aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichetimport android.view.LayoutInflater;
39a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichetimport android.view.ViewGroup;
40e5f2388b8d24876ebbd6daf302487bf452245d50Jerome Poichetimport android.view.inputmethod.CompletionInfo;
41c9422fb7ac972aee1f05c5ec0f07c2ec24cc771bJerome Poichetimport android.view.inputmethod.EditorInfo;
42c9422fb7ac972aee1f05c5ec0f07c2ec24cc771bJerome Poichetimport android.view.KeyEvent;
4385c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichetimport android.view.MotionEvent;
4485c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichetimport android.view.View;
45514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichetimport android.widget.ImageView;
4625aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichetimport android.view.inputmethod.InputMethodManager;
4785c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichetimport android.widget.RelativeLayout;
48c9422fb7ac972aee1f05c5ec0f07c2ec24cc771bJerome Poichetimport android.widget.TextView;
4985c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet
50ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Guimport android.support.v17.leanback.R;
51ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu
5225aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichetimport java.util.ArrayList;
53e5f2388b8d24876ebbd6daf302487bf452245d50Jerome Poichetimport java.util.List;
5425aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
5585c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet/**
56a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout * A search widget containing a search orb and a text entry view.
5710960072d3c1f9c7f42f9ae77adbfb12f9aed138Jerome Poichet *
58ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu * <p>
59ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu * Note: When {@link SpeechRecognitionCallback} is not used, i.e. using {@link SpeechRecognizer},
60ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu * your application will need to declare android.permission.RECORD_AUDIO in manifest file.
61ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu * If your application target >= 23 and the device is running >= 23, it needs implement
62ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu * {@link SearchBarPermissionListener} where requests runtime permission.
63ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu * </p>
6485c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet */
6585c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichetpublic class SearchBar extends RelativeLayout {
6699ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas    static final String TAG = SearchBar.class.getSimpleName();
6799ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas    static final boolean DEBUG = false;
6885c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet
6999ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas    static final float FULL_LEFT_VOLUME = 1.0f;
7099ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas    static final float FULL_RIGHT_VOLUME = 1.0f;
7199ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas    static final int DEFAULT_PRIORITY = 1;
7299ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas    static final int DO_NOT_LOOP = 0;
7399ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas    static final float DEFAULT_RATE = 1.0f;
7425aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
7585c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet    /**
76a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Interface for receiving notification of search query changes.
7785c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet     */
7885c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet    public interface SearchBarListener {
7985c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet
8085c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet        /**
813c2998d502e4e2586d0bfc52ff5d1f2725fa9e6dJerome Poichet         * Method invoked when the search bar detects a change in the query.
823c2998d502e4e2586d0bfc52ff5d1f2725fa9e6dJerome Poichet         *
833c2998d502e4e2586d0bfc52ff5d1f2725fa9e6dJerome Poichet         * @param query The current full query.
8485c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet         */
853c2998d502e4e2586d0bfc52ff5d1f2725fa9e6dJerome Poichet        public void onSearchQueryChange(String query);
863c2998d502e4e2586d0bfc52ff5d1f2725fa9e6dJerome Poichet
873c2998d502e4e2586d0bfc52ff5d1f2725fa9e6dJerome Poichet        /**
8810960072d3c1f9c7f42f9ae77adbfb12f9aed138Jerome Poichet         * <p>Method invoked when the search query is submitted.</p>
8910960072d3c1f9c7f42f9ae77adbfb12f9aed138Jerome Poichet         *
9010960072d3c1f9c7f42f9ae77adbfb12f9aed138Jerome Poichet         * <p>This method can be called without a preceeding onSearchQueryChange,
9110960072d3c1f9c7f42f9ae77adbfb12f9aed138Jerome Poichet         * in particular in the case of a voice input.</p>
923c2998d502e4e2586d0bfc52ff5d1f2725fa9e6dJerome Poichet         *
933c2998d502e4e2586d0bfc52ff5d1f2725fa9e6dJerome Poichet         * @param query The query being submitted.
943c2998d502e4e2586d0bfc52ff5d1f2725fa9e6dJerome Poichet         */
953c2998d502e4e2586d0bfc52ff5d1f2725fa9e6dJerome Poichet        public void onSearchQuerySubmit(String query);
963c2998d502e4e2586d0bfc52ff5d1f2725fa9e6dJerome Poichet
973c2998d502e4e2586d0bfc52ff5d1f2725fa9e6dJerome Poichet        /**
983c2998d502e4e2586d0bfc52ff5d1f2725fa9e6dJerome Poichet         * Method invoked when the IME is being dismissed.
993c2998d502e4e2586d0bfc52ff5d1f2725fa9e6dJerome Poichet         *
1003c2998d502e4e2586d0bfc52ff5d1f2725fa9e6dJerome Poichet         * @param query The query set in the search bar at the time the IME is being dismissed.
1013c2998d502e4e2586d0bfc52ff5d1f2725fa9e6dJerome Poichet         */
1023c2998d502e4e2586d0bfc52ff5d1f2725fa9e6dJerome Poichet        public void onKeyboardDismiss(String query);
103ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu
104ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu    }
105ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu
106ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu    /**
107ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu     * Interface that handles runtime permissions requests. App sets listener on SearchBar via
108ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu     * {@link #setPermissionListener(SearchBarPermissionListener)}.
109ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu     */
110ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu    public interface SearchBarPermissionListener {
111ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu
112ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu        /**
113ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu         * Method invoked when SearchBar asks for "android.permission.RECORD_AUDIO" runtime
114ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu         * permission.
115ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu         */
116ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu        void requestAudioPermission();
117ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu
11885c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet    }
11985c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet
120e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet    private AudioManager.OnAudioFocusChangeListener mAudioFocusChangeListener =
121e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet            new AudioManager.OnAudioFocusChangeListener() {
122e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                @Override
123e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                public void onAudioFocusChange(int focusChange) {
1247c8283f6542b1045377e88566d363b4c2c83088bCraig Stout                    stopRecognition();
125e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                }
126e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet            };
127e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet
12899ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas    SearchBarListener mSearchBarListener;
12999ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas    SearchEditText mSearchTextEditor;
13099ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas    SpeechOrbView mSpeechOrbView;
131514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet    private ImageView mBadgeView;
13299ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas    String mSearchQuery;
133490691bd414dbb2093dc6cb56ae63ae9b04e70e3Craig Stout    private String mHint;
134514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet    private String mTitle;
135514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet    private Drawable mBadgeDrawable;
13699ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas    final Handler mHandler = new Handler();
13725aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet    private final InputMethodManager mInputMethodManager;
13899ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas    boolean mAutoStartRecognition = false;
139a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet    private Drawable mBarBackground;
140a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet
141a6c18317f09969688b7d329a5b4ce35c8d648e4fCraig Stout    private final int mTextColor;
142a6c18317f09969688b7d329a5b4ce35c8d648e4fCraig Stout    private final int mTextColorSpeechMode;
143a6c18317f09969688b7d329a5b4ce35c8d648e4fCraig Stout    private final int mTextHintColor;
144a6c18317f09969688b7d329a5b4ce35c8d648e4fCraig Stout    private final int mTextHintColorSpeechMode;
145a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet    private int mBackgroundAlpha;
146a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet    private int mBackgroundSpeechAlpha;
147a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet    private int mBarHeight;
148a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet    private SpeechRecognizer mSpeechRecognizer;
149361955cd7c040bf30330c8e21d9016c747a94473Craig Stout    private SpeechRecognitionCallback mSpeechRecognitionCallback;
150a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet    private boolean mListening;
15199ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas    SoundPool mSoundPool;
15299ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas    SparseIntArray mSoundMap = new SparseIntArray();
15399ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas    boolean mRecognizing = false;
154c8737e35c611362db621b25656b57be0c2b8a306Jerome Poichet    private final Context mContext;
155e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet    private AudioManager mAudioManager;
156ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu    private SearchBarPermissionListener mPermissionListener;
15785c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet
15885c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet    public SearchBar(Context context) {
15985c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet        this(context, null);
16085c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet    }
16185c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet
16285c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet    public SearchBar(Context context, AttributeSet attrs) {
16385c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet        this(context, attrs, 0);
16485c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet    }
16585c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet
16685c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet    public SearchBar(Context context, AttributeSet attrs, int defStyle) {
16785c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet        super(context, attrs, defStyle);
168c8737e35c611362db621b25656b57be0c2b8a306Jerome Poichet        mContext = context;
16910960072d3c1f9c7f42f9ae77adbfb12f9aed138Jerome Poichet
1702ad1027496fefed641f91f3cde2f8c8b468bca0cJérôme Poichet        Resources r = getResources();
17125aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
17225aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet        LayoutInflater inflater = LayoutInflater.from(getContext());
17325aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet        inflater.inflate(R.layout.lb_search_bar, this, true);
17425aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
1752ad1027496fefed641f91f3cde2f8c8b468bca0cJérôme Poichet        mBarHeight = getResources().getDimensionPixelSize(R.dimen.lb_search_bar_height);
176a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet        RelativeLayout.LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
177a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet                mBarHeight);
178a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet        params.addRule(ALIGN_PARENT_TOP, RelativeLayout.TRUE);
179a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet        setLayoutParams(params);
180a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet        setBackgroundColor(Color.TRANSPARENT);
181a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet        setClipChildren(false);
182a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet
18385c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet        mSearchQuery = "";
18425aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet        mInputMethodManager =
18525aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE);
1862ad1027496fefed641f91f3cde2f8c8b468bca0cJérôme Poichet
187a6c18317f09969688b7d329a5b4ce35c8d648e4fCraig Stout        mTextColorSpeechMode = r.getColor(R.color.lb_search_bar_text_speech_mode);
188a6c18317f09969688b7d329a5b4ce35c8d648e4fCraig Stout        mTextColor = r.getColor(R.color.lb_search_bar_text);
189a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet
190a6c18317f09969688b7d329a5b4ce35c8d648e4fCraig Stout        mBackgroundSpeechAlpha = r.getInteger(R.integer.lb_search_bar_speech_mode_background_alpha);
191a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet        mBackgroundAlpha = r.getInteger(R.integer.lb_search_bar_text_mode_background_alpha);
192a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet
193a6c18317f09969688b7d329a5b4ce35c8d648e4fCraig Stout        mTextHintColorSpeechMode = r.getColor(R.color.lb_search_bar_hint_speech_mode);
194a6c18317f09969688b7d329a5b4ce35c8d648e4fCraig Stout        mTextHintColor = r.getColor(R.color.lb_search_bar_hint);
195e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet
196e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
19785c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet    }
19885c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet
19985c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet    @Override
20085c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet    protected void onFinishInflate() {
20185c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet        super.onFinishInflate();
20285c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet
203a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet        RelativeLayout items = (RelativeLayout)findViewById(R.id.lb_search_bar_items);
204a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet        mBarBackground = items.getBackground();
205a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet
2063c2998d502e4e2586d0bfc52ff5d1f2725fa9e6dJerome Poichet        mSearchTextEditor = (SearchEditText)findViewById(R.id.lb_search_text_editor);
207514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet        mBadgeView = (ImageView)findViewById(R.id.lb_search_bar_badge);
208514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet        if (null != mBadgeDrawable) {
209514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet            mBadgeView.setImageDrawable(mBadgeDrawable);
210514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet        }
211514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet
21285c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet        mSearchTextEditor.setOnFocusChangeListener(new OnFocusChangeListener() {
21385c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet            @Override
21485c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet            public void onFocusChange(View view, boolean hasFocus) {
21525aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                if (DEBUG) Log.v(TAG, "EditText.onFocusChange " + hasFocus);
21685c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet                if (hasFocus) {
21785c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet                    showNativeKeyboard();
21885c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet                }
219936867a6a063492207bd3cd68a1aa870dd5c9da5Craig Stout                updateUi(hasFocus);
22085c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet            }
22185c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet        });
222f52e0718c20bedc86f0b67f6b3660e737d787bd4Dake Gu        final Runnable mOnTextChangedRunnable = new Runnable() {
223f52e0718c20bedc86f0b67f6b3660e737d787bd4Dake Gu            @Override
224f52e0718c20bedc86f0b67f6b3660e737d787bd4Dake Gu            public void run() {
225f52e0718c20bedc86f0b67f6b3660e737d787bd4Dake Gu                setSearchQueryInternal(mSearchTextEditor.getText().toString());
226f52e0718c20bedc86f0b67f6b3660e737d787bd4Dake Gu            }
227f52e0718c20bedc86f0b67f6b3660e737d787bd4Dake Gu        };
22885c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet        mSearchTextEditor.addTextChangedListener(new TextWatcher() {
22985c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet            @Override
23085c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet            public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
23185c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet            }
23285c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet
23385c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet            @Override
23485c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet            public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
23522e46a46e20c53b2e2d23dca792534ee3434c954Dake Gu                // don't propagate event during speech recognition.
23622e46a46e20c53b2e2d23dca792534ee3434c954Dake Gu                if (mRecognizing) {
23722e46a46e20c53b2e2d23dca792534ee3434c954Dake Gu                    return;
23822e46a46e20c53b2e2d23dca792534ee3434c954Dake Gu                }
239f52e0718c20bedc86f0b67f6b3660e737d787bd4Dake Gu                // while IME opens,  text editor becomes "" then restores to current value
240f52e0718c20bedc86f0b67f6b3660e737d787bd4Dake Gu                mHandler.removeCallbacks(mOnTextChangedRunnable);
241f52e0718c20bedc86f0b67f6b3660e737d787bd4Dake Gu                mHandler.post(mOnTextChangedRunnable);
24285c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet            }
24385c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet
24485c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet            @Override
24585c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet            public void afterTextChanged(Editable editable) {
24685c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet
24785c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet            }
24885c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet        });
2493c2998d502e4e2586d0bfc52ff5d1f2725fa9e6dJerome Poichet        mSearchTextEditor.setOnKeyboardDismissListener(
250c9422fb7ac972aee1f05c5ec0f07c2ec24cc771bJerome Poichet                new SearchEditText.OnKeyboardDismissListener() {
251c9422fb7ac972aee1f05c5ec0f07c2ec24cc771bJerome Poichet                    @Override
252c9422fb7ac972aee1f05c5ec0f07c2ec24cc771bJerome Poichet                    public void onKeyboardDismiss() {
253c9422fb7ac972aee1f05c5ec0f07c2ec24cc771bJerome Poichet                        if (null != mSearchBarListener) {
254c9422fb7ac972aee1f05c5ec0f07c2ec24cc771bJerome Poichet                            mSearchBarListener.onKeyboardDismiss(mSearchQuery);
255c9422fb7ac972aee1f05c5ec0f07c2ec24cc771bJerome Poichet                        }
2563c2998d502e4e2586d0bfc52ff5d1f2725fa9e6dJerome Poichet                    }
257c9422fb7ac972aee1f05c5ec0f07c2ec24cc771bJerome Poichet                });
25825aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
259c9422fb7ac972aee1f05c5ec0f07c2ec24cc771bJerome Poichet        mSearchTextEditor.setOnEditorActionListener(new TextView.OnEditorActionListener() {
260c9422fb7ac972aee1f05c5ec0f07c2ec24cc771bJerome Poichet            @Override
261c9422fb7ac972aee1f05c5ec0f07c2ec24cc771bJerome Poichet            public boolean onEditorAction(TextView textView, int action, KeyEvent keyEvent) {
26225aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                if (DEBUG) Log.v(TAG, "onEditorAction: " + action + " event: " + keyEvent);
26325aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                boolean handled = true;
264e2047098f696c81f6435cdd588a62d8ab5829c7dCraig Stout                if ((EditorInfo.IME_ACTION_SEARCH == action ||
265e2047098f696c81f6435cdd588a62d8ab5829c7dCraig Stout                        EditorInfo.IME_NULL == action) && null != mSearchBarListener) {
266e2047098f696c81f6435cdd588a62d8ab5829c7dCraig Stout                    if (DEBUG) Log.v(TAG, "Action or enter pressed");
26725aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                    hideNativeKeyboard();
26825aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                    mHandler.postDelayed(new Runnable() {
26925aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                        @Override
27025aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                        public void run() {
27125aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                            if (DEBUG) Log.v(TAG, "Delayed action handling (search)");
272a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet                            submitQuery();
27325aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                        }
27425aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                    }, 500);
27525aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
27625aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                } else if (EditorInfo.IME_ACTION_NONE == action && null != mSearchBarListener) {
27725aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                    if (DEBUG) Log.v(TAG, "Escaped North");
27825aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                    hideNativeKeyboard();
27925aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                    mHandler.postDelayed(new Runnable() {
28025aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                        @Override
28125aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                        public void run() {
28225aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                            if (DEBUG) Log.v(TAG, "Delayed action handling (escape_north)");
28325aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                            mSearchBarListener.onKeyboardDismiss(mSearchQuery);
28425aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                        }
28525aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                    }, 500);
28625aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                } else if (EditorInfo.IME_ACTION_GO == action) {
28725aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                    if (DEBUG) Log.v(TAG, "Voice Clicked");
28825aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                        hideNativeKeyboard();
28925aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                        mHandler.postDelayed(new Runnable() {
29025aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                            @Override
29125aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                            public void run() {
29225aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                                if (DEBUG) Log.v(TAG, "Delayed action handling (voice_mode)");
29325aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                                mAutoStartRecognition = true;
29425aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                                mSpeechOrbView.requestFocus();
29525aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                            }
29625aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                        }, 500);
29725aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                } else {
29825aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                    handled = false;
29925aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                }
30025aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
30125aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                return handled;
30225aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            }
30325aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet        });
30425aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
30525aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet        mSearchTextEditor.setPrivateImeOptions("EscapeNorth=1;VoiceDismiss=1;");
30625aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
30725aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet        mSpeechOrbView = (SpeechOrbView)findViewById(R.id.lb_search_bar_speech_orb);
30825aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet        mSpeechOrbView.setOnOrbClickedListener(new OnClickListener() {
30925aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            @Override
31025aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            public void onClick(View view) {
311c8737e35c611362db621b25656b57be0c2b8a306Jerome Poichet                toggleRecognition();
31225aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            }
31325aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet        });
31425aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet        mSpeechOrbView.setOnFocusChangeListener(new OnFocusChangeListener() {
31525aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            @Override
31625aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            public void onFocusChange(View view, boolean hasFocus) {
31725aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                if (DEBUG) Log.v(TAG, "SpeechOrb.onFocusChange " + hasFocus);
31825aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                if (hasFocus) {
31925aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                    hideNativeKeyboard();
32025aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                    if (mAutoStartRecognition) {
32125aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                        startRecognition();
32225aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                        mAutoStartRecognition = false;
32325aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                    }
32425aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                } else {
32525aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                    stopRecognition();
3263c2998d502e4e2586d0bfc52ff5d1f2725fa9e6dJerome Poichet                }
327936867a6a063492207bd3cd68a1aa870dd5c9da5Craig Stout                updateUi(hasFocus);
328c9422fb7ac972aee1f05c5ec0f07c2ec24cc771bJerome Poichet            }
3293c2998d502e4e2586d0bfc52ff5d1f2725fa9e6dJerome Poichet        });
330514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet
331936867a6a063492207bd3cd68a1aa870dd5c9da5Craig Stout        updateUi(hasFocus());
332514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet        updateHint();
3330075dfe4e812a633be39018e1d6a85e5c6e58de5Jerome Poichet    }
3340075dfe4e812a633be39018e1d6a85e5c6e58de5Jerome Poichet
3350075dfe4e812a633be39018e1d6a85e5c6e58de5Jerome Poichet    @Override
3360075dfe4e812a633be39018e1d6a85e5c6e58de5Jerome Poichet    protected void onAttachedToWindow() {
3370075dfe4e812a633be39018e1d6a85e5c6e58de5Jerome Poichet        super.onAttachedToWindow();
338c8737e35c611362db621b25656b57be0c2b8a306Jerome Poichet        if (DEBUG) Log.v(TAG, "Loading soundPool");
339c8737e35c611362db621b25656b57be0c2b8a306Jerome Poichet        mSoundPool = new SoundPool(2, AudioManager.STREAM_SYSTEM, 0);
340c8737e35c611362db621b25656b57be0c2b8a306Jerome Poichet        loadSounds(mContext);
34185c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet    }
34285c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet
343c8737e35c611362db621b25656b57be0c2b8a306Jerome Poichet    @Override
344c8737e35c611362db621b25656b57be0c2b8a306Jerome Poichet    protected void onDetachedFromWindow() {
3457c8283f6542b1045377e88566d363b4c2c83088bCraig Stout        stopRecognition();
346c8737e35c611362db621b25656b57be0c2b8a306Jerome Poichet        if (DEBUG) Log.v(TAG, "Releasing SoundPool");
347c8737e35c611362db621b25656b57be0c2b8a306Jerome Poichet        mSoundPool.release();
348c8737e35c611362db621b25656b57be0c2b8a306Jerome Poichet        super.onDetachedFromWindow();
349c8737e35c611362db621b25656b57be0c2b8a306Jerome Poichet    }
350c8737e35c611362db621b25656b57be0c2b8a306Jerome Poichet
35185c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet    /**
352a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Sets a listener for when the term search changes
35385c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet     * @param listener
35485c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet     */
35585c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet    public void setSearchBarListener(SearchBarListener listener) {
35685c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet        mSearchBarListener = listener;
35785c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet    }
35885c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet
35985c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet    /**
360a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Sets the search query
36185c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet     * @param query the search query to use
36285c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet     */
36385c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet    public void setSearchQuery(String query) {
36422e46a46e20c53b2e2d23dca792534ee3434c954Dake Gu        stopRecognition();
365963e336e8839f26c739007242caf7ca4e50e792dCraig Stout        mSearchTextEditor.setText(query);
366361955cd7c040bf30330c8e21d9016c747a94473Craig Stout        setSearchQueryInternal(query);
367361955cd7c040bf30330c8e21d9016c747a94473Craig Stout    }
368361955cd7c040bf30330c8e21d9016c747a94473Craig Stout
36999ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas    void setSearchQueryInternal(String query) {
370963e336e8839f26c739007242caf7ca4e50e792dCraig Stout        if (DEBUG) Log.v(TAG, "setSearchQueryInternal " + query);
371e33fac64498a83e1138b0e0aad732ea29e2f8a81Jerome Poichet        if (TextUtils.equals(mSearchQuery, query)) {
37285c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet            return;
37385c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet        }
37485c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet        mSearchQuery = query;
375361955cd7c040bf30330c8e21d9016c747a94473Craig Stout
37685c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet        if (null != mSearchBarListener) {
3773c2998d502e4e2586d0bfc52ff5d1f2725fa9e6dJerome Poichet            mSearchBarListener.onSearchQueryChange(mSearchQuery);
37885c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet        }
37985c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet    }
38085c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet
3813c2998d502e4e2586d0bfc52ff5d1f2725fa9e6dJerome Poichet    /**
382a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Sets the title text used in the hint shown in the search bar.
383514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet     * @param title The hint to use.
384514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet     */
385514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet    public void setTitle(String title) {
386514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet        mTitle = title;
387514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet        updateHint();
388514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet    }
389514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet
390514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet    /**
391514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet     * Returns the current title
3923c2998d502e4e2586d0bfc52ff5d1f2725fa9e6dJerome Poichet     */
393514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet    public String getTitle() {
394514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet        return mTitle;
395514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet    }
396514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet
397514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet    /**
398963e336e8839f26c739007242caf7ca4e50e792dCraig Stout     * Returns the current search bar hint text.
399963e336e8839f26c739007242caf7ca4e50e792dCraig Stout     */
400963e336e8839f26c739007242caf7ca4e50e792dCraig Stout    public CharSequence getHint() {
401490691bd414dbb2093dc6cb56ae63ae9b04e70e3Craig Stout        return mHint;
402963e336e8839f26c739007242caf7ca4e50e792dCraig Stout    }
403963e336e8839f26c739007242caf7ca4e50e792dCraig Stout
404963e336e8839f26c739007242caf7ca4e50e792dCraig Stout    /**
405a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Sets the badge drawable showing inside the search bar.
406514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet     * @param drawable The drawable to be used in the search bar.
407514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet     */
408514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet    public void setBadgeDrawable(Drawable drawable) {
409514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet        mBadgeDrawable = drawable;
410514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet        if (null != mBadgeView) {
411514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet            mBadgeView.setImageDrawable(drawable);
412514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet            if (null != drawable) {
413514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet                mBadgeView.setVisibility(View.VISIBLE);
414514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet            } else {
415514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet                mBadgeView.setVisibility(View.GONE);
416514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet            }
417514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet        }
418514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet    }
419514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet
420514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet    /**
421514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet     * Returns the badge drawable
422514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet     */
423514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet    public Drawable getBadgeDrawable() {
424514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet        return mBadgeDrawable;
4253c2998d502e4e2586d0bfc52ff5d1f2725fa9e6dJerome Poichet    }
4263c2998d502e4e2586d0bfc52ff5d1f2725fa9e6dJerome Poichet
427e5f2388b8d24876ebbd6daf302487bf452245d50Jerome Poichet    /**
428a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Updates the completion list shown by the IME
429e5f2388b8d24876ebbd6daf302487bf452245d50Jerome Poichet     *
430e5f2388b8d24876ebbd6daf302487bf452245d50Jerome Poichet     * @param completions list of completions shown in the IME, can be null or empty to clear them
431e5f2388b8d24876ebbd6daf302487bf452245d50Jerome Poichet     */
432e5f2388b8d24876ebbd6daf302487bf452245d50Jerome Poichet    public void displayCompletions(List<String> completions) {
433c738357d90fcc7876415ee16d7fe935510229d4aJerome Poichet        List<CompletionInfo> infos = new ArrayList<>();
434e5f2388b8d24876ebbd6daf302487bf452245d50Jerome Poichet        if (null != completions) {
435e5f2388b8d24876ebbd6daf302487bf452245d50Jerome Poichet            for (String completion : completions) {
436e5f2388b8d24876ebbd6daf302487bf452245d50Jerome Poichet                infos.add(new CompletionInfo(infos.size(), infos.size(), completion));
437e5f2388b8d24876ebbd6daf302487bf452245d50Jerome Poichet            }
438e5f2388b8d24876ebbd6daf302487bf452245d50Jerome Poichet        }
439c738357d90fcc7876415ee16d7fe935510229d4aJerome Poichet        CompletionInfo[] array = new CompletionInfo[infos.size()];
440c738357d90fcc7876415ee16d7fe935510229d4aJerome Poichet        displayCompletions(infos.toArray(array));
441c738357d90fcc7876415ee16d7fe935510229d4aJerome Poichet    }
442e5f2388b8d24876ebbd6daf302487bf452245d50Jerome Poichet
443c738357d90fcc7876415ee16d7fe935510229d4aJerome Poichet    /**
444c738357d90fcc7876415ee16d7fe935510229d4aJerome Poichet     * Updates the completion list shown by the IME
445c738357d90fcc7876415ee16d7fe935510229d4aJerome Poichet     *
446c738357d90fcc7876415ee16d7fe935510229d4aJerome Poichet     * @param completions list of completions shown in the IME, can be null or empty to clear them
447c738357d90fcc7876415ee16d7fe935510229d4aJerome Poichet     */
448c738357d90fcc7876415ee16d7fe935510229d4aJerome Poichet    public void displayCompletions(CompletionInfo[] completions) {
449c738357d90fcc7876415ee16d7fe935510229d4aJerome Poichet        mInputMethodManager.displayCompletions(mSearchTextEditor, completions);
450e5f2388b8d24876ebbd6daf302487bf452245d50Jerome Poichet    }
451e5f2388b8d24876ebbd6daf302487bf452245d50Jerome Poichet
4524fbaf9b54d7f9723f3ec5c320db9cea887eeff7bJerome Poichet    /**
453a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Sets the speech recognizer to be used when doing voice search. The Activity/Fragment is in
4544fbaf9b54d7f9723f3ec5c320db9cea887eeff7bJerome Poichet     * charge of creating and destroying the recognizer with its own lifecycle.
4554fbaf9b54d7f9723f3ec5c320db9cea887eeff7bJerome Poichet     *
4564fbaf9b54d7f9723f3ec5c320db9cea887eeff7bJerome Poichet     * @param recognizer a SpeechRecognizer
4574fbaf9b54d7f9723f3ec5c320db9cea887eeff7bJerome Poichet     */
4584fbaf9b54d7f9723f3ec5c320db9cea887eeff7bJerome Poichet    public void setSpeechRecognizer(SpeechRecognizer recognizer) {
4597c8283f6542b1045377e88566d363b4c2c83088bCraig Stout        stopRecognition();
4604fbaf9b54d7f9723f3ec5c320db9cea887eeff7bJerome Poichet        if (null != mSpeechRecognizer) {
4614fbaf9b54d7f9723f3ec5c320db9cea887eeff7bJerome Poichet            mSpeechRecognizer.setRecognitionListener(null);
4624fbaf9b54d7f9723f3ec5c320db9cea887eeff7bJerome Poichet            if (mListening) {
463e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                mSpeechRecognizer.cancel();
4644fbaf9b54d7f9723f3ec5c320db9cea887eeff7bJerome Poichet                mListening = false;
4654fbaf9b54d7f9723f3ec5c320db9cea887eeff7bJerome Poichet            }
4664fbaf9b54d7f9723f3ec5c320db9cea887eeff7bJerome Poichet        }
4674fbaf9b54d7f9723f3ec5c320db9cea887eeff7bJerome Poichet        mSpeechRecognizer = recognizer;
468361955cd7c040bf30330c8e21d9016c747a94473Craig Stout        if (mSpeechRecognitionCallback != null && mSpeechRecognizer != null) {
469361955cd7c040bf30330c8e21d9016c747a94473Craig Stout            throw new IllegalStateException("Can't have speech recognizer and request");
470361955cd7c040bf30330c8e21d9016c747a94473Craig Stout        }
471361955cd7c040bf30330c8e21d9016c747a94473Craig Stout    }
472361955cd7c040bf30330c8e21d9016c747a94473Craig Stout
473a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout    /**
474a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Sets the speech recognition callback.
475a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     */
476361955cd7c040bf30330c8e21d9016c747a94473Craig Stout    public void setSpeechRecognitionCallback(SpeechRecognitionCallback request) {
477361955cd7c040bf30330c8e21d9016c747a94473Craig Stout        mSpeechRecognitionCallback = request;
478361955cd7c040bf30330c8e21d9016c747a94473Craig Stout        if (mSpeechRecognitionCallback != null && mSpeechRecognizer != null) {
479361955cd7c040bf30330c8e21d9016c747a94473Craig Stout            throw new IllegalStateException("Can't have speech recognizer and request");
480361955cd7c040bf30330c8e21d9016c747a94473Craig Stout        }
4814fbaf9b54d7f9723f3ec5c320db9cea887eeff7bJerome Poichet    }
4824fbaf9b54d7f9723f3ec5c320db9cea887eeff7bJerome Poichet
48399ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas    void hideNativeKeyboard() {
48425aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet        mInputMethodManager.hideSoftInputFromWindow(mSearchTextEditor.getWindowToken(),
48525aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                InputMethodManager.RESULT_UNCHANGED_SHOWN);
48625aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet    }
48725aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
48899ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas    void showNativeKeyboard() {
48985c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet        mHandler.post(new Runnable() {
49085c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet            @Override
49185c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet            public void run() {
49285c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet                mSearchTextEditor.requestFocusFromTouch();
49385c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet                mSearchTextEditor.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(),
49485c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet                        SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN,
49585c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet                        mSearchTextEditor.getWidth(), mSearchTextEditor.getHeight(), 0));
49685c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet                mSearchTextEditor.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(),
49785c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet                        SystemClock.uptimeMillis(), MotionEvent.ACTION_UP,
49885c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet                        mSearchTextEditor.getWidth(), mSearchTextEditor.getHeight(), 0));
49985c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet            }
50085c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet        });
50185c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet    }
50285c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet
503514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet    /**
504514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet     * This will update the hint for the search bar properly depending on state and provided title
505514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet     */
5062ad1027496fefed641f91f3cde2f8c8b468bca0cJérôme Poichet    private void updateHint() {
507514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet        String title = getResources().getString(R.string.lb_search_bar_hint);
508514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet        if (!TextUtils.isEmpty(mTitle)) {
509a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet            if (isVoiceMode()) {
510a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet                title = getResources().getString(R.string.lb_search_bar_hint_with_title_speech, mTitle);
511a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet            } else {
512a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet                title = getResources().getString(R.string.lb_search_bar_hint_with_title, mTitle);
513a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet            }
514a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet        } else if (isVoiceMode()) {
515a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet            title = getResources().getString(R.string.lb_search_bar_hint_speech);
516514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet        }
517490691bd414dbb2093dc6cb56ae63ae9b04e70e3Craig Stout        mHint = title;
518490691bd414dbb2093dc6cb56ae63ae9b04e70e3Craig Stout        if (mSearchTextEditor != null) {
519490691bd414dbb2093dc6cb56ae63ae9b04e70e3Craig Stout            mSearchTextEditor.setHint(mHint);
520490691bd414dbb2093dc6cb56ae63ae9b04e70e3Craig Stout        }
521514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet    }
522514bdd5473ae7f4da990d6035422d0784c6c344eJerome Poichet
52399ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas    void toggleRecognition() {
524c8737e35c611362db621b25656b57be0c2b8a306Jerome Poichet        if (mRecognizing) {
525c8737e35c611362db621b25656b57be0c2b8a306Jerome Poichet            stopRecognition();
526c8737e35c611362db621b25656b57be0c2b8a306Jerome Poichet        } else {
527c8737e35c611362db621b25656b57be0c2b8a306Jerome Poichet            startRecognition();
528c8737e35c611362db621b25656b57be0c2b8a306Jerome Poichet        }
529c8737e35c611362db621b25656b57be0c2b8a306Jerome Poichet    }
530c8737e35c611362db621b25656b57be0c2b8a306Jerome Poichet
531f52e0718c20bedc86f0b67f6b3660e737d787bd4Dake Gu    /**
532ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu     * Returns true if is not running Recognizer, false otherwise.
533ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu     * @return True if is not running Recognizer, false otherwise.
534ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu     */
535ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu    public boolean isRecognizing() {
536ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu        return mRecognizing;
537ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu    }
538ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu
539ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu    /**
540a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Stops the speech recognition, if already started.
541f52e0718c20bedc86f0b67f6b3660e737d787bd4Dake Gu     */
542f52e0718c20bedc86f0b67f6b3660e737d787bd4Dake Gu    public void stopRecognition() {
543e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet        if (DEBUG) Log.v(TAG, String.format("stopRecognition (listening: %s, recognizing: %s)",
544e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                mListening, mRecognizing));
545e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet
54622e46a46e20c53b2e2d23dca792534ee3434c954Dake Gu        if (!mRecognizing) return;
547e2047098f696c81f6435cdd588a62d8ab5829c7dCraig Stout
548c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas        // Edit text content was cleared when starting recognition; ensure the content is restored
549e2047098f696c81f6435cdd588a62d8ab5829c7dCraig Stout        // in error cases
550e2047098f696c81f6435cdd588a62d8ab5829c7dCraig Stout        mSearchTextEditor.setText(mSearchQuery);
551490691bd414dbb2093dc6cb56ae63ae9b04e70e3Craig Stout        mSearchTextEditor.setHint(mHint);
552e2047098f696c81f6435cdd588a62d8ab5829c7dCraig Stout
55322e46a46e20c53b2e2d23dca792534ee3434c954Dake Gu        mRecognizing = false;
5544fbaf9b54d7f9723f3ec5c320db9cea887eeff7bJerome Poichet
555e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet        if (mSpeechRecognitionCallback != null || null == mSpeechRecognizer) return;
556e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet
55725aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet        mSpeechOrbView.showNotListening();
55825aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
55925aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet        if (mListening) {
56025aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            mSpeechRecognizer.cancel();
561e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet            mListening = false;
562e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet            mAudioManager.abandonAudioFocus(mAudioFocusChangeListener);
56325aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet        }
564e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet
565e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet        mSpeechRecognizer.setRecognitionListener(null);
56625aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet    }
56725aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
568f52e0718c20bedc86f0b67f6b3660e737d787bd4Dake Gu    /**
569ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu     * Sets listener that handles runtime permission requests.
570ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu     * @param listener Listener that handles runtime permission requests.
571f52e0718c20bedc86f0b67f6b3660e737d787bd4Dake Gu     */
572ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu    public void setPermissionListener(SearchBarPermissionListener listener) {
573ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu        mPermissionListener = listener;
574ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu    }
575ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu
576f52e0718c20bedc86f0b67f6b3660e737d787bd4Dake Gu    public void startRecognition() {
577e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet        if (DEBUG) Log.v(TAG, String.format("startRecognition (listening: %s, recognizing: %s)",
578e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                mListening, mRecognizing));
579e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet
58022e46a46e20c53b2e2d23dca792534ee3434c954Dake Gu        if (mRecognizing) return;
58122e46a46e20c53b2e2d23dca792534ee3434c954Dake Gu        if (!hasFocus()) {
58222e46a46e20c53b2e2d23dca792534ee3434c954Dake Gu            requestFocus();
58322e46a46e20c53b2e2d23dca792534ee3434c954Dake Gu        }
584361955cd7c040bf30330c8e21d9016c747a94473Craig Stout        if (mSpeechRecognitionCallback != null) {
585490691bd414dbb2093dc6cb56ae63ae9b04e70e3Craig Stout            mSearchTextEditor.setText("");
586490691bd414dbb2093dc6cb56ae63ae9b04e70e3Craig Stout            mSearchTextEditor.setHint("");
587361955cd7c040bf30330c8e21d9016c747a94473Craig Stout            mSpeechRecognitionCallback.recognizeSpeech();
588ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu            mRecognizing = true;
589361955cd7c040bf30330c8e21d9016c747a94473Craig Stout            return;
590361955cd7c040bf30330c8e21d9016c747a94473Craig Stout        }
5914fbaf9b54d7f9723f3ec5c320db9cea887eeff7bJerome Poichet        if (null == mSpeechRecognizer) return;
592ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu        int res = getContext().checkCallingOrSelfPermission(Manifest.permission.RECORD_AUDIO);
593ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu        if (PackageManager.PERMISSION_GRANTED != res) {
594ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu            if (Build.VERSION.SDK_INT >= 23 && mPermissionListener != null) {
595ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu                mPermissionListener.requestAudioPermission();
596ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu                return;
597ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu            } else {
598ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu                throw new IllegalStateException(Manifest.permission.RECORD_AUDIO +
599ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu                        " required for search");
600ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu            }
601ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu        }
6024fbaf9b54d7f9723f3ec5c320db9cea887eeff7bJerome Poichet
603ce06f6b530f22e1e7839d068d5dd8ea3e4af8262Dake Gu        mRecognizing = true;
604e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet        // Request audio focus
605e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet        int result = mAudioManager.requestAudioFocus(mAudioFocusChangeListener,
606e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                // Use the music stream.
607e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                AudioManager.STREAM_MUSIC,
608e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                // Request exclusive transient focus.
609e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
610e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet
611e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet
612e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet        if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
613e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet            Log.w(TAG, "Could not get audio focus");
614e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet        }
61525aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
616a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet        mSearchTextEditor.setText("");
617a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet
61825aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet        Intent recognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
61925aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
62025aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet        recognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
62125aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
62225aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet        recognizerIntent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true);
62325aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
62425aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet        mSpeechRecognizer.setRecognitionListener(new RecognitionListener() {
62525aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            @Override
62625aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            public void onReadyForSpeech(Bundle bundle) {
62725aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                if (DEBUG) Log.v(TAG, "onReadyForSpeech");
6289d9f969b5f37b4454b49f74125521c82a8bf88c4Tim Kilbourn                mSpeechOrbView.showListening();
6299d9f969b5f37b4454b49f74125521c82a8bf88c4Tim Kilbourn                playSearchOpen();
63025aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            }
63125aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
63225aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            @Override
63325aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            public void onBeginningOfSpeech() {
63425aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                if (DEBUG) Log.v(TAG, "onBeginningOfSpeech");
63525aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            }
63625aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
63725aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            @Override
63825aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            public void onRmsChanged(float rmsdB) {
63925aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                if (DEBUG) Log.v(TAG, "onRmsChanged " + rmsdB);
64025aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                int level = rmsdB < 0 ? 0 : (int)(10 * rmsdB);
64125aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                mSpeechOrbView.setSoundLevel(level);
64225aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            }
64325aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
64425aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            @Override
64525aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            public void onBufferReceived(byte[] bytes) {
64625aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                if (DEBUG) Log.v(TAG, "onBufferReceived " + bytes.length);
64725aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            }
64825aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
64925aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            @Override
65025aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            public void onEndOfSpeech() {
65125aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                if (DEBUG) Log.v(TAG, "onEndOfSpeech");
65225aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            }
65325aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
65425aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            @Override
65525aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            public void onError(int error) {
65625aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                if (DEBUG) Log.v(TAG, "onError " + error);
65725aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                switch (error) {
658e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                    case SpeechRecognizer.ERROR_NETWORK_TIMEOUT:
659e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                        Log.w(TAG, "recognizer network timeout");
660e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                        break;
661e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                    case SpeechRecognizer.ERROR_NETWORK:
662e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                        Log.w(TAG, "recognizer network error");
663e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                        break;
664e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                    case SpeechRecognizer.ERROR_AUDIO:
665e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                        Log.w(TAG, "recognizer audio error");
66625aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                        break;
66725aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                    case SpeechRecognizer.ERROR_SERVER:
668e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                        Log.w(TAG, "recognizer server error");
669e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                        break;
670e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                    case SpeechRecognizer.ERROR_CLIENT:
671e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                        Log.w(TAG, "recognizer client error");
67225aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                        break;
67325aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                    case SpeechRecognizer.ERROR_SPEECH_TIMEOUT:
674e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                        Log.w(TAG, "recognizer speech timeout");
67525aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                        break;
676e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                    case SpeechRecognizer.ERROR_NO_MATCH:
677e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                        Log.w(TAG, "recognizer no match");
678e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                        break;
679e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                    case SpeechRecognizer.ERROR_RECOGNIZER_BUSY:
680e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                        Log.w(TAG, "recognizer busy");
681e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                        break;
682e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                    case SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS:
683e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                        Log.w(TAG, "recognizer insufficient permissions");
68425aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                        break;
68525aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                    default:
68625aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                        Log.d(TAG, "recognizer other error");
68725aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                        break;
68825aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                }
68925aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
69022e46a46e20c53b2e2d23dca792534ee3434c954Dake Gu                stopRecognition();
691a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet                playSearchFailure();
69225aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            }
69325aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
69425aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            @Override
69525aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            public void onResults(Bundle bundle) {
69625aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                if (DEBUG) Log.v(TAG, "onResults");
69725aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                final ArrayList<String> matches =
69825aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                        bundle.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
69925aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                if (matches != null) {
700e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet                    if (DEBUG) Log.v(TAG, "Got results" + matches);
70125aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
70225aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                    mSearchQuery = matches.get(0);
70325aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                    mSearchTextEditor.setText(mSearchQuery);
704a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet                    submitQuery();
70525aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet                }
706e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet
70722e46a46e20c53b2e2d23dca792534ee3434c954Dake Gu                stopRecognition();
708a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet                playSearchSuccess();
70925aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            }
71025aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
71125aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            @Override
71225aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            public void onPartialResults(Bundle bundle) {
713a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                ArrayList<String> results = bundle.getStringArrayList(
714a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                        SpeechRecognizer.RESULTS_RECOGNITION);
715a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                if (DEBUG) Log.v(TAG, "onPartialResults " + bundle + " results " +
716a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                        (results == null ? results : results.size()));
717a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                if (results == null || results.size() == 0) {
718a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                    return;
719a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                }
720a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
721a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                // stableText: high confidence text from PartialResults, if any.
722a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                // Otherwise, existing stable text.
723a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                final String stableText = results.get(0);
724a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                if (DEBUG) Log.v(TAG, "onPartialResults stableText " + stableText);
725a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
726a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                // pendingText: low confidence text from PartialResults, if any.
727a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                // Otherwise, empty string.
728a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                final String pendingText = results.size() > 1 ? results.get(1) : null;
729a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                if (DEBUG) Log.v(TAG, "onPartialResults pendingText " + pendingText);
73025aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
731a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                mSearchTextEditor.updateRecognizedText(stableText, pendingText);
73225aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            }
73325aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
73425aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            @Override
73525aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            public void onEvent(int i, Bundle bundle) {
73625aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
73725aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet            }
73825aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet        });
73925aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
740e2bc82a1c02f667901bd96e244afa7dc0232b354Jerome Poichet        mListening = true;
74125aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet        mSpeechRecognizer.startListening(recognizerIntent);
74225aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet    }
74325aacd3f5896ec09053739cc731bdbab3a6f2b81Jerome Poichet
74499ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas    void updateUi(boolean hasFocus) {
745936867a6a063492207bd3cd68a1aa870dd5c9da5Craig Stout        if (hasFocus) {
746a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet            mBarBackground.setAlpha(mBackgroundSpeechAlpha);
747936867a6a063492207bd3cd68a1aa870dd5c9da5Craig Stout            if (isVoiceMode()) {
748936867a6a063492207bd3cd68a1aa870dd5c9da5Craig Stout                mSearchTextEditor.setTextColor(mTextHintColorSpeechMode);
749936867a6a063492207bd3cd68a1aa870dd5c9da5Craig Stout                mSearchTextEditor.setHintTextColor(mTextHintColorSpeechMode);
750936867a6a063492207bd3cd68a1aa870dd5c9da5Craig Stout            } else {
751936867a6a063492207bd3cd68a1aa870dd5c9da5Craig Stout                mSearchTextEditor.setTextColor(mTextColorSpeechMode);
752936867a6a063492207bd3cd68a1aa870dd5c9da5Craig Stout                mSearchTextEditor.setHintTextColor(mTextHintColorSpeechMode);
753936867a6a063492207bd3cd68a1aa870dd5c9da5Craig Stout            }
754a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet        } else {
755a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet            mBarBackground.setAlpha(mBackgroundAlpha);
756a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet            mSearchTextEditor.setTextColor(mTextColor);
757a6c18317f09969688b7d329a5b4ce35c8d648e4fCraig Stout            mSearchTextEditor.setHintTextColor(mTextHintColor);
758a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet        }
759a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet
760a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet        updateHint();
761a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet    }
762a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet
7632ad1027496fefed641f91f3cde2f8c8b468bca0cJérôme Poichet    private boolean isVoiceMode() {
764a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet        return mSpeechOrbView.isFocused();
765a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet    }
766a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet
76799ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas    void submitQuery() {
768a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet        if (!TextUtils.isEmpty(mSearchQuery) && null != mSearchBarListener) {
769a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet            mSearchBarListener.onSearchQuerySubmit(mSearchQuery);
770a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet        }
771a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet    }
772a9b678302ddee2149e5605afd429c74ba5625c24Jerome Poichet
773a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet    private void loadSounds(Context context) {
774a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet        int[] sounds = {
775decb4e284bb5befddcc3812564dab41049fca303Jaekyun Seok                R.raw.lb_voice_failure,
776decb4e284bb5befddcc3812564dab41049fca303Jaekyun Seok                R.raw.lb_voice_open,
777decb4e284bb5befddcc3812564dab41049fca303Jaekyun Seok                R.raw.lb_voice_no_input,
778decb4e284bb5befddcc3812564dab41049fca303Jaekyun Seok                R.raw.lb_voice_success,
779a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet        };
780a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet        for (int sound : sounds) {
781a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet            mSoundMap.put(sound, mSoundPool.load(context, sound, 1));
782a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet        }
783a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet    }
784a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet
785a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet    private void play(final int resId) {
786a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet        mHandler.post(new Runnable() {
787a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet            @Override
788a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet            public void run() {
789a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet                int sound = mSoundMap.get(resId);
790a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet                mSoundPool.play(sound, FULL_LEFT_VOLUME, FULL_RIGHT_VOLUME, DEFAULT_PRIORITY,
791a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet                        DO_NOT_LOOP, DEFAULT_RATE);
792a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet            }
793a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet        });
794a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet    }
795a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet
79699ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas    void playSearchOpen() {
797decb4e284bb5befddcc3812564dab41049fca303Jaekyun Seok        play(R.raw.lb_voice_open);
798a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet    }
799a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet
80099ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas    void playSearchFailure() {
801decb4e284bb5befddcc3812564dab41049fca303Jaekyun Seok        play(R.raw.lb_voice_failure);
802a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet    }
803a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet
804a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet    private void playSearchNoInput() {
805decb4e284bb5befddcc3812564dab41049fca303Jaekyun Seok        play(R.raw.lb_voice_no_input);
806a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet    }
807a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet
80899ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0dAurimas Liutikas    void playSearchSuccess() {
809decb4e284bb5befddcc3812564dab41049fca303Jaekyun Seok        play(R.raw.lb_voice_success);
810a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet    }
811a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet
812f6221e91bd3f0f584893baadcb9f8cf5621dce3bCraig Stout    @Override
813f6221e91bd3f0f584893baadcb9f8cf5621dce3bCraig Stout    public void setNextFocusDownId(int viewId) {
814f6221e91bd3f0f584893baadcb9f8cf5621dce3bCraig Stout        mSpeechOrbView.setNextFocusDownId(viewId);
815f6221e91bd3f0f584893baadcb9f8cf5621dce3bCraig Stout        mSearchTextEditor.setNextFocusDownId(viewId);
816f6221e91bd3f0f584893baadcb9f8cf5621dce3bCraig Stout    }
817a1798f408d3acafffc93cce1b309a54dce52d22dJérôme Poichet
81885c10faf654848a8aacd1e61760b3e24a1902a83Jerome Poichet}
819