blob: 481b91805108564bd014e9d50469f2250da1a6f3 [file] [log] [blame]
Michael Wright9209c9c2015-09-03 17:57:01 +01001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of 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,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.keyboard;
18
19import android.app.AlertDialog;
20import android.bluetooth.BluetoothAdapter;
21import android.bluetooth.BluetoothDevice;
22import android.bluetooth.BluetoothManager;
23import android.bluetooth.le.BluetoothLeScanner;
24import android.bluetooth.le.ScanCallback;
25import android.bluetooth.le.ScanFilter;
Dmitry Torokhov79f00cf2015-10-22 10:07:53 -070026import android.bluetooth.le.ScanRecord;
Michael Wright9209c9c2015-09-03 17:57:01 +010027import android.bluetooth.le.ScanResult;
28import android.bluetooth.le.ScanSettings;
29import android.content.ContentResolver;
30import android.content.Context;
31import android.content.DialogInterface;
32import android.content.Intent;
33import android.content.res.Configuration;
34import android.hardware.input.InputManager;
35import android.os.Handler;
36import android.os.HandlerThread;
37import android.os.Looper;
38import android.os.Message;
39import android.os.Process;
40import android.os.SystemClock;
41import android.os.UserHandle;
42import android.provider.Settings.Secure;
43import android.text.TextUtils;
44import android.util.Slog;
45import android.view.WindowManager;
46
47import com.android.settingslib.bluetooth.BluetoothCallback;
48import com.android.settingslib.bluetooth.CachedBluetoothDevice;
49import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
50import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
51import com.android.settingslib.bluetooth.LocalBluetoothManager;
52import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
53import com.android.systemui.R;
54import com.android.systemui.SystemUI;
55
56import java.io.FileDescriptor;
57import java.io.PrintWriter;
58import java.util.Arrays;
59import java.util.Collection;
60import java.util.List;
61import java.util.Map;
62import java.util.Set;
63
64public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeChangedListener {
65 private static final String TAG = "KeyboardUI";
66 private static final boolean DEBUG = false;
67
68 // Give BT some time to start after SyUI comes up. This avoids flashing a dialog in the user's
69 // face because BT starts a little bit later in the boot process than SysUI and it takes some
70 // time for us to receive the signal that it's starting.
71 private static final long BLUETOOTH_START_DELAY_MILLIS = 10 * 1000;
72
Dmitry Torokhov365bf062015-11-05 17:40:32 -080073 // We will be scanning up to 30 seconds, after which we'll stop.
74 private static final long BLUETOOTH_SCAN_TIMEOUT_MILLIS = 30 * 1000;
75
Michael Wright9209c9c2015-09-03 17:57:01 +010076 private static final int STATE_NOT_ENABLED = -1;
77 private static final int STATE_UNKNOWN = 0;
78 private static final int STATE_WAITING_FOR_BOOT_COMPLETED = 1;
79 private static final int STATE_WAITING_FOR_TABLET_MODE_EXIT = 2;
80 private static final int STATE_WAITING_FOR_DEVICE_DISCOVERY = 3;
81 private static final int STATE_WAITING_FOR_BLUETOOTH = 4;
Dmitry Torokhov365bf062015-11-05 17:40:32 -080082 private static final int STATE_PAIRING = 5;
83 private static final int STATE_PAIRED = 6;
84 private static final int STATE_USER_CANCELLED = 7;
85 private static final int STATE_DEVICE_NOT_FOUND = 8;
Michael Wright9209c9c2015-09-03 17:57:01 +010086
87 private static final int MSG_INIT = 0;
88 private static final int MSG_ON_BOOT_COMPLETED = 1;
89 private static final int MSG_PROCESS_KEYBOARD_STATE = 2;
90 private static final int MSG_ENABLE_BLUETOOTH = 3;
91 private static final int MSG_ON_BLUETOOTH_STATE_CHANGED = 4;
92 private static final int MSG_ON_DEVICE_BOND_STATE_CHANGED = 5;
93 private static final int MSG_ON_BLUETOOTH_DEVICE_ADDED = 6;
94 private static final int MSG_ON_BLE_SCAN_FAILED = 7;
95 private static final int MSG_SHOW_BLUETOOTH_DIALOG = 8;
96 private static final int MSG_DISMISS_BLUETOOTH_DIALOG = 9;
Dmitry Torokhov365bf062015-11-05 17:40:32 -080097 private static final int MSG_BLE_ABORT_SCAN = 10;
Michael Wright9209c9c2015-09-03 17:57:01 +010098
99 private volatile KeyboardHandler mHandler;
100 private volatile KeyboardUIHandler mUIHandler;
101
102 protected volatile Context mContext;
103
104 private boolean mEnabled;
105 private String mKeyboardName;
106 private CachedBluetoothDeviceManager mCachedDeviceManager;
107 private LocalBluetoothAdapter mLocalBluetoothAdapter;
108 private LocalBluetoothProfileManager mProfileManager;
109 private boolean mBootCompleted;
110 private long mBootCompletedTime;
111
112 private int mInTabletMode = InputManager.SWITCH_STATE_UNKNOWN;
Dmitry Torokhov365bf062015-11-05 17:40:32 -0800113 private int mScanAttempt = 0;
Michael Wright9209c9c2015-09-03 17:57:01 +0100114 private ScanCallback mScanCallback;
115 private BluetoothDialog mDialog;
116
117 private int mState;
118
119 @Override
120 public void start() {
121 mContext = super.mContext;
122 HandlerThread thread = new HandlerThread("Keyboard", Process.THREAD_PRIORITY_BACKGROUND);
123 thread.start();
124 mHandler = new KeyboardHandler(thread.getLooper());
125 mHandler.sendEmptyMessage(MSG_INIT);
126 }
127
128 @Override
129 protected void onConfigurationChanged(Configuration newConfig) {
130 }
131
132 @Override
133 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
134 pw.println("KeyboardUI:");
135 pw.println(" mEnabled=" + mEnabled);
136 pw.println(" mBootCompleted=" + mEnabled);
137 pw.println(" mBootCompletedTime=" + mBootCompletedTime);
138 pw.println(" mKeyboardName=" + mKeyboardName);
139 pw.println(" mInTabletMode=" + mInTabletMode);
140 pw.println(" mState=" + stateToString(mState));
141 }
142
143 @Override
144 protected void onBootCompleted() {
145 mHandler.sendEmptyMessage(MSG_ON_BOOT_COMPLETED);
146 }
147
148 @Override
149 public void onTabletModeChanged(long whenNanos, boolean inTabletMode) {
150 if (DEBUG) {
151 Slog.d(TAG, "onTabletModeChanged(" + whenNanos + ", " + inTabletMode + ")");
152 }
153
154 if (inTabletMode && mInTabletMode != InputManager.SWITCH_STATE_ON
155 || !inTabletMode && mInTabletMode != InputManager.SWITCH_STATE_OFF) {
156 mInTabletMode = inTabletMode ?
157 InputManager.SWITCH_STATE_ON : InputManager.SWITCH_STATE_OFF;
158 processKeyboardState();
159 }
160 }
161
162 // Shoud only be called on the handler thread
163 private void init() {
164 Context context = mContext;
165 mKeyboardName =
166 context.getString(com.android.internal.R.string.config_packagedKeyboardName);
167 if (TextUtils.isEmpty(mKeyboardName)) {
168 if (DEBUG) {
169 Slog.d(TAG, "No packaged keyboard name given.");
170 }
171 return;
172 }
173
174 LocalBluetoothManager bluetoothManager = LocalBluetoothManager.getInstance(context, null);
175 if (bluetoothManager == null) {
176 if (DEBUG) {
177 Slog.e(TAG, "Failed to retrieve LocalBluetoothManager instance");
178 }
179 return;
180 }
181 mEnabled = true;
182 mCachedDeviceManager = bluetoothManager.getCachedDeviceManager();
183 mLocalBluetoothAdapter = bluetoothManager.getBluetoothAdapter();
184 mProfileManager = bluetoothManager.getProfileManager();
185 bluetoothManager.getEventManager().registerCallback(new BluetoothCallbackHandler());
186
Yohei Yukawa8ce2a532015-11-25 20:35:04 -0800187 InputManager im = context.getSystemService(InputManager.class);
Michael Wright9209c9c2015-09-03 17:57:01 +0100188 im.registerOnTabletModeChangedListener(this, mHandler);
189 mInTabletMode = im.isInTabletMode();
190
191 processKeyboardState();
192 mUIHandler = new KeyboardUIHandler();
193 }
194
195 // Should only be called on the handler thread
196 private void processKeyboardState() {
197 mHandler.removeMessages(MSG_PROCESS_KEYBOARD_STATE);
198
199 if (!mEnabled) {
200 mState = STATE_NOT_ENABLED;
201 return;
202 }
203
204 if (!mBootCompleted) {
205 mState = STATE_WAITING_FOR_BOOT_COMPLETED;
206 return;
207 }
208
209 if (mInTabletMode != InputManager.SWITCH_STATE_OFF) {
210 if (mState == STATE_WAITING_FOR_DEVICE_DISCOVERY) {
211 stopScanning();
212 }
213 mState = STATE_WAITING_FOR_TABLET_MODE_EXIT;
214 return;
215 }
216
217 final int btState = mLocalBluetoothAdapter.getState();
218 if (btState == BluetoothAdapter.STATE_TURNING_ON || btState == BluetoothAdapter.STATE_ON
219 && mState == STATE_WAITING_FOR_BLUETOOTH) {
220 // If we're waiting for bluetooth but it has come on in the meantime, or is coming
221 // on, just dismiss the dialog. This frequently happens during device startup.
222 mUIHandler.sendEmptyMessage(MSG_DISMISS_BLUETOOTH_DIALOG);
223 }
224
225 if (btState == BluetoothAdapter.STATE_TURNING_ON) {
226 mState = STATE_WAITING_FOR_BLUETOOTH;
227 // Wait for bluetooth to fully come on.
228 return;
229 }
230
231 if (btState != BluetoothAdapter.STATE_ON) {
232 mState = STATE_WAITING_FOR_BLUETOOTH;
233 showBluetoothDialog();
234 return;
235 }
236
237 CachedBluetoothDevice device = getPairedKeyboard();
Dmitry Torokhov79f00cf2015-10-22 10:07:53 -0700238 if (mState == STATE_WAITING_FOR_TABLET_MODE_EXIT || mState == STATE_WAITING_FOR_BLUETOOTH) {
239 if (device != null) {
240 // If we're just coming out of tablet mode or BT just turned on,
241 // then we want to go ahead and automatically connect to the
242 // keyboard. We want to avoid this in other cases because we might
243 // be spuriously called after the user has manually disconnected
244 // the keyboard, meaning we shouldn't try to automtically connect
245 // it again.
246 mState = STATE_PAIRED;
247 device.connect(false);
248 return;
249 }
250 mCachedDeviceManager.clearNonBondedDevices();
Michael Wright9209c9c2015-09-03 17:57:01 +0100251 }
252
253 device = getDiscoveredKeyboard();
254 if (device != null) {
255 mState = STATE_PAIRING;
256 device.startPairing();
257 } else {
258 mState = STATE_WAITING_FOR_DEVICE_DISCOVERY;
259 startScanning();
260 }
261 }
262
263 // Should only be called on the handler thread
264 public void onBootCompletedInternal() {
265 mBootCompleted = true;
266 mBootCompletedTime = SystemClock.uptimeMillis();
267 if (mState == STATE_WAITING_FOR_BOOT_COMPLETED) {
268 processKeyboardState();
269 }
270 }
271
272 // Should only be called on the handler thread
273 private void showBluetoothDialog() {
274 if (isUserSetupComplete()) {
275 long now = SystemClock.uptimeMillis();
276 long earliestDialogTime = mBootCompletedTime + BLUETOOTH_START_DELAY_MILLIS;
277 if (earliestDialogTime < now) {
278 mUIHandler.sendEmptyMessage(MSG_SHOW_BLUETOOTH_DIALOG);
279 } else {
280 mHandler.sendEmptyMessageAtTime(MSG_PROCESS_KEYBOARD_STATE, earliestDialogTime);
281 }
282 } else {
283 // If we're in setup wizard and the keyboard is docked, just automatically enable BT.
284 mLocalBluetoothAdapter.enable();
285 }
286 }
287
288 private boolean isUserSetupComplete() {
289 ContentResolver resolver = mContext.getContentResolver();
290 return Secure.getIntForUser(
291 resolver, Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
292 }
293
294 private CachedBluetoothDevice getPairedKeyboard() {
295 Set<BluetoothDevice> devices = mLocalBluetoothAdapter.getBondedDevices();
296 for (BluetoothDevice d : devices) {
297 if (mKeyboardName.equals(d.getName())) {
298 return getCachedBluetoothDevice(d);
299 }
300 }
301 return null;
302 }
303
304 private CachedBluetoothDevice getDiscoveredKeyboard() {
305 Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
306 for (CachedBluetoothDevice d : devices) {
307 if (d.getName().equals(mKeyboardName)) {
308 return d;
309 }
310 }
311 return null;
312 }
313
314
315 private CachedBluetoothDevice getCachedBluetoothDevice(BluetoothDevice d) {
316 CachedBluetoothDevice cachedDevice = mCachedDeviceManager.findDevice(d);
317 if (cachedDevice == null) {
318 cachedDevice = mCachedDeviceManager.addDevice(
319 mLocalBluetoothAdapter, mProfileManager, d);
320 }
321 return cachedDevice;
322 }
323
324 private void startScanning() {
325 BluetoothLeScanner scanner = mLocalBluetoothAdapter.getBluetoothLeScanner();
326 ScanFilter filter = (new ScanFilter.Builder()).setDeviceName(mKeyboardName).build();
327 ScanSettings settings = (new ScanSettings.Builder())
328 .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
329 .setNumOfMatches(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT)
330 .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
331 .setReportDelay(0)
332 .build();
333 mScanCallback = new KeyboardScanCallback();
334 scanner.startScan(Arrays.asList(filter), settings, mScanCallback);
Dmitry Torokhov365bf062015-11-05 17:40:32 -0800335
336 Message abortMsg = mHandler.obtainMessage(MSG_BLE_ABORT_SCAN, ++mScanAttempt, 0);
337 mHandler.sendMessageDelayed(abortMsg, BLUETOOTH_SCAN_TIMEOUT_MILLIS);
Michael Wright9209c9c2015-09-03 17:57:01 +0100338 }
339
340 private void stopScanning() {
341 if (mScanCallback != null) {
342 mLocalBluetoothAdapter.getBluetoothLeScanner().stopScan(mScanCallback);
343 mScanCallback = null;
344 }
345 }
346
347 // Should only be called on the handler thread
Dmitry Torokhov365bf062015-11-05 17:40:32 -0800348 private void bleAbortScanInternal(int scanAttempt) {
349 if (mState == STATE_WAITING_FOR_DEVICE_DISCOVERY && scanAttempt == mScanAttempt) {
350 if (DEBUG) {
351 Slog.d(TAG, "Bluetooth scan timed out");
352 }
353 stopScanning();
354 // FIXME: should we also try shutting off bluetooth if we enabled
355 // it in the first place?
356 mState = STATE_DEVICE_NOT_FOUND;
357 }
358 }
359
360 // Should only be called on the handler thread
Michael Wright9209c9c2015-09-03 17:57:01 +0100361 private void onDeviceAddedInternal(CachedBluetoothDevice d) {
362 if (mState == STATE_WAITING_FOR_DEVICE_DISCOVERY && d.getName().equals(mKeyboardName)) {
363 stopScanning();
364 d.startPairing();
365 mState = STATE_PAIRING;
366 }
367 }
368
369 // Should only be called on the handler thread
370 private void onBluetoothStateChangedInternal(int bluetoothState) {
371 if (bluetoothState == BluetoothAdapter.STATE_ON && mState == STATE_WAITING_FOR_BLUETOOTH) {
372 processKeyboardState();
373 }
374 }
375
376 // Should only be called on the handler thread
377 private void onDeviceBondStateChangedInternal(CachedBluetoothDevice d, int bondState) {
378 if (d.getName().equals(mKeyboardName) && bondState == BluetoothDevice.BOND_BONDED) {
379 // We don't need to manually connect to the device here because it will automatically
380 // try to connect after it has been paired.
381 mState = STATE_PAIRED;
382 }
383 }
384
385 // Should only be called on the handler thread
386 private void onBleScanFailedInternal() {
387 mScanCallback = null;
388 if (mState == STATE_WAITING_FOR_DEVICE_DISCOVERY) {
389 mState = STATE_DEVICE_NOT_FOUND;
390 }
391 }
392
393 private final class KeyboardUIHandler extends Handler {
394 public KeyboardUIHandler() {
395 super(Looper.getMainLooper(), null, true /*async*/);
396 }
397 @Override
398 public void handleMessage(Message msg) {
399 switch(msg.what) {
400 case MSG_SHOW_BLUETOOTH_DIALOG: {
401 DialogInterface.OnClickListener listener = new BluetoothDialogClickListener();
402 mDialog = new BluetoothDialog(mContext);
403 mDialog.setTitle(R.string.enable_bluetooth_title);
404 mDialog.setMessage(R.string.enable_bluetooth_message);
405 mDialog.setPositiveButton(R.string.enable_bluetooth_confirmation_ok, listener);
406 mDialog.setNegativeButton(android.R.string.cancel, listener);
407 mDialog.show();
408 break;
409 }
410 case MSG_DISMISS_BLUETOOTH_DIALOG: {
411 if (mDialog != null) {
412 mDialog.dismiss();
413 mDialog = null;
414 }
415 break;
416 }
417 }
418 }
419 }
420
421 private final class KeyboardHandler extends Handler {
422 public KeyboardHandler(Looper looper) {
423 super(looper, null, true /*async*/);
424 }
425
426 @Override
427 public void handleMessage(Message msg) {
428 switch(msg.what) {
429 case MSG_INIT: {
430 init();
431 break;
432 }
433 case MSG_ON_BOOT_COMPLETED: {
434 onBootCompletedInternal();
435 break;
436 }
437 case MSG_PROCESS_KEYBOARD_STATE: {
438 processKeyboardState();
439 break;
440 }
441 case MSG_ENABLE_BLUETOOTH: {
442 boolean enable = msg.arg1 == 1;
443 if (enable) {
444 mLocalBluetoothAdapter.enable();
445 } else {
446 mState = STATE_USER_CANCELLED;
447 }
Dmitry Torokhov365bf062015-11-05 17:40:32 -0800448 break;
449 }
450 case MSG_BLE_ABORT_SCAN: {
451 int scanAttempt = msg.arg1;
452 bleAbortScanInternal(scanAttempt);
453 break;
Michael Wright9209c9c2015-09-03 17:57:01 +0100454 }
455 case MSG_ON_BLUETOOTH_STATE_CHANGED: {
456 int bluetoothState = msg.arg1;
457 onBluetoothStateChangedInternal(bluetoothState);
458 break;
459 }
460 case MSG_ON_DEVICE_BOND_STATE_CHANGED: {
461 CachedBluetoothDevice d = (CachedBluetoothDevice)msg.obj;
462 int bondState = msg.arg1;
463 onDeviceBondStateChangedInternal(d, bondState);
464 break;
465 }
466 case MSG_ON_BLUETOOTH_DEVICE_ADDED: {
467 BluetoothDevice d = (BluetoothDevice)msg.obj;
468 CachedBluetoothDevice cachedDevice = getCachedBluetoothDevice(d);
469 onDeviceAddedInternal(cachedDevice);
470 break;
471
472 }
473 case MSG_ON_BLE_SCAN_FAILED: {
474 onBleScanFailedInternal();
475 break;
476 }
477 }
478 }
479 }
480
481 private final class BluetoothDialogClickListener implements DialogInterface.OnClickListener {
482 @Override
483 public void onClick(DialogInterface dialog, int which) {
484 int enable = DialogInterface.BUTTON_POSITIVE == which ? 1 : 0;
485 mHandler.obtainMessage(MSG_ENABLE_BLUETOOTH, enable, 0).sendToTarget();
486 mDialog = null;
487 }
488 }
489
490 private final class KeyboardScanCallback extends ScanCallback {
Dmitry Torokhov79f00cf2015-10-22 10:07:53 -0700491
492 private boolean isDeviceDiscoverable(ScanResult result) {
493 final ScanRecord scanRecord = result.getScanRecord();
494 final int flags = scanRecord.getAdvertiseFlags();
495 final int BT_DISCOVERABLE_MASK = 0x03;
496
497 return (flags & BT_DISCOVERABLE_MASK) != 0;
498 }
499
Michael Wright9209c9c2015-09-03 17:57:01 +0100500 @Override
501 public void onBatchScanResults(List<ScanResult> results) {
502 if (DEBUG) {
503 Slog.d(TAG, "onBatchScanResults(" + results.size() + ")");
504 }
Dmitry Torokhov79f00cf2015-10-22 10:07:53 -0700505
506 BluetoothDevice bestDevice = null;
507 int bestRssi = Integer.MIN_VALUE;
508
509 for (ScanResult result : results) {
510 if (DEBUG) {
511 Slog.d(TAG, "onBatchScanResults: considering " + result);
Michael Wright9209c9c2015-09-03 17:57:01 +0100512 }
Dmitry Torokhov79f00cf2015-10-22 10:07:53 -0700513
514 if (isDeviceDiscoverable(result) && result.getRssi() > bestRssi) {
515 bestDevice = result.getDevice();
516 bestRssi = result.getRssi();
517 }
518 }
519
520 if (bestDevice != null) {
Michael Wright9209c9c2015-09-03 17:57:01 +0100521 mHandler.obtainMessage(MSG_ON_BLUETOOTH_DEVICE_ADDED, bestDevice).sendToTarget();
522 }
523 }
524
525 @Override
526 public void onScanFailed(int errorCode) {
527 if (DEBUG) {
528 Slog.d(TAG, "onScanFailed(" + errorCode + ")");
529 }
530 mHandler.obtainMessage(MSG_ON_BLE_SCAN_FAILED).sendToTarget();
531 }
532
533 @Override
534 public void onScanResult(int callbackType, ScanResult result) {
535 if (DEBUG) {
536 Slog.d(TAG, "onScanResult(" + callbackType + ", " + result + ")");
537 }
Dmitry Torokhov79f00cf2015-10-22 10:07:53 -0700538
539 if (isDeviceDiscoverable(result)) {
540 mHandler.obtainMessage(MSG_ON_BLUETOOTH_DEVICE_ADDED,
541 result.getDevice()).sendToTarget();
542 } else if (DEBUG) {
543 Slog.d(TAG, "onScanResult: device " + result.getDevice() +
544 " is not discoverable, ignoring");
545 }
Michael Wright9209c9c2015-09-03 17:57:01 +0100546 }
547 }
548
549 private final class BluetoothCallbackHandler implements BluetoothCallback {
550 @Override
551 public void onBluetoothStateChanged(int bluetoothState) {
552 mHandler.obtainMessage(MSG_ON_BLUETOOTH_STATE_CHANGED,
553 bluetoothState, 0).sendToTarget();
554 }
555
556 @Override
557 public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
558 mHandler.obtainMessage(MSG_ON_DEVICE_BOND_STATE_CHANGED,
559 bondState, 0, cachedDevice).sendToTarget();
560 }
561
562 @Override
563 public void onDeviceAdded(CachedBluetoothDevice cachedDevice) { }
564 @Override
565 public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) { }
566 @Override
567 public void onScanningStateChanged(boolean started) { }
568 @Override
569 public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { }
570 }
571
572 private static String stateToString(int state) {
573 switch (state) {
574 case STATE_NOT_ENABLED:
575 return "STATE_NOT_ENABLED";
576 case STATE_WAITING_FOR_BOOT_COMPLETED:
577 return "STATE_WAITING_FOR_BOOT_COMPLETED";
578 case STATE_WAITING_FOR_TABLET_MODE_EXIT:
579 return "STATE_WAITING_FOR_TABLET_MODE_EXIT";
580 case STATE_WAITING_FOR_DEVICE_DISCOVERY:
581 return "STATE_WAITING_FOR_DEVICE_DISCOVERY";
582 case STATE_WAITING_FOR_BLUETOOTH:
583 return "STATE_WAITING_FOR_BLUETOOTH";
Michael Wright9209c9c2015-09-03 17:57:01 +0100584 case STATE_PAIRING:
585 return "STATE_PAIRING";
586 case STATE_PAIRED:
587 return "STATE_PAIRED";
588 case STATE_USER_CANCELLED:
589 return "STATE_USER_CANCELLED";
590 case STATE_DEVICE_NOT_FOUND:
591 return "STATE_DEVICE_NOT_FOUND";
592 case STATE_UNKNOWN:
593 default:
594 return "STATE_UNKNOWN";
595 }
596 }
597}