/TalkBack/src/com/google/android/marvin/talkback/BluetoothHandler.java

http://eyes-free.googlecode.com/ · 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. package com.google.android.marvin.talkback;
  17. import android.bluetooth.BluetoothAdapter;
  18. import android.bluetooth.BluetoothDevice;
  19. import android.bluetooth.BluetoothHeadset;
  20. import android.bluetooth.BluetoothProfile;
  21. import android.content.Context;
  22. import android.os.Handler;
  23. import android.os.Message;
  24. import android.util.Log;
  25. import java.util.List;
  26. /**
  27. * A helper class for handling the Bluetooth headset.
  28. */
  29. class BluetoothHandler {
  30. private static final String TAG = BluetoothHandler.class.getSimpleName();
  31. private static final boolean DBG = true;
  32. private static final int MSG_START_BLUETOOTH = 1;
  33. private static final int MSG_STOP_BLUETOOTH = 2;
  34. private static final int MSG_CANCEL_BLUETOOTH = 3;
  35. private final Listener mListener;
  36. private final int mStartupTimeoutMillis;
  37. private final BluetoothAdapter mBluetoothAdapter;
  38. private final Handler mHandler;
  39. private final BluetoothHeadset.ServiceListener mBluetoothHeadsetServiceListener;
  40. private BluetoothDevice mBluetoothDevice;
  41. private BluetoothHeadset mBluetoothProxy;
  42. private BluetoothHeadsetCompatWrapper mBluetoothProxyCompat;
  43. private boolean mBluetoothConnectionCanceled = false;
  44. /**
  45. * Creates the Bluetooth handler. No connection to a device is made in the
  46. * constructor.
  47. *
  48. * @param listener the listener that will be notified about the bluetooth
  49. * connection state changes
  50. */
  51. public BluetoothHandler(int startupTimeoutMillis, Listener listener) {
  52. mListener = listener;
  53. mStartupTimeoutMillis = startupTimeoutMillis;
  54. mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
  55. mHandler = new Handler() {
  56. @Override
  57. public void handleMessage(Message msg) {
  58. switch (msg.what) {
  59. case MSG_START_BLUETOOTH:
  60. startBluetooth((BluetoothHeadset) msg.obj);
  61. break;
  62. case MSG_STOP_BLUETOOTH:
  63. stop();
  64. break;
  65. case MSG_CANCEL_BLUETOOTH:
  66. cancel();
  67. break;
  68. }
  69. super.handleMessage(msg);
  70. }
  71. };
  72. mBluetoothHeadsetServiceListener = new BluetoothHeadset.ServiceListener() {
  73. @Override
  74. public void onServiceConnected(int profile, BluetoothProfile proxy) {
  75. if (DBG)
  76. Log.d(TAG, "BluetoothHeadset.ServiceListener.onServiceConnected");
  77. Message.obtain(mHandler, MSG_START_BLUETOOTH, proxy).sendToTarget();
  78. }
  79. @Override
  80. public void onServiceDisconnected(int profile) {
  81. if (DBG)
  82. Log.d(TAG, "BluetoothHeadset.ServiceListener.onServiceDisconnected");
  83. Message.obtain(mHandler, MSG_STOP_BLUETOOTH).sendToTarget();
  84. }
  85. };
  86. }
  87. private void startWatchdog() {
  88. stopWatchdog();
  89. mBluetoothConnectionCanceled = false;
  90. mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_CANCEL_BLUETOOTH),
  91. mStartupTimeoutMillis);
  92. }
  93. private void stopWatchdog() {
  94. mHandler.removeMessages(MSG_CANCEL_BLUETOOTH);
  95. }
  96. private void startBluetooth(BluetoothHeadset proxy) {
  97. stopWatchdog();
  98. if (mBluetoothConnectionCanceled) {
  99. mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, proxy);
  100. return;
  101. }
  102. List<BluetoothDevice> devices = proxy.getConnectedDevices();
  103. if (devices.size() > 0) {
  104. mBluetoothDevice = devices.get(0);
  105. mBluetoothProxy = proxy;
  106. mBluetoothProxyCompat = new BluetoothHeadsetCompatWrapper(proxy);
  107. Log.i(TAG, "Connected to a bluetooth device.");
  108. } else {
  109. if (DBG)
  110. Log.d(TAG, "No device is connected, closing bluetooth proxy.");
  111. mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, proxy);
  112. }
  113. mListener.onConnectionComplete();
  114. }
  115. private void cancel() {
  116. mListener.onConnectionComplete();
  117. stop();
  118. }
  119. /**
  120. * Closes the connection to the Bluetooth proxy. This method must be called
  121. * to prevent resource leakage.
  122. */
  123. public void stop() {
  124. if (DBG)
  125. Log.d(TAG, "Closing bluetooth proxy:" + mBluetoothProxy);
  126. mBluetoothConnectionCanceled = true;
  127. stopWatchdog();
  128. if (mBluetoothProxy != null) {
  129. mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothProxy);
  130. mBluetoothProxy = null;
  131. mBluetoothProxyCompat = null;
  132. mBluetoothDevice = null;
  133. }
  134. }
  135. /**
  136. * @return {@code false} if no devices are available or the
  137. * {@link BluetoothAdapter} is turned off.
  138. */
  139. public boolean isBluetoothAvailable() {
  140. return mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()
  141. && mBluetoothAdapter.getBondedDevices() != null
  142. && mBluetoothAdapter.getBondedDevices().size() > 0;
  143. }
  144. /**
  145. * Initiates an asynchronous connection to a bluetooth device. This function
  146. * should be called on the main thread.
  147. * {@link Listener#onConnectionComplete} will be called on the main thread
  148. * after invoking this method.
  149. *
  150. * @param context the {@link Context} in which the handler is created
  151. */
  152. public void start(Context context) {
  153. if (mBluetoothProxy != null) {
  154. if (DBG)
  155. Log.d(TAG, "BluetoothHandler.start() was called without closing the proxy.");
  156. mListener.onConnectionComplete();
  157. return;
  158. }
  159. startWatchdog();
  160. mBluetoothAdapter.getProfileProxy(context, mBluetoothHeadsetServiceListener,
  161. BluetoothProfile.HEADSET);
  162. }
  163. public boolean startSco() {
  164. if (mBluetoothProxy == null || mBluetoothDevice == null)
  165. return false;
  166. return mBluetoothProxyCompat.startScoUsingVirtualVoiceCall(mBluetoothDevice);
  167. }
  168. public boolean stopSco() {
  169. if (mBluetoothProxy == null || mBluetoothDevice == null)
  170. return false;
  171. return mBluetoothProxyCompat.stopScoUsingVirtualVoiceCall(mBluetoothDevice);
  172. }
  173. public boolean isAudioConnected() {
  174. if (mBluetoothProxy == null || mBluetoothDevice == null)
  175. return false;
  176. return mBluetoothProxy.isAudioConnected(mBluetoothDevice);
  177. }
  178. /**
  179. * An interface for the bluetooth callbacks.
  180. */
  181. public interface Listener {
  182. /**
  183. * Called when a bluetooth headset connection establishment is complete.
  184. * If the connection to a bluetooth headset was successful,
  185. * {@link BluetoothHeadset#startVoiceRecognition} is called before this
  186. * callback to redirect the input audio to the headset, otherwise the
  187. * audio input will be received from the mic. This call is done on the
  188. * main thread.
  189. */
  190. void onConnectionComplete();
  191. }
  192. }