1 /* 2 * Copyright (C) 2014 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 17 package com.android.tv.settings.accessories; 18 19 import android.app.Activity; 20 import android.app.FragmentManager; 21 import android.bluetooth.BluetoothDevice; 22 import android.content.ComponentName; 23 import android.content.Intent; 24 import android.content.ServiceConnection; 25 import android.content.pm.ResolveInfo; 26 import android.hardware.input.InputManager; 27 import android.os.Bundle; 28 import android.os.Handler; 29 import android.os.IBinder; 30 import android.os.Message; 31 import android.os.Messenger; 32 import android.os.RemoteException; 33 import android.os.SystemClock; 34 import android.support.annotation.NonNull; 35 import android.transition.TransitionManager; 36 import android.util.Log; 37 import android.view.KeyEvent; 38 import android.view.View; 39 import android.view.ViewGroup; 40 import android.view.WindowManager; 41 42 import com.android.tv.settings.R; 43 44 import java.lang.ref.WeakReference; 45 import java.util.ArrayList; 46 import java.util.List; 47 48 /** 49 * Activity for detecting and adding (pairing) new bluetooth devices. 50 */ 51 public class AddAccessoryActivity extends Activity implements BluetoothDevicePairer.EventListener { 52 53 private static final boolean DEBUG = false; 54 private static final String TAG = "AddAccessoryActivity"; 55 56 private static final String ACTION_CONNECT_INPUT = 57 "com.google.android.intent.action.CONNECT_INPUT"; 58 59 private static final String INTENT_EXTRA_NO_INPUT_MODE = "no_input_mode"; 60 61 private static final String SAVED_STATE_PREFERENCE_FRAGMENT = 62 "AddAccessoryActivity.PREFERENCE_FRAGMENT"; 63 private static final String SAVED_STATE_CONTENT_FRAGMENT = 64 "AddAccessoryActivity.CONTENT_FRAGMENT"; 65 private static final String SAVED_STATE_BLUETOOTH_DEVICES = 66 "AddAccessoryActivity.BLUETOOTH_DEVICES"; 67 68 private static final String ADDRESS_NONE = "NONE"; 69 70 private static final int AUTOPAIR_COUNT = 10; 71 72 private static final int MSG_UPDATE_VIEW = 1; 73 private static final int MSG_REMOVE_CANCELED = 2; 74 private static final int MSG_PAIRING_COMPLETE = 3; 75 private static final int MSG_OP_TIMEOUT = 4; 76 private static final int MSG_RESTART = 5; 77 private static final int MSG_TRIGGER_SELECT_DOWN = 6; 78 private static final int MSG_TRIGGER_SELECT_UP = 7; 79 private static final int MSG_AUTOPAIR_TICK = 8; 80 private static final int MSG_START_AUTOPAIR_COUNTDOWN = 9; 81 82 private static final int CANCEL_MESSAGE_TIMEOUT = 3000; 83 private static final int DONE_MESSAGE_TIMEOUT = 3000; 84 private static final int PAIR_OPERATION_TIMEOUT = 120000; 85 private static final int CONNECT_OPERATION_TIMEOUT = 15000; 86 private static final int RESTART_DELAY = 3000; 87 private static final int LONG_PRESS_DURATION = 3000; 88 private static final int KEY_DOWN_TIME = 150; 89 private static final int TIME_TO_START_AUTOPAIR_COUNT = 5000; 90 private static final int EXIT_TIMEOUT_MILLIS = 90 * 1000; 91 92 private AddAccessoryPreferenceFragment mPreferenceFragment; 93 private AddAccessoryContentFragment mContentFragment; 94 95 // members related to Bluetooth pairing 96 private BluetoothDevicePairer mBluetoothPairer; 97 private int mPreviousStatus = BluetoothDevicePairer.STATUS_NONE; 98 private boolean mPairingSuccess = false; 99 private boolean mPairingBluetooth = false; 100 private List<BluetoothDevice> mBluetoothDevices; 101 private String mCancelledAddress = ADDRESS_NONE; 102 private String mCurrentTargetAddress = ADDRESS_NONE; 103 private String mCurrentTargetStatus = ""; 104 private boolean mPairingInBackground = false; 105 106 private boolean mDone = false; 107 108 private boolean mHwKeyDown; 109 private boolean mHwKeyDidSelect; 110 private boolean mNoInputMode; 111 112 private static final String GAMEPAD_PAIRING_SERVICE = 113 "com.google.android.settings.accessories.GamepadPairingService"; 114 private static final String GAMEPAD_PAIRING_PACKAGE = 115 "com.google.android.tv.remotepairing"; 116 /* Keep in sync with GamepadPairingService */ 117 private static final int GAMEPAD_MESSAGE_START_PAIRING = 1; 118 private static final int GAMEPAD_MESSAGE_STOP_PAIRING = 2; 119 private static final int GAMEPAD_MESSAGE_PAUSE_PAIRING = 3; 120 private static final int GAMEPAD_MESSAGE_RESUME_PAIRING = 4; 121 private final List<Message> mGamepadMessageQueue = new ArrayList<>(); 122 private Messenger mGamepadService; 123 private ServiceConnection mGamepadServiceConn; 124 125 private class GamepadServiceConnection implements ServiceConnection { 126 @Override 127 public void onServiceConnected(ComponentName name, IBinder service) { 128 Log.d(TAG, "Gamepad Service Connected"); 129 mGamepadService = new Messenger(service); 130 for (Message message : mGamepadMessageQueue) { 131 try { 132 mGamepadService.send(message); 133 } catch (RemoteException e) { 134 Log.e(TAG, "Remote exception sending message " + message + " " + e); 135 } 136 } 137 mGamepadMessageQueue.clear(); 138 } 139 140 @Override 141 public void onServiceDisconnected(ComponentName name) { 142 Log.d(TAG, "Gamepad Service Disconnected"); 143 mGamepadService = null; 144 } 145 }; 146 147 // Internal message handler 148 private final MessageHandler mMsgHandler = new MessageHandler(); 149 150 private static class MessageHandler extends Handler { 151 152 private WeakReference<AddAccessoryActivity> mActivityRef = new WeakReference<>(null); 153 154 public void setActivity(AddAccessoryActivity activity) { 155 mActivityRef = new WeakReference<>(activity); 156 } 157 158 @Override 159 public void handleMessage(Message msg) { 160 final AddAccessoryActivity activity = mActivityRef.get(); 161 if (activity == null) { 162 return; 163 } 164 switch (msg.what) { 165 case MSG_UPDATE_VIEW: 166 activity.updateView(); 167 break; 168 case MSG_REMOVE_CANCELED: 169 activity.mCancelledAddress = ADDRESS_NONE; 170 activity.updateView(); 171 break; 172 case MSG_PAIRING_COMPLETE: 173 activity.finish(); 174 break; 175 case MSG_OP_TIMEOUT: 176 activity.handlePairingTimeout(); 177 break; 178 case MSG_RESTART: 179 if (activity.mBluetoothPairer != null) { 180 activity.mBluetoothPairer.start(); 181 activity.mBluetoothPairer.cancelPairing(); 182 } 183 break; 184 case MSG_TRIGGER_SELECT_DOWN: 185 activity.sendKeyEvent(KeyEvent.KEYCODE_DPAD_CENTER, true); 186 activity.mHwKeyDidSelect = true; 187 sendEmptyMessageDelayed(MSG_TRIGGER_SELECT_UP, KEY_DOWN_TIME); 188 activity.cancelPairingCountdown(); 189 break; 190 case MSG_TRIGGER_SELECT_UP: 191 activity.sendKeyEvent(KeyEvent.KEYCODE_DPAD_CENTER, false); 192 break; 193 case MSG_START_AUTOPAIR_COUNTDOWN: 194 activity.setPairingText( 195 activity.getString(R.string.accessories_autopair_msg, AUTOPAIR_COUNT)); 196 sendMessageDelayed(obtainMessage(MSG_AUTOPAIR_TICK, 197 AUTOPAIR_COUNT, 0, null), 1000); 198 break; 199 case MSG_AUTOPAIR_TICK: 200 int countToAutoPair = msg.arg1 - 1; 201 if (countToAutoPair <= 0) { 202 activity.setPairingText(null); 203 // AutoPair 204 activity.startAutoPairing(); 205 } else { 206 activity.setPairingText( 207 activity.getString(R.string.accessories_autopair_msg, 208 countToAutoPair)); 209 sendMessageDelayed(obtainMessage(MSG_AUTOPAIR_TICK, 210 countToAutoPair, 0, null), 1000); 211 } 212 break; 213 default: 214 super.handleMessage(msg); 215 } 216 } 217 } 218 219 private final Handler mAutoExitHandler = new Handler(); 220 221 private final Runnable mAutoExitRunnable = new Runnable() { 222 @Override 223 public void run() { 224 finish(); 225 } 226 }; 227 228 @Override 229 public void onCreate(Bundle savedInstanceState) { 230 super.onCreate(savedInstanceState); 231 232 setContentView(R.layout.lb_dialog_fragment); 233 234 Intent intent = new Intent(); 235 intent.setAction(GAMEPAD_PAIRING_SERVICE); 236 intent.setPackage(GAMEPAD_PAIRING_PACKAGE); 237 if (serviceIntentIsHandled(intent)) { 238 // Don't auto-start the service. If it's not running we don't need to pause it. 239 mGamepadServiceConn = new GamepadServiceConnection(); 240 bindService(intent, mGamepadServiceConn, BIND_ADJUST_WITH_ACTIVITY); 241 } 242 243 pauseGamepadPairingService(); 244 245 mMsgHandler.setActivity(this); 246 247 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | 248 WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); 249 250 mNoInputMode = getIntent().getBooleanExtra(INTENT_EXTRA_NO_INPUT_MODE, false); 251 mHwKeyDown = false; 252 253 if (savedInstanceState == null) { 254 mBluetoothDevices = new ArrayList<>(); 255 } else { 256 mBluetoothDevices = 257 savedInstanceState.getParcelableArrayList(SAVED_STATE_BLUETOOTH_DEVICES); 258 } 259 260 final FragmentManager fm = getFragmentManager(); 261 if (savedInstanceState == null) { 262 mPreferenceFragment = AddAccessoryPreferenceFragment.newInstance(); 263 mContentFragment = AddAccessoryContentFragment.newInstance(); 264 fm.beginTransaction() 265 .add(R.id.action_fragment, mPreferenceFragment) 266 .add(R.id.content_fragment, mContentFragment) 267 .commit(); 268 } else { 269 mPreferenceFragment = (AddAccessoryPreferenceFragment) 270 fm.getFragment(savedInstanceState, 271 SAVED_STATE_PREFERENCE_FRAGMENT); 272 mContentFragment = (AddAccessoryContentFragment) 273 fm.getFragment(savedInstanceState, 274 SAVED_STATE_CONTENT_FRAGMENT); 275 } 276 277 rearrangeViews(); 278 } 279 280 @Override 281 protected void onSaveInstanceState(@NonNull Bundle outState) { 282 super.onSaveInstanceState(outState); 283 getFragmentManager().putFragment(outState, 284 SAVED_STATE_PREFERENCE_FRAGMENT, mPreferenceFragment); 285 getFragmentManager().putFragment(outState, 286 SAVED_STATE_CONTENT_FRAGMENT, mContentFragment); 287 outState.putParcelableList(SAVED_STATE_BLUETOOTH_DEVICES, mBluetoothDevices); 288 } 289 290 @Override 291 protected void onStart() { 292 super.onStart(); 293 294 if (DEBUG) { 295 Log.d(TAG, "onStart() mPairingInBackground = " + mPairingInBackground); 296 } 297 298 // Only do the following if we are not coming back to this activity from 299 // the Secure Pairing activity. 300 if (!mPairingInBackground) { 301 startBluetoothPairer(); 302 } 303 304 mPairingInBackground = false; 305 } 306 307 @Override 308 public void onResume() { 309 super.onResume(); 310 if (mNoInputMode) { 311 // Start timer count down for exiting activity. 312 if (DEBUG) Log.d(TAG, "starting auto-exit timer"); 313 mAutoExitHandler.postDelayed(mAutoExitRunnable, EXIT_TIMEOUT_MILLIS); 314 } 315 } 316 317 @Override 318 public void onPause() { 319 super.onPause(); 320 if (DEBUG) Log.d(TAG, "stopping auto-exit timer"); 321 mAutoExitHandler.removeCallbacks(mAutoExitRunnable); 322 } 323 324 325 @Override 326 public void onStop() { 327 if (DEBUG) { 328 Log.d(TAG, "onStop()"); 329 } 330 if (!mPairingBluetooth) { 331 stopBluetoothPairer(); 332 mMsgHandler.removeCallbacksAndMessages(null); 333 } else { 334 // allow activity to remain in the background while we perform the 335 // BT Secure pairing. 336 mPairingInBackground = true; 337 } 338 339 super.onStop(); 340 } 341 342 @Override 343 protected void onDestroy() { 344 super.onDestroy(); 345 resumeGamepadPairingService(); 346 if (mGamepadServiceConn != null) { 347 unbindService(mGamepadServiceConn); 348 mGamepadServiceConn = null; 349 } 350 stopBluetoothPairer(); 351 mMsgHandler.removeCallbacksAndMessages(null); 352 } 353 354 @Override 355 public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) { 356 if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_HOME) { 357 if (mPairingBluetooth && !mDone) { 358 cancelBtPairing(); 359 } 360 } 361 return super.onKeyUp(keyCode, event); 362 } 363 364 @Override 365 public void onNewIntent(Intent intent) { 366 if (ACTION_CONNECT_INPUT.equals(intent.getAction()) && 367 (intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) == 0) { 368 369 KeyEvent event = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); 370 if (event != null && event.getKeyCode() == KeyEvent.KEYCODE_PAIRING) { 371 if (event.getAction() == KeyEvent.ACTION_UP) { 372 onHwKeyEvent(false); 373 } else if (event.getAction() == KeyEvent.ACTION_DOWN) { 374 onHwKeyEvent(true); 375 } 376 } 377 } else { 378 setIntent(intent); 379 } 380 } 381 382 private boolean serviceIntentIsHandled(Intent intent) { 383 List<ResolveInfo> matches = getPackageManager().queryIntentServices(intent, 0); 384 if (matches != null) { 385 for (ResolveInfo info : matches) { 386 if (info.serviceInfo != null && info.serviceInfo.enabled) { 387 return true; 388 } 389 } 390 } 391 return false; 392 } 393 394 private void pauseGamepadPairingService() { 395 final Message m = Message.obtain(null, GAMEPAD_MESSAGE_PAUSE_PAIRING); 396 if (mGamepadService != null) { 397 try { 398 mGamepadService.send(m); 399 } catch (RemoteException e) { 400 Log.e(TAG, "Remote exception sending message " + m + " " + e); 401 } 402 } else { 403 Log.d(TAG, "Queueing pause message"); 404 mGamepadMessageQueue.add(m); 405 } 406 } 407 408 private void resumeGamepadPairingService() { 409 final Message m = Message.obtain(null, GAMEPAD_MESSAGE_RESUME_PAIRING); 410 if (mGamepadService != null) { 411 try { 412 mGamepadService.send(m); 413 } catch (RemoteException e) { 414 Log.e(TAG, "Remote exception sending message " + m + " " + e); 415 } 416 } else { 417 Log.d(TAG, "Queueing resume message"); 418 mGamepadMessageQueue.add(m); 419 } 420 } 421 422 423 public void onActionClicked(String address) { 424 cancelPairingCountdown(); 425 if (!mDone) { 426 btDeviceClicked(address); 427 } 428 } 429 430 // Events related to a device HW key 431 protected void onHwKeyEvent(boolean keyDown) { 432 if (!mHwKeyDown) { 433 // HW key was in UP state before 434 if (keyDown) { 435 // Back key pressed down 436 mHwKeyDown = true; 437 mHwKeyDidSelect = false; 438 mMsgHandler.sendEmptyMessageDelayed(MSG_TRIGGER_SELECT_DOWN, LONG_PRESS_DURATION); 439 } 440 } else { 441 // HW key was in DOWN state before 442 if (!keyDown) { 443 // HW key released 444 mHwKeyDown = false; 445 mMsgHandler.removeMessages(MSG_TRIGGER_SELECT_DOWN); 446 if (!mHwKeyDidSelect) { 447 // key wasn't pressed long enough for selection, move selection 448 // to next item. 449 mPreferenceFragment.advanceSelection(); 450 } 451 mHwKeyDidSelect = false; 452 } 453 } 454 } 455 456 private void sendKeyEvent(int keyCode, boolean down) { 457 InputManager iMgr = (InputManager) getSystemService(INPUT_SERVICE); 458 if (iMgr != null) { 459 long time = SystemClock.uptimeMillis(); 460 KeyEvent evt = new KeyEvent(time, time, 461 down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP, 462 keyCode, 0); 463 iMgr.injectInputEvent(evt, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); 464 } 465 } 466 467 protected void updateView() { 468 if (mPreferenceFragment == null) { 469 // view not yet ready, update will happen on first layout event 470 return; 471 } 472 473 int prevNumDevices = mPreferenceFragment.getPreferenceScreen().getPreferenceCount(); 474 475 mPreferenceFragment.updateList(mBluetoothDevices, mCurrentTargetAddress, 476 mCurrentTargetStatus, mCancelledAddress); 477 478 if (mNoInputMode) { 479 if (DEBUG) Log.d(TAG, "stopping auto-exit timer"); 480 mAutoExitHandler.removeCallbacks(mAutoExitRunnable); 481 if (mBluetoothDevices.size() == 1 && prevNumDevices == 0) { 482 // first device added, start counter for autopair 483 mMsgHandler.sendEmptyMessageDelayed(MSG_START_AUTOPAIR_COUNTDOWN, 484 TIME_TO_START_AUTOPAIR_COUNT); 485 } else { 486 487 // Start timer count down for exiting activity. 488 if (DEBUG) Log.d(TAG, "starting auto-exit timer"); 489 mAutoExitHandler.postDelayed(mAutoExitRunnable, EXIT_TIMEOUT_MILLIS); 490 491 if (mBluetoothDevices.size() > 1) { 492 // More than one device found, cancel auto pair 493 cancelPairingCountdown(); 494 } 495 } 496 } 497 498 TransitionManager.beginDelayedTransition((ViewGroup) findViewById(R.id.content_frame)); 499 500 rearrangeViews(); 501 } 502 503 private void rearrangeViews() { 504 final boolean empty = mBluetoothDevices.isEmpty(); 505 506 final View contentView = findViewById(R.id.content_fragment); 507 final ViewGroup.LayoutParams contentLayoutParams = contentView.getLayoutParams(); 508 contentLayoutParams.width = empty ? ViewGroup.LayoutParams.MATCH_PARENT : 509 getResources().getDimensionPixelSize(R.dimen.lb_content_section_width); 510 contentView.setLayoutParams(contentLayoutParams); 511 512 mContentFragment.setContentWidth(empty 513 ? getResources().getDimensionPixelSize(R.dimen.progress_fragment_content_width) 514 : getResources().getDimensionPixelSize(R.dimen.bt_progress_width_narrow)); 515 } 516 517 private void setPairingText(CharSequence text) { 518 if (mContentFragment != null) { 519 mContentFragment.setExtraText(text); 520 } 521 } 522 523 private void cancelPairingCountdown() { 524 // Cancel countdown 525 mMsgHandler.removeMessages(MSG_AUTOPAIR_TICK); 526 mMsgHandler.removeMessages(MSG_START_AUTOPAIR_COUNTDOWN); 527 setPairingText(null); 528 } 529 530 private void setTimeout(int timeout) { 531 cancelTimeout(); 532 mMsgHandler.sendEmptyMessageDelayed(MSG_OP_TIMEOUT, timeout); 533 } 534 535 private void cancelTimeout() { 536 mMsgHandler.removeMessages(MSG_OP_TIMEOUT); 537 } 538 539 protected void startAutoPairing() { 540 if (mBluetoothDevices.size() > 0) { 541 onActionClicked(mBluetoothDevices.get(0).getAddress()); 542 } 543 } 544 545 private void btDeviceClicked(String clickedAddress) { 546 if (mBluetoothPairer != null && !mBluetoothPairer.isInProgress()) { 547 if (mBluetoothPairer.getStatus() == BluetoothDevicePairer.STATUS_WAITING_TO_PAIR && 548 mBluetoothPairer.getTargetDevice() != null) { 549 cancelBtPairing(); 550 } else { 551 if (DEBUG) { 552 Log.d(TAG, "Looking for " + clickedAddress + 553 " in available devices to start pairing"); 554 } 555 for (BluetoothDevice target : mBluetoothDevices) { 556 if (target.getAddress().equalsIgnoreCase(clickedAddress)) { 557 if (DEBUG) { 558 Log.d(TAG, "Found it!"); 559 } 560 mCancelledAddress = ADDRESS_NONE; 561 setPairingBluetooth(true); 562 mBluetoothPairer.startPairing(target); 563 break; 564 } 565 } 566 } 567 } 568 } 569 570 private void cancelBtPairing() { 571 // cancel current request to pair 572 if (mBluetoothPairer != null) { 573 if (mBluetoothPairer.getTargetDevice() != null) { 574 mCancelledAddress = mBluetoothPairer.getTargetDevice().getAddress(); 575 } else { 576 mCancelledAddress = ADDRESS_NONE; 577 } 578 mBluetoothPairer.cancelPairing(); 579 } 580 mPairingSuccess = false; 581 setPairingBluetooth(false); 582 mMsgHandler.sendEmptyMessageDelayed(MSG_REMOVE_CANCELED, 583 CANCEL_MESSAGE_TIMEOUT); 584 } 585 586 private void setPairingBluetooth(boolean pairing) { 587 if (mPairingBluetooth != pairing) { 588 mPairingBluetooth = pairing; 589 } 590 } 591 592 private void startBluetoothPairer() { 593 stopBluetoothPairer(); 594 mBluetoothPairer = new BluetoothDevicePairer(this, this); 595 mBluetoothPairer.start(); 596 597 mBluetoothPairer.disableAutoPairing(); 598 599 mPairingSuccess = false; 600 statusChanged(); 601 } 602 603 private void stopBluetoothPairer() { 604 if (mBluetoothPairer != null) { 605 mBluetoothPairer.setListener(null); 606 mBluetoothPairer.dispose(); 607 mBluetoothPairer = null; 608 } 609 } 610 611 private String getMessageForStatus(int status) { 612 final int msgId; 613 String msg; 614 615 switch (status) { 616 case BluetoothDevicePairer.STATUS_WAITING_TO_PAIR: 617 case BluetoothDevicePairer.STATUS_PAIRING: 618 msgId = R.string.accessory_state_pairing; 619 break; 620 case BluetoothDevicePairer.STATUS_CONNECTING: 621 msgId = R.string.accessory_state_connecting; 622 break; 623 case BluetoothDevicePairer.STATUS_ERROR: 624 msgId = R.string.accessory_state_error; 625 break; 626 default: 627 return ""; 628 } 629 630 msg = getString(msgId); 631 632 return msg; 633 } 634 635 @Override 636 public void statusChanged() { 637 if (mBluetoothPairer == null) return; 638 639 int numDevices = mBluetoothPairer.getAvailableDevices().size(); 640 int status = mBluetoothPairer.getStatus(); 641 int oldStatus = mPreviousStatus; 642 mPreviousStatus = status; 643 644 String address = mBluetoothPairer.getTargetDevice() == null ? ADDRESS_NONE : 645 mBluetoothPairer.getTargetDevice().getAddress(); 646 647 if (DEBUG) { 648 String state = "?"; 649 switch (status) { 650 case BluetoothDevicePairer.STATUS_NONE: 651 state = "BluetoothDevicePairer.STATUS_NONE"; 652 break; 653 case BluetoothDevicePairer.STATUS_SCANNING: 654 state = "BluetoothDevicePairer.STATUS_SCANNING"; 655 break; 656 case BluetoothDevicePairer.STATUS_WAITING_TO_PAIR: 657 state = "BluetoothDevicePairer.STATUS_WAITING_TO_PAIR"; 658 break; 659 case BluetoothDevicePairer.STATUS_PAIRING: 660 state = "BluetoothDevicePairer.STATUS_PAIRING"; 661 break; 662 case BluetoothDevicePairer.STATUS_CONNECTING: 663 state = "BluetoothDevicePairer.STATUS_CONNECTING"; 664 break; 665 case BluetoothDevicePairer.STATUS_ERROR: 666 state = "BluetoothDevicePairer.STATUS_ERROR"; 667 break; 668 } 669 long time = mBluetoothPairer.getNextStageTime() - SystemClock.elapsedRealtime(); 670 Log.d(TAG, "Update received, number of devices:" + numDevices + " state: " + 671 state + " target device: " + address + " time to next event: " + time); 672 } 673 674 mBluetoothDevices.clear(); 675 for (BluetoothDevice device : mBluetoothPairer.getAvailableDevices()) { 676 mBluetoothDevices.add(device); 677 } 678 679 cancelTimeout(); 680 681 switch (status) { 682 case BluetoothDevicePairer.STATUS_NONE: 683 // if we just connected to something or just tried to connect 684 // to something, restart scanning just in case the user wants 685 // to pair another device. 686 if (oldStatus == BluetoothDevicePairer.STATUS_CONNECTING) { 687 if (mPairingSuccess) { 688 // Pairing complete 689 mCurrentTargetStatus = getString(R.string.accessory_state_paired); 690 mMsgHandler.sendEmptyMessage(MSG_UPDATE_VIEW); 691 mMsgHandler.sendEmptyMessageDelayed(MSG_PAIRING_COMPLETE, 692 DONE_MESSAGE_TIMEOUT); 693 mDone = true; 694 695 // Done, return here and just wait for the message 696 // to close the activity 697 return; 698 } 699 if (DEBUG) { 700 Log.d(TAG, "Invalidating and restarting."); 701 } 702 703 mBluetoothPairer.invalidateDevice(mBluetoothPairer.getTargetDevice()); 704 mBluetoothPairer.start(); 705 mBluetoothPairer.cancelPairing(); 706 setPairingBluetooth(false); 707 708 // if this looks like a successful connection run, reflect 709 // this in the UI, otherwise use the default message 710 if (!mPairingSuccess && BluetoothDevicePairer.hasValidInputDevice(this)) { 711 mPairingSuccess = true; 712 } 713 } 714 break; 715 case BluetoothDevicePairer.STATUS_SCANNING: 716 mPairingSuccess = false; 717 break; 718 case BluetoothDevicePairer.STATUS_WAITING_TO_PAIR: 719 break; 720 case BluetoothDevicePairer.STATUS_PAIRING: 721 // reset the pairing success value since this is now a new 722 // pairing run 723 mPairingSuccess = true; 724 setTimeout(PAIR_OPERATION_TIMEOUT); 725 break; 726 case BluetoothDevicePairer.STATUS_CONNECTING: 727 setTimeout(CONNECT_OPERATION_TIMEOUT); 728 break; 729 case BluetoothDevicePairer.STATUS_ERROR: 730 mPairingSuccess = false; 731 setPairingBluetooth(false); 732 if (mNoInputMode) { 733 clearDeviceList(); 734 } 735 break; 736 } 737 738 mCurrentTargetAddress = address; 739 mCurrentTargetStatus = getMessageForStatus(status); 740 mMsgHandler.sendEmptyMessage(MSG_UPDATE_VIEW); 741 } 742 743 private void clearDeviceList() { 744 mBluetoothDevices.clear(); 745 mBluetoothPairer.clearDeviceList(); 746 } 747 748 private void handlePairingTimeout() { 749 if (mPairingInBackground) { 750 finish(); 751 } else { 752 // Either Pairing or Connecting timeout out. 753 // Display error message and post delayed message to the scanning process. 754 mPairingSuccess = false; 755 if (mBluetoothPairer != null) { 756 mBluetoothPairer.cancelPairing(); 757 } 758 mCurrentTargetStatus = getString(R.string.accessory_state_error); 759 mMsgHandler.sendEmptyMessage(MSG_UPDATE_VIEW); 760 mMsgHandler.sendEmptyMessageDelayed(MSG_RESTART, RESTART_DELAY); 761 } 762 } 763 764 List<BluetoothDevice> getBluetoothDevices() { 765 return mBluetoothDevices; 766 } 767 768 String getCurrentTargetAddress() { 769 return mCurrentTargetAddress; 770 } 771 772 String getCurrentTargetStatus() { 773 return mCurrentTargetStatus; 774 } 775 776 String getCancelledAddress() { 777 return mCancelledAddress; 778 } 779 } 780