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