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