BluetoothHeadset:为什么有必要使用计时器来调用startVoiceRecognition?

时间:2013-02-28 04:32:21

标签: android bluetooth bluetooth-sco

我写了一些用于检测蓝牙耳机连接并通过耳机启动音频的代码。对于API 11及更高版本,可以在连接耳机时调用startVoiceRecognition。所以有几个用例如下:

  1. 在应用程序启动之前已打开耳机
    应用程序应检查启动时连接的耳机并建立音频连接。

  2. 用户在应用程序有效期内开启耳机
    应用程序应注册耳机连接状态的广播,并在接收连接状态时启动音频连接。

  3. 第二个用例存在问题。当收到连接状态时,我调用startVoiceRecognition,但它总是返回false。所以我必须实现一个计时器,大约一秒后,调用将返回true。我想操作系统和耳机需要一段时间才能让一切准备就绪。 有没有人知道如何在没有实现计时器的情况下获得耳机音频连接。如果不可能,应该是应该处理这种情况的操作系统(例如READY_FOR_AUDIO_CONNECTION广播)而不是应用程序吗?

    以下是API 11或更高版本的完整工作代码。

    清单权限

    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BROADCAST_STICKY" />
    

    代码

    public class MainActivity extends Activity
    {
    
        protected TextView mInfoTextview;
    
        protected BluetoothAdapter mBluetoothAdapter;
        protected BluetoothHeadset mBluetoothHeadset;
        protected BluetoothDevice mConnectedHeadset;
    
        protected AudioManager mAudioManager;
    
    
        private static final String TAG = "Bluetooth Headset"; //$NON-NLS-1$
    
        @Override
        protected void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
    
            setContentView(R.layout.activity_main);
            mInfoTextview = (TextView) findViewById(R.id.main_textview);
    
            mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    
            if (mBluetoothAdapter != null)
            {
                mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
                if (mAudioManager.isBluetoothScoAvailableOffCall())
                {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
                    {
                        mBluetoothAdapter.getProfileProxy(this, mHeadsetProfileListener, BluetoothProfile.HEADSET);
                    }
                }
            }
        }
    
        @Override
        protected void onDestroy()
        {
            super.onDestroy();
    
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
            {
                if (mBluetoothHeadset != null)
                {
                    // Need to call stopVoiceRecognition here when the app
                    // change orientation or close with headset still turns on.
                     mBluetoothHeadset.stopVoiceRecognition(mConnectedHeadset);
                     unregisterReceiver(mHeadsetBroadcastReceiver);
                     mCountDown.cancel();
                }
                mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
            }
            Log.d(TAG, "onDestroy"); //$NON-NLS-1$
        }
    
        protected BluetoothProfile.ServiceListener mHeadsetProfileListener = new BluetoothProfile.ServiceListener()
        {
    
            /**
             * This method is never called, even when we closeProfileProxy on onPause.
             * When or will it ever be called???
             */
            @Override
            public void onServiceDisconnected(int profile)
            {
                Log.d(TAG, "Profile listener onServiceDisconnected"); //$NON-NLS-1$
                mBluetoothHeadset.stopVoiceRecognition(mConnectedHeadset);
                unregisterReceiver(mHeadsetBroadcastReceiver);
                mBluetoothHeadset = null;
            }
    
            @Override
            public void onServiceConnected(int profile, BluetoothProfile proxy)
            {
                Log.d(TAG, "Profile listener onServiceConnected"); //$NON-NLS-1$
    
                // mBluetoothHeadset is just a head set profile, 
                // it does not represent a head set device.
                mBluetoothHeadset = (BluetoothHeadset) proxy;
    
                // If a head set is connected before this application starts,
                // ACTION_CONNECTION_STATE_CHANGED will not be broadcast. 
                // So we need to check for already connected head set.
                List<BluetoothDevice> devices = mBluetoothHeadset.getConnectedDevices();
                if (devices.size() > 0)
                {
                    // Only one head set can be connected at a time, 
                    // so the connected head set is at index 0.
                    mConnectedHeadset = devices.get(0);
    
                    String log;
    
                    // The audio should not yet be connected at this stage.
                    // But just to make sure we check.
                    if (mBluetoothHeadset.isAudioConnected(mConnectedHeadset))
                    {
                        log = "Profile listener audio already connected"; //$NON-NLS-1$     
                    }
                    else
                    {
                        // The if statement is just for debug. So far startVoiceRecognition always 
                        // returns true here. What can we do if it returns false? Perhaps the only
                        // sensible thing is to inform the user.
                        // Well actually, it only returns true if a call to stopVoiceRecognition is
                        // call somewhere after a call to startVoiceRecognition. Otherwise, if 
                        // stopVoiceRecognition is never called, then when the application is restarted
                        // startVoiceRecognition always returns false whenever it is called.
                        if (mBluetoothHeadset.startVoiceRecognition(mConnectedHeadset))
                        {
                            log = "Profile listener startVoiceRecognition returns true"; //$NON-NLS-1$
                        }
                        else
                        {
                            log = "Profile listener startVoiceRecognition returns false"; //$NON-NLS-1$
                        }   
                    }
    
                    mInfoTextview.setText("Device name = " + mConnectedHeadset.getName() //$NON-NLS-1$
                                            + "\n\n" + log); //$NON-NLS-1$
                    Log.d(TAG, log); 
                }
    
                // During the active life time of the app, a user may turn on and off the head set.
                // So register for broadcast of connection states.
                registerReceiver(mHeadsetBroadcastReceiver, 
                                new IntentFilter(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED));
                // Calling startVoiceRecognition does not result in immediate audio connection.
                // So register for broadcast of audio connection states. This broadcast will
                // only be sent if startVoiceRecognition returns true.
                registerReceiver(mHeadsetBroadcastReceiver, 
                                new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED));
            }
        };
    
        protected BroadcastReceiver mHeadsetBroadcastReceiver = new BroadcastReceiver()
        {
    
            @Override
            public void onReceive(Context context, Intent intent)
            {
                String action = intent.getAction();
                int state;
                int previousState = intent.getIntExtra(BluetoothHeadset.EXTRA_PREVIOUS_STATE, BluetoothHeadset.STATE_DISCONNECTED);
                String log = ""; //$NON-NLS-1$
                if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED))
                {
                    state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_DISCONNECTED);
                    if (state == BluetoothHeadset.STATE_CONNECTED)
                    {
                        mConnectedHeadset = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
    
                        mInfoTextview.append("\n\nDevice name = " + mConnectedHeadset.getName()); //$NON-NLS-1$
    
                        // Audio should not be connected yet but just to make sure.
                        if (mBluetoothHeadset.isAudioConnected(mConnectedHeadset))
                        {
                            log = "Headset connected audio already connected"; //$NON-NLS-1$
                        }
                        else
                        {
                            // Calling startVoiceRecognition always returns false here, 
                            // that why a count down timer is implemented to call
                            // startVoiceRecognition in the onTick and onFinish.
                            if (mBluetoothHeadset.startVoiceRecognition(mConnectedHeadset))
                            {
                                log = "Headset connected startVoiceRecognition returns true"; //$NON-NLS-1$
                            }
                            else
                            {
                                log = "Headset connected startVoiceRecognition returns false"; //$NON-NLS-1$
                                mCountDown.start();
                            }
                        }
                    }
                    else if (state == BluetoothHeadset.STATE_DISCONNECTED)
                    {
                        // Calling stopVoiceRecognition always returns false here
                        // as it should since the headset is no longer connected.
                        mConnectedHeadset = null;
                    }
                }
                else // audio
                {
                    state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
                    if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED)
                    {
                        log = "Head set audio connected, cancel countdown timer";  //$NON-NLS-1$
                        mCountDown.cancel();    
                    }
                    else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED)
                    {
                        // The headset audio is disconnected, but calling
                        // stopVoiceRecognition always returns true here.
                        boolean returnValue = mBluetoothHeadset.stopVoiceRecognition(mConnectedHeadset);
                        log = "Audio disconnected stopVoiceRecognition return " + returnValue; //$NON-NLS-1$
                    }
                }   
    
                log += "\nAction = " + action + "\nState = " + state //$NON-NLS-1$ //$NON-NLS-2$
                        + " previous state = " + previousState; //$NON-NLS-1$
                mInfoTextview.append("\n\n" + log); //$NON-NLS-1$
                Log.d(TAG, log);
            }
        };
    
    
        protected CountDownTimer mCountDown = new CountDownTimer(10000, 1000)
        {
    
            @Override
            public void onTick(long millisUntilFinished)
            {
                String log;
                if (mBluetoothHeadset.isAudioConnected(mConnectedHeadset))
                {
                    log = "\nonTick audio already connected"; //$NON-NLS-1$
                }
                else
                {
                    // First stick calls always returns false. The second stick
                    // always returns true if the countDownInterval is set to 1000.
                    // It is somewhere in between 500 to a 1000.
                    if (mBluetoothHeadset.startVoiceRecognition(mConnectedHeadset))
                    {
                        log = "\nonTick startVoiceRecognition returns true"; //$NON-NLS-1$
                    }
                    else
                    {
                        log = "\nonTick startVoiceRecognition returns false"; //$NON-NLS-1$
                    }
                }
    
                mInfoTextview.append(log);
                Log.d(TAG, log);
            }
    
            @Override
            public void onFinish()
            {
                String log;
                if (mBluetoothHeadset.isAudioConnected(mConnectedHeadset))
                {
                    log = "\nonFinish audio already connected"; //$NON-NLS-1$
                }
                else
                {
                    if (mBluetoothHeadset.startVoiceRecognition(mConnectedHeadset))
                    {
                        log = "\nonFinish startVoiceRecognition returns true"; //$NON-NLS-1$
                    }
                    else
                    {
                        log = "\nonFinish startVoiceRecognition returns false"; //$NON-NLS-1$
                    }
                }
                mInfoTextview.append(log);
                Log.d(TAG, log);
            }
        };
    }
    

    布局文件

    <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    
        <TextView
            android:id="@+id/main_textview"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" 
            android:textIsSelectable="false" />
    
    </ScrollView>
    

0 个答案:

没有答案