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.util.Log;
40import android.view.KeyEvent;
41import android.view.View;
42import android.view.ViewGroup;
43import android.view.ViewTreeObserver;
44import android.view.animation.DecelerateInterpolator;
45import android.widget.FrameLayout;
46import android.widget.ImageView;
47import android.widget.TextView;
48
49import java.util.ArrayList;
50
51/**
52 * Activity for detecting and adding (pairing) new bluetooth devices.
53 */
54public 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