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 com.android.tv.settings.R; 20 import com.android.tv.settings.dialog.old.Action; 21 import com.android.tv.settings.dialog.old.ActionAdapter; 22 import com.android.tv.settings.dialog.old.ActionFragment; 23 import com.android.tv.settings.dialog.old.DialogActivity; 24 25 import android.app.FragmentManager; 26 import android.app.FragmentTransaction; 27 import android.bluetooth.BluetoothDevice; 28 import android.content.Intent; 29 import android.graphics.drawable.AnimationDrawable; 30 import android.hardware.input.InputManager; 31 import android.os.Bundle; 32 import android.os.Handler; 33 import android.os.Message; 34 import android.os.RemoteException; 35 import android.os.ServiceManager; 36 import android.os.SystemClock; 37 import android.service.dreams.DreamService; 38 import android.service.dreams.IDreamManager; 39 import android.util.Log; 40 import android.view.KeyEvent; 41 import android.view.View; 42 import android.view.ViewGroup; 43 import android.view.ViewTreeObserver; 44 import android.view.animation.DecelerateInterpolator; 45 import android.widget.FrameLayout; 46 import android.widget.ImageView; 47 import android.widget.TextView; 48 49 import java.util.ArrayList; 50 51 /** 52 * Activity for detecting and adding (pairing) new bluetooth devices. 53 */ 54 public class AddAccessoryActivity extends DialogActivity 55 implements ActionAdapter.Listener, 56 InputPairer.EventListener { 57 58 private static final boolean DEBUG = false; 59 private static final String TAG = "aah.AddAccessoryActivity"; 60 61 private static final String ACTION_CONNECT_INPUT = 62 "com.google.android.intent.action.CONNECT_INPUT"; 63 64 private static final String INTENT_EXTRA_NO_INPUT_MODE = "no_input_mode"; 65 66 private static final String KEY_BT_DEVICE = "selected_bt_device"; 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 private static final int MSG_MULTIPAIR_BLINK = 10; 82 83 private static final int CANCEL_MESSAGE_TIMEOUT = 3000; 84 private static final int DONE_MESSAGE_TIMEOUT = 3000; 85 private static final int PAIR_OPERATION_TIMEOUT = 120000; 86 private static final int CONNECT_OPERATION_TIMEOUT = 15000; 87 private static final int RESTART_DELAY = 3000; 88 private static final int LONG_PRESS_DURATION = 3000; 89 private static final int KEY_DOWN_TIME = 150; 90 private static final int TIME_TO_START_AUTOPAIR_COUNT = 5000; 91 private static final int BLINK_START = 1000; 92 93 private ActionFragment mActionFragment; 94 private ArrayList<Action> mActions; 95 private AddAccessoryContentFragment mContentFragment; 96 97 // members related to Bluetooth pairing 98 private InputPairer mBtPairer; 99 private int mPreviousStatus = InputPairer.STATUS_NONE; 100 private boolean mPairingSuccess = false; 101 private boolean mPairingBluetooth = false; 102 private ArrayList<BluetoothDevice> mBtDevices; 103 private String mCancelledAddress = ADDRESS_NONE; 104 private String mCurrentTargetAddress = ADDRESS_NONE; 105 private String mCurrentTargetStatus = ""; 106 private boolean mPairingInBackground = false; 107 108 private boolean mActionsVisible = false; 109 private FrameLayout mTopLayout; 110 private View mActionView; 111 private View mContentView; 112 private boolean mShowingMultiFragment; 113 private TextView mAutoPairText; 114 private AnimationDrawable mAnimation; 115 private int mViewOffset = 0; 116 private static final int ANIMATE_IN_DELAY = 1500; 117 private static long mStartTime; 118 private boolean mAnimateOnStart = true; 119 private boolean mDone = false; 120 private final Object mLock = new Object(); 121 122 private FragmentManager mFragmentManager; 123 private FragmentTransaction mFragmentTransaction; 124 125 private IDreamManager mDreamManager; 126 private boolean mHwKeyDown; 127 private boolean mHwKeyDidSelect; 128 private boolean mNoInputMode; 129 private boolean mActionsAnimationDone; 130 private boolean mFragmentTransactionPending; 131 132 // Internal message handler 133 private Handler mMsgHandler = new Handler() { 134 @Override 135 public void handleMessage(Message msg) { 136 switch (msg.what) { 137 case MSG_UPDATE_VIEW: 138 updateView(); 139 break; 140 case MSG_REMOVE_CANCELED: 141 mCancelledAddress = ADDRESS_NONE; 142 updateView(); 143 break; 144 case MSG_PAIRING_COMPLETE: 145 AddAccessoryActivity.this.finish(); 146 break; 147 case MSG_OP_TIMEOUT: 148 handlePairingTimeout(); 149 break; 150 case MSG_RESTART: 151 if (mBtPairer != null) { 152 mBtPairer.start(); 153 mBtPairer.cancelPairing(); 154 } 155 break; 156 case MSG_TRIGGER_SELECT_DOWN: 157 sendKeyEvent(KeyEvent.KEYCODE_DPAD_CENTER, true); 158 mHwKeyDidSelect = true; 159 sendEmptyMessageDelayed(MSG_TRIGGER_SELECT_UP, KEY_DOWN_TIME); 160 cancelPairingCountdown(); 161 break; 162 case MSG_TRIGGER_SELECT_UP: 163 sendKeyEvent(KeyEvent.KEYCODE_DPAD_CENTER, false); 164 break; 165 case MSG_START_AUTOPAIR_COUNTDOWN: 166 mAutoPairText.setVisibility(View.VISIBLE); 167 mAutoPairText.setText(String.format( 168 getString(R.string.accessories_autopair_msg), AUTOPAIR_COUNT)); 169 sendMessageDelayed(mMsgHandler.obtainMessage(MSG_AUTOPAIR_TICK, 170 AUTOPAIR_COUNT, 0, null), 1000); 171 break; 172 case MSG_AUTOPAIR_TICK: 173 int countToAutoPair = msg.arg1 - 1; 174 if (mAutoPairText != null) { 175 if (countToAutoPair <= 0) { 176 mAutoPairText.setVisibility(View.GONE); 177 // AutoPair 178 startAutoPairing(); 179 } else { 180 mAutoPairText.setText(String.format( 181 getString(R.string.accessories_autopair_msg), 182 countToAutoPair)); 183 sendMessageDelayed(mMsgHandler.obtainMessage(MSG_AUTOPAIR_TICK, 184 countToAutoPair, 0, null), 1000); 185 } 186 } 187 break; 188 case MSG_MULTIPAIR_BLINK: 189 // Kick off the blinking animation 190 ImageView backImage = (ImageView) findViewById(R.id.back_panel_image); 191 if (backImage != null) { 192 mAnimation = (AnimationDrawable) backImage.getDrawable(); 193 if (mAnimation != null) { 194 mAnimation.start(); 195 } 196 } 197 break; 198 default: 199 super.handleMessage(msg); 200 } 201 } 202 }; 203 204 @Override 205 public void onCreate(Bundle savedInstanceState) { 206 setLayoutProperties(R.layout.add_accessory_custom_two_pane_dialog, R.id.content_fragment, 207 R.id.action_fragment); 208 209 super.onCreate(savedInstanceState); 210 211 mDreamManager = IDreamManager.Stub.asInterface(ServiceManager.checkService( 212 DreamService.DREAM_SERVICE)); 213 214 mFragmentManager = getFragmentManager(); 215 216 mBtDevices = new ArrayList<BluetoothDevice>(); 217 218 mActions = new ArrayList<Action>(); 219 220 mNoInputMode = getIntent().getBooleanExtra(INTENT_EXTRA_NO_INPUT_MODE, false); 221 mHwKeyDown = false; 222 223 mActions.clear(); 224 225 mActionFragment = ActionFragment.newInstance(mActions); 226 mContentFragment = AddAccessoryContentFragment.newInstance(false); 227 setContentAndActionFragments(mContentFragment, mActionFragment); 228 mShowingMultiFragment = false; 229 230 mActionsAnimationDone = false; 231 mFragmentTransactionPending = false; 232 } 233 234 @Override 235 protected void onStart() { 236 super.onStart(); 237 238 if (DEBUG) { 239 Log.d(TAG, "onStart() mPairingInBackground = " + mPairingInBackground); 240 } 241 242 // Only do the following if we are not coming back to this activity from 243 // the Secure Pairing activity. 244 if (!mPairingInBackground) { 245 if (mAnimateOnStart) { 246 mAnimateOnStart = false; 247 ViewGroup contentView = (ViewGroup) findViewById(android.R.id.content); 248 mTopLayout = (FrameLayout) contentView.getChildAt(0); 249 250 // Fade out the old activity, and fade in the new activity. 251 overridePendingTransition(R.anim.fade_in, R.anim.fade_out); 252 253 // Set the activity background 254 int bgColor = getResources().getColor(R.color.dialog_activity_background); 255 getBackgroundDrawable().setColor(bgColor); 256 mTopLayout.setBackground(getBackgroundDrawable()); 257 258 // Delay the rest of the changes until the first layout event 259 mTopLayout.getViewTreeObserver().addOnGlobalLayoutListener( 260 new ViewTreeObserver.OnGlobalLayoutListener() { 261 @Override 262 public void onGlobalLayout() { 263 mTopLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this); 264 265 // set the Action and Content fragments to their start offsets 266 mActionView = findViewById(R.id.action_fragment); 267 mContentView = findViewById(R.id.content_fragment); 268 if (mActionView != null) { 269 mViewOffset = mActionView.getMeasuredWidth(); 270 mActionView.setTranslationX(mViewOffset); 271 mContentView.setTranslationX(mViewOffset / 2); 272 } 273 mAutoPairText = (TextView) findViewById(R.id.autopair_message); 274 if (mAutoPairText != null) { 275 mAutoPairText.setVisibility(View.GONE); 276 } 277 updateView(); 278 } 279 }); 280 } 281 282 startBluetoothPairer(); 283 284 mStartTime = SystemClock.elapsedRealtime(); 285 } 286 287 mPairingInBackground = false; 288 } 289 290 @Override 291 public void onStop() { 292 if (DEBUG) { 293 Log.d(TAG, "onStop()"); 294 } 295 if (!mPairingBluetooth) { 296 stopActivity(); 297 } else { 298 // allow activity to remain in the background while we perform the 299 // BT Secure pairing. 300 mPairingInBackground = true; 301 } 302 303 super.onStop(); 304 } 305 306 @Override 307 308 public boolean onKeyUp(int keyCode, KeyEvent event) { 309 if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_HOME) { 310 if (mPairingBluetooth && !mDone) { 311 cancelBtPairing(); 312 } 313 } 314 return super.onKeyUp(keyCode, event); 315 } 316 317 @Override 318 public void onNewIntent(Intent intent) { 319 if (ACTION_CONNECT_INPUT.equals(intent.getAction()) && 320 (intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) == 0) { 321 // We were the front most app and we got a new intent. 322 // If screen saver is going, stop it. 323 try { 324 if (mDreamManager != null && mDreamManager.isDreaming()) { 325 mDreamManager.awaken(); 326 } 327 } catch (RemoteException e) { 328 // Do nothing. 329 } 330 331 KeyEvent event = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); 332 if (event != null && event.getKeyCode() == KeyEvent.KEYCODE_PAIRING) { 333 if (event.getAction() == KeyEvent.ACTION_UP) { 334 onHwKeyEvent(false); 335 } else if (event.getAction() == KeyEvent.ACTION_DOWN) { 336 onHwKeyEvent(true); 337 } 338 } 339 } else { 340 setIntent(intent); 341 } 342 } 343 344 @Override 345 protected void onIntroAnimationFinished() { 346 mActionsAnimationDone = true; 347 if (mFragmentTransactionPending) { 348 mFragmentTransactionPending = false; 349 switchToMultipleDevicesFragment(); 350 } 351 } 352 353 @Override 354 public void onActionClicked(Action action) { 355 cancelPairingCountdown(); 356 if (!mDone) { 357 String key = action.getKey(); 358 359 if (KEY_BT_DEVICE.equals(key)) { 360 btDeviceClicked(action.getDescription()); 361 } 362 } 363 } 364 365 // Events related to a device HW key 366 protected void onHwKeyEvent(boolean keyDown) { 367 if (!mHwKeyDown) { 368 // HW key was in UP state before 369 if (keyDown) { 370 // Back key pressed down 371 mHwKeyDown = true; 372 mHwKeyDidSelect = false; 373 mMsgHandler.sendEmptyMessageDelayed(MSG_TRIGGER_SELECT_DOWN, LONG_PRESS_DURATION); 374 } 375 } else { 376 // HW key was in DOWN state before 377 if (!keyDown) { 378 // HW key released 379 mHwKeyDown = false; 380 mMsgHandler.removeMessages(MSG_TRIGGER_SELECT_DOWN); 381 if (!mHwKeyDidSelect) { 382 // key wasn't pressed long enough for selection, move selection 383 // to next item. 384 int selectedIndex = mActionFragment.getSelectedItemPosition() + 1; 385 if (selectedIndex >= mActions.size()) { 386 selectedIndex = 0; 387 } 388 mActionFragment.setSelectionSmooth(selectedIndex); 389 } 390 mHwKeyDidSelect = false; 391 } 392 } 393 } 394 395 private void sendKeyEvent(int keyCode, boolean down) { 396 InputManager iMgr = (InputManager) getSystemService(INPUT_SERVICE); 397 if (iMgr != null) { 398 long time = SystemClock.uptimeMillis(); 399 KeyEvent evt = new KeyEvent(time, time, 400 down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP, 401 keyCode, 0); 402 iMgr.injectInputEvent(evt, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); 403 } 404 } 405 406 protected void updateView() { 407 if (mActionView == null || mStartTime == 0) { 408 // view not yet ready, update will happen on first layout event 409 return; 410 } 411 412 synchronized (mLock) { 413 int prevNumDevices = mActions.size(); 414 mActions.clear(); 415 416 if (mActionFragment != null && mBtPairer != null) { 417 // Add entries for the discovered Bluetooth devices 418 for (BluetoothDevice bt : mBtDevices) { 419 String title = bt.getName(); 420 String desc; 421 if (mCurrentTargetAddress.equalsIgnoreCase(bt.getAddress()) && 422 !mCurrentTargetStatus.isEmpty()) { 423 desc = mCurrentTargetStatus; 424 } else if (mCancelledAddress.equalsIgnoreCase(bt.getAddress())) { 425 desc = getString(R.string.accessory_state_canceled); 426 } else { 427 desc = bt.getAddress(); 428 } 429 mActions.add(new Action.Builder() 430 .key(KEY_BT_DEVICE) 431 .title(title) 432 .description(desc.toUpperCase()) 433 .drawableResource(AccessoryUtils.getImageIdForDevice(bt)) 434 .build()); 435 } 436 } 437 438 // Update the main fragment. 439 ActionAdapter adapter = (ActionAdapter) mActionFragment.getAdapter(); 440 if (adapter != null) { 441 adapter.setActions(mActions); 442 } 443 444 if (!mActionsVisible && mActions.size() > 0) { 445 mActionsVisible = true; 446 long delay = ANIMATE_IN_DELAY - (SystemClock.elapsedRealtime() - mStartTime); 447 if (delay > 0) { 448 // Make sure we have a little bit of time after the activity 449 // fades in 450 // before we animate the actions in 451 mActionView.postDelayed(new Runnable() { 452 @Override 453 public void run() { 454 animateActionsIn(); 455 } 456 }, delay); 457 } else { 458 animateActionsIn(); 459 } 460 } 461 462 if (mNoInputMode) { 463 if (mActions.size() == 1 && prevNumDevices == 0) { 464 // first device added, start counter for autopair 465 mMsgHandler.sendEmptyMessageDelayed(MSG_START_AUTOPAIR_COUNTDOWN, 466 TIME_TO_START_AUTOPAIR_COUNT); 467 } else if (mActions.size() > 1) { 468 // More than one device found, cancel auto pair 469 cancelPairingCountdown(); 470 471 if (!mShowingMultiFragment && !mFragmentTransactionPending) { 472 if (mActionsAnimationDone) { 473 switchToMultipleDevicesFragment(); 474 } else { 475 mFragmentTransactionPending = true; 476 } 477 } 478 } 479 } 480 } 481 } 482 483 private void cancelPairingCountdown() { 484 // Cancel countdown 485 mMsgHandler.removeMessages(MSG_AUTOPAIR_TICK); 486 mMsgHandler.removeMessages(MSG_START_AUTOPAIR_COUNTDOWN); 487 if (mAutoPairText != null) { 488 mAutoPairText.setVisibility(View.GONE); 489 } 490 } 491 492 protected void switchToMultipleDevicesFragment() { 493 FragmentTransaction ft = mFragmentManager.beginTransaction(); 494 mContentFragment = AddAccessoryContentFragment.newInstance(true); 495 ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); 496 ft.replace(R.id.content_fragment, mContentFragment); 497 ft.disallowAddToBackStack(); 498 499 ft.commit(); 500 mMsgHandler.sendEmptyMessageDelayed(MSG_MULTIPAIR_BLINK, BLINK_START); 501 mShowingMultiFragment = true; 502 } 503 504 private void setTimeout(int timeout) { 505 cancelTimeout(); 506 mMsgHandler.sendEmptyMessageDelayed(MSG_OP_TIMEOUT, timeout); 507 } 508 509 private void cancelTimeout() { 510 mMsgHandler.removeMessages(MSG_OP_TIMEOUT); 511 } 512 513 private void animateActionsIn() { 514 prepareAndAnimateView(mContentView, 1f, mViewOffset / 2, 0, ANIMATE_IN_DURATION, 515 new DecelerateInterpolator(1.0f), true); 516 prepareAndAnimateView(mActionView, 1f, mViewOffset, 0, ANIMATE_IN_DURATION, 517 new DecelerateInterpolator(1.0f), false); 518 } 519 520 protected void startAutoPairing() { 521 if (mActions.size() > 0) { 522 onActionClicked(mActions.get(0)); 523 } 524 } 525 526 private void btDeviceClicked(String clickedAddress) { 527 if (mBtPairer != null && !mBtPairer.isInProgress()) { 528 if (mBtPairer.getStatus() == InputPairer.STATUS_WAITING_TO_PAIR && 529 mBtPairer.getTargetDevice() != null) { 530 cancelBtPairing(); 531 } else { 532 if (DEBUG) { 533 Log.d(TAG, "Looking for " + clickedAddress + 534 " in available devices to start pairing"); 535 } 536 for (BluetoothDevice target : mBtDevices) { 537 if (target.getAddress().equalsIgnoreCase(clickedAddress)) { 538 if (DEBUG) { 539 Log.d(TAG, "Found it!"); 540 } 541 mCancelledAddress = ADDRESS_NONE; 542 setPairingBluetooth(true); 543 mBtPairer.startPairing(target); 544 break; 545 } 546 } 547 } 548 } 549 } 550 551 private void cancelBtPairing() { 552 // cancel current request to pair 553 if (mBtPairer != null) { 554 if (mBtPairer.getTargetDevice() != null) { 555 mCancelledAddress = mBtPairer.getTargetDevice().getAddress(); 556 } else { 557 mCancelledAddress = ADDRESS_NONE; 558 } 559 mBtPairer.cancelPairing(); 560 } 561 mPairingSuccess = false; 562 setPairingBluetooth(false); 563 mMsgHandler.sendEmptyMessageDelayed(MSG_REMOVE_CANCELED, 564 CANCEL_MESSAGE_TIMEOUT); 565 } 566 567 private void setPairingBluetooth(boolean pairing) { 568 if (mPairingBluetooth != pairing) { 569 mPairingBluetooth = pairing; 570 } 571 } 572 573 private void startBluetoothPairer() { 574 stopBluetoothPairer(); 575 mBtPairer = new InputPairer(this, this); 576 mBtPairer.start(); 577 578 // Disable auto-pairing 579 mBtPairer.cancelPairing(); 580 581 mPairingSuccess = false; 582 statusChanged(); 583 } 584 585 private void stopBluetoothPairer() { 586 if (mBtPairer != null) { 587 mBtPairer.setListener(null); 588 mBtPairer.dispose(); 589 mBtPairer = null; 590 } 591 } 592 593 private String getMessageForStatus(int status) { 594 int msgId = 0; 595 String msg; 596 597 switch (status) { 598 case InputPairer.STATUS_WAITING_TO_PAIR: 599 case InputPairer.STATUS_PAIRING: 600 msgId = R.string.accessory_state_pairing; 601 break; 602 case InputPairer.STATUS_CONNECTING: 603 msgId = R.string.accessory_state_connecting; 604 break; 605 case InputPairer.STATUS_ERROR: 606 msgId = R.string.accessory_state_error; 607 break; 608 default: 609 return ""; 610 } 611 612 msg = getString(msgId); 613 614 return msg; 615 } 616 617 @Override 618 public void statusChanged() { 619 synchronized (mLock) { 620 if (mBtPairer == null) return; 621 622 int numDevices = mBtPairer.getAvailableDevices().size(); 623 int status = mBtPairer.getStatus(); 624 int oldStatus = mPreviousStatus; 625 mPreviousStatus = status; 626 627 String address = mBtPairer.getTargetDevice() == null ? ADDRESS_NONE : 628 mBtPairer.getTargetDevice().getAddress(); 629 630 if (DEBUG) { 631 String state = "?"; 632 switch (status) { 633 case InputPairer.STATUS_NONE: 634 state = "InputPairer.STATUS_NONEi"; 635 break; 636 case InputPairer.STATUS_SCANNING: 637 state = "InputPairer.STATUS_SCANNING"; 638 break; 639 case InputPairer.STATUS_WAITING_TO_PAIR: 640 state = "InputPairer.STATUS_WAITING_TO_PAIR"; 641 break; 642 case InputPairer.STATUS_PAIRING: 643 state = "InputPairer.STATUS_PAIRING"; 644 break; 645 case InputPairer.STATUS_CONNECTING: 646 state = "InputPairer.STATUS_CONNECTING"; 647 break; 648 case InputPairer.STATUS_ERROR: 649 state = "InputPairer.STATUS_ERROR"; 650 break; 651 } 652 long time = mBtPairer.getNextStageTime() - SystemClock.elapsedRealtime(); 653 Log.d(TAG, "Update received, number of devices:" + numDevices + " state: " + 654 state + " target device: " + address + " time to next event: " + time); 655 } 656 657 mBtDevices.clear(); 658 for (BluetoothDevice device : mBtPairer.getAvailableDevices()) { 659 mBtDevices.add(device); 660 } 661 662 cancelTimeout(); 663 664 switch (status) { 665 case InputPairer.STATUS_NONE: 666 // if we just connected to something or just tried to connect 667 // to something, restart scanning just in case the user wants 668 // to pair another device. 669 if (oldStatus == InputPairer.STATUS_CONNECTING) { 670 if (mPairingSuccess) { 671 // Pairing complete 672 mCurrentTargetStatus = getString(R.string.accessory_state_paired); 673 mMsgHandler.sendEmptyMessage(MSG_UPDATE_VIEW); 674 mMsgHandler.sendEmptyMessageDelayed(MSG_PAIRING_COMPLETE, 675 DONE_MESSAGE_TIMEOUT); 676 mDone = true; 677 if (mAnimation != null) { 678 mAnimation.setOneShot(true); 679 } 680 681 // Done, return here and just wait for the message 682 // to close the activity 683 return; 684 } 685 if (DEBUG) { 686 Log.d(TAG, "Invalidating and restarting."); 687 } 688 689 mBtPairer.invalidateDevice(mBtPairer.getTargetDevice()); 690 mBtPairer.start(); 691 mBtPairer.cancelPairing(); 692 setPairingBluetooth(false); 693 694 // if this looks like a successful connection run, reflect 695 // this in the UI, otherwise use the default message 696 if (!mPairingSuccess && InputPairer.hasValidInputDevice(this)) { 697 mPairingSuccess = true; 698 } 699 } 700 break; 701 case InputPairer.STATUS_SCANNING: 702 mPairingSuccess = false; 703 break; 704 case InputPairer.STATUS_WAITING_TO_PAIR: 705 break; 706 case InputPairer.STATUS_PAIRING: 707 // reset the pairing success value since this is now a new 708 // pairing run 709 mPairingSuccess = true; 710 setTimeout(PAIR_OPERATION_TIMEOUT); 711 break; 712 case InputPairer.STATUS_CONNECTING: 713 setTimeout(CONNECT_OPERATION_TIMEOUT); 714 break; 715 case InputPairer.STATUS_ERROR: 716 mPairingSuccess = false; 717 setPairingBluetooth(false); 718 break; 719 } 720 721 mCurrentTargetAddress = address; 722 mCurrentTargetStatus = getMessageForStatus(status); 723 mMsgHandler.sendEmptyMessage(MSG_UPDATE_VIEW); 724 } 725 } 726 727 private void stopActivity() { 728 stopBluetoothPairer(); 729 mMsgHandler.removeCallbacksAndMessages(null); 730 mAnimateOnStart = true; 731 732 // Forcing this activity to finish in OnStop, to make sure it always gets created 733 // fresh, since it has different behavior depending on the intent that launched 734 // it (Settings vs HW button press). 735 Log.d(TAG, "Calling finish() on activity.onStop()."); 736 finish(); 737 } 738 739 private void handlePairingTimeout() { 740 if (mPairingInBackground) { 741 stopActivity(); 742 } else { 743 // Either Pairing or Connecting timeout out. 744 // Display error message and post delayed message to the scanning process. 745 mPairingSuccess = false; 746 if (mBtPairer != null) { 747 mBtPairer.cancelPairing(); 748 } 749 mCurrentTargetStatus = getString(R.string.accessory_state_error); 750 mMsgHandler.sendEmptyMessage(MSG_UPDATE_VIEW); 751 mMsgHandler.sendEmptyMessageDelayed(MSG_RESTART, RESTART_DELAY); 752 } 753 } 754 755 } 756