/TalkBack/src/com/google/android/marvin/talkback/BluetoothHandler.java
Java | 218 lines | 140 code | 26 blank | 52 comment | 30 complexity | e9b075d792990bda73ceb20111ed8c05 MD5 | raw file
1/* 2 * Copyright (C) 2011 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17package com.google.android.marvin.talkback; 18 19import android.bluetooth.BluetoothAdapter; 20import android.bluetooth.BluetoothDevice; 21import android.bluetooth.BluetoothHeadset; 22import android.bluetooth.BluetoothProfile; 23import android.content.Context; 24import android.os.Handler; 25import android.os.Message; 26import android.util.Log; 27 28import java.util.List; 29 30/** 31 * A helper class for handling the Bluetooth headset. 32 */ 33class BluetoothHandler { 34 private static final String TAG = BluetoothHandler.class.getSimpleName(); 35 36 private static final boolean DBG = true; 37 38 private static final int MSG_START_BLUETOOTH = 1; 39 private static final int MSG_STOP_BLUETOOTH = 2; 40 private static final int MSG_CANCEL_BLUETOOTH = 3; 41 42 private final Listener mListener; 43 private final int mStartupTimeoutMillis; 44 private final BluetoothAdapter mBluetoothAdapter; 45 private final Handler mHandler; 46 private final BluetoothHeadset.ServiceListener mBluetoothHeadsetServiceListener; 47 48 private BluetoothDevice mBluetoothDevice; 49 private BluetoothHeadset mBluetoothProxy; 50 private BluetoothHeadsetCompatWrapper mBluetoothProxyCompat; 51 52 private boolean mBluetoothConnectionCanceled = false; 53 54 /** 55 * Creates the Bluetooth handler. No connection to a device is made in the 56 * constructor. 57 * 58 * @param listener the listener that will be notified about the bluetooth 59 * connection state changes 60 */ 61 public BluetoothHandler(int startupTimeoutMillis, Listener listener) { 62 mListener = listener; 63 mStartupTimeoutMillis = startupTimeoutMillis; 64 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 65 mHandler = new Handler() { 66 @Override 67 public void handleMessage(Message msg) { 68 switch (msg.what) { 69 case MSG_START_BLUETOOTH: 70 startBluetooth((BluetoothHeadset) msg.obj); 71 break; 72 case MSG_STOP_BLUETOOTH: 73 stop(); 74 break; 75 case MSG_CANCEL_BLUETOOTH: 76 cancel(); 77 break; 78 } 79 super.handleMessage(msg); 80 } 81 }; 82 mBluetoothHeadsetServiceListener = new BluetoothHeadset.ServiceListener() { 83 @Override 84 public void onServiceConnected(int profile, BluetoothProfile proxy) { 85 if (DBG) 86 Log.d(TAG, "BluetoothHeadset.ServiceListener.onServiceConnected"); 87 Message.obtain(mHandler, MSG_START_BLUETOOTH, proxy).sendToTarget(); 88 } 89 90 @Override 91 public void onServiceDisconnected(int profile) { 92 if (DBG) 93 Log.d(TAG, "BluetoothHeadset.ServiceListener.onServiceDisconnected"); 94 Message.obtain(mHandler, MSG_STOP_BLUETOOTH).sendToTarget(); 95 } 96 }; 97 } 98 99 private void startWatchdog() { 100 stopWatchdog(); 101 mBluetoothConnectionCanceled = false; 102 mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_CANCEL_BLUETOOTH), 103 mStartupTimeoutMillis); 104 } 105 106 private void stopWatchdog() { 107 mHandler.removeMessages(MSG_CANCEL_BLUETOOTH); 108 } 109 110 private void startBluetooth(BluetoothHeadset proxy) { 111 stopWatchdog(); 112 if (mBluetoothConnectionCanceled) { 113 mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, proxy); 114 return; 115 } 116 List<BluetoothDevice> devices = proxy.getConnectedDevices(); 117 if (devices.size() > 0) { 118 mBluetoothDevice = devices.get(0); 119 mBluetoothProxy = proxy; 120 mBluetoothProxyCompat = new BluetoothHeadsetCompatWrapper(proxy); 121 Log.i(TAG, "Connected to a bluetooth device."); 122 } else { 123 if (DBG) 124 Log.d(TAG, "No device is connected, closing bluetooth proxy."); 125 mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, proxy); 126 } 127 mListener.onConnectionComplete(); 128 } 129 130 private void cancel() { 131 mListener.onConnectionComplete(); 132 stop(); 133 } 134 135 /** 136 * Closes the connection to the Bluetooth proxy. This method must be called 137 * to prevent resource leakage. 138 */ 139 public void stop() { 140 if (DBG) 141 Log.d(TAG, "Closing bluetooth proxy:" + mBluetoothProxy); 142 mBluetoothConnectionCanceled = true; 143 stopWatchdog(); 144 if (mBluetoothProxy != null) { 145 mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothProxy); 146 mBluetoothProxy = null; 147 mBluetoothProxyCompat = null; 148 mBluetoothDevice = null; 149 } 150 } 151 152 /** 153 * @return {@code false} if no devices are available or the 154 * {@link BluetoothAdapter} is turned off. 155 */ 156 public boolean isBluetoothAvailable() { 157 return mBluetoothAdapter != null && mBluetoothAdapter.isEnabled() 158 && mBluetoothAdapter.getBondedDevices() != null 159 && mBluetoothAdapter.getBondedDevices().size() > 0; 160 } 161 162 /** 163 * Initiates an asynchronous connection to a bluetooth device. This function 164 * should be called on the main thread. 165 * {@link Listener#onConnectionComplete} will be called on the main thread 166 * after invoking this method. 167 * 168 * @param context the {@link Context} in which the handler is created 169 */ 170 public void start(Context context) { 171 if (mBluetoothProxy != null) { 172 if (DBG) 173 Log.d(TAG, "BluetoothHandler.start() was called without closing the proxy."); 174 mListener.onConnectionComplete(); 175 return; 176 } 177 startWatchdog(); 178 mBluetoothAdapter.getProfileProxy(context, mBluetoothHeadsetServiceListener, 179 BluetoothProfile.HEADSET); 180 } 181 182 public boolean startSco() { 183 if (mBluetoothProxy == null || mBluetoothDevice == null) 184 return false; 185 186 return mBluetoothProxyCompat.startScoUsingVirtualVoiceCall(mBluetoothDevice); 187 } 188 189 public boolean stopSco() { 190 if (mBluetoothProxy == null || mBluetoothDevice == null) 191 return false; 192 193 return mBluetoothProxyCompat.stopScoUsingVirtualVoiceCall(mBluetoothDevice); 194 } 195 196 public boolean isAudioConnected() { 197 if (mBluetoothProxy == null || mBluetoothDevice == null) 198 return false; 199 200 return mBluetoothProxy.isAudioConnected(mBluetoothDevice); 201 } 202 203 /** 204 * An interface for the bluetooth callbacks. 205 */ 206 public interface Listener { 207 208 /** 209 * Called when a bluetooth headset connection establishment is complete. 210 * If the connection to a bluetooth headset was successful, 211 * {@link BluetoothHeadset#startVoiceRecognition} is called before this 212 * callback to redirect the input audio to the headset, otherwise the 213 * audio input will be received from the mic. This call is done on the 214 * main thread. 215 */ 216 void onConnectionComplete(); 217 } 218}