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