Home | History | Annotate | Download | only in accessories
      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