13cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler/*
23cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler * Copyright (C) 2015 The Android Open Source Project
33cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler *
43cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler * Licensed under the Apache License, Version 2.0 (the "License");
53cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler * you may not use this file except in compliance with the License.
63cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler * You may obtain a copy of the License at
73cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler *
83cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler *      http://www.apache.org/licenses/LICENSE-2.0
93cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler *
103cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler * Unless required by applicable law or agreed to in writing, software
113cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler * distributed under the License is distributed on an "AS IS" BASIS,
123cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler * See the License for the specific language governing permissions and
143cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler * limitations under the License
153cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler */
163cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
173cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantlerpackage com.android.tv.settings.accessories;
183cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
193cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantlerimport android.bluetooth.BluetoothAdapter;
203cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantlerimport android.bluetooth.BluetoothDevice;
213cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantlerimport android.bluetooth.BluetoothGatt;
223cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantlerimport android.bluetooth.BluetoothGattCallback;
233cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantlerimport android.bluetooth.BluetoothGattCharacteristic;
243cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantlerimport android.bluetooth.BluetoothGattService;
253cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantlerimport android.content.BroadcastReceiver;
263cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantlerimport android.content.Context;
273cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantlerimport android.content.Intent;
283cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantlerimport android.content.IntentFilter;
293cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantlerimport android.os.Bundle;
303cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantlerimport android.os.Handler;
313cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantlerimport android.support.annotation.DrawableRes;
323cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantlerimport android.support.annotation.NonNull;
333cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantlerimport android.support.v17.leanback.app.GuidedStepFragment;
343cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantlerimport android.support.v17.leanback.widget.GuidanceStylist;
353cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantlerimport android.support.v17.leanback.widget.GuidedAction;
363cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantlerimport android.support.v17.preference.LeanbackPreferenceFragment;
373cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantlerimport android.support.v7.preference.Preference;
383cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantlerimport android.support.v7.preference.PreferenceScreen;
393cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantlerimport android.util.Log;
403cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
413cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantlerimport com.android.tv.settings.R;
423cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
433cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantlerimport java.util.List;
443cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantlerimport java.util.Set;
453cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantlerimport java.util.UUID;
463cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
473cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantlerpublic class BluetoothAccessoryFragment extends LeanbackPreferenceFragment {
483cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
493cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    private static final boolean DEBUG = false;
503cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    private static final String TAG = "BluetoothAccessoryFrag";
513cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
523cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    private static final UUID GATT_BATTERY_SERVICE_UUID =
533cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            UUID.fromString("0000180f-0000-1000-8000-00805f9b34fb");
543cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    private static final UUID GATT_BATTERY_LEVEL_CHARACTERISTIC_UUID =
553cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            UUID.fromString("00002a19-0000-1000-8000-00805f9b34fb");
563cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
573cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    private static final String SAVE_STATE_UNPAIRING = "BluetoothAccessoryActivity.unpairing";
583cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
593cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    private static final int UNPAIR_TIMEOUT = 5000;
603cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
613cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    private static final String ARG_ACCESSORY_ADDRESS = "accessory_address";
623cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    private static final String ARG_ACCESSORY_NAME = "accessory_name";
633cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    private static final String ARG_ACCESSORY_ICON_ID = "accessory_icon_res";
643cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
653cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    private BluetoothDevice mDevice;
663cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    private BluetoothGatt mDeviceGatt;
673cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    private String mDeviceAddress;
683cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    private String mDeviceName;
693cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    private @DrawableRes int mDeviceImgId;
703cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    private boolean mUnpairing;
713cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    private Preference mUnpairPref;
723cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    private Preference mBatteryPref;
733cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
7405343fd6638c21c7c6287c7a18b41660be1121c9Tony Mantler    private final Handler mHandler = new Handler();
7505343fd6638c21c7c6287c7a18b41660be1121c9Tony Mantler    private Runnable mBailoutRunnable = new Runnable() {
7605343fd6638c21c7c6287c7a18b41660be1121c9Tony Mantler        @Override
7705343fd6638c21c7c6287c7a18b41660be1121c9Tony Mantler        public void run() {
7805343fd6638c21c7c6287c7a18b41660be1121c9Tony Mantler            if (isResumed() && !getFragmentManager().popBackStackImmediate()) {
7905343fd6638c21c7c6287c7a18b41660be1121c9Tony Mantler                getActivity().onBackPressed();
8005343fd6638c21c7c6287c7a18b41660be1121c9Tony Mantler            }
8105343fd6638c21c7c6287c7a18b41660be1121c9Tony Mantler        }
8205343fd6638c21c7c6287c7a18b41660be1121c9Tony Mantler    };
8305343fd6638c21c7c6287c7a18b41660be1121c9Tony Mantler
843cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    // Broadcast Receiver for Bluetooth related events
853cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
863cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        @Override
873cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        public void onReceive(Context context, Intent intent) {
883cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            BluetoothDevice device = intent
893cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                    .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
903cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            if (mUnpairing) {
913cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                if (mDevice.equals(device)) {
923cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                    // Done removing device, finish the activity
933cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                    mMsgHandler.removeCallbacks(mTimeoutRunnable);
943cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                    navigateBack();
953cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                }
963cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            }
973cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        }
983cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    };
993cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
1003cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    // Internal message handler
1013cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    private final Handler mMsgHandler = new Handler();
1023cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
1033cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    private final Runnable mTimeoutRunnable = new Runnable() {
1043cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        @Override
1053cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        public void run() {
1063cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            navigateBack();
1073cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        }
1083cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    };
1093cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
1103cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    public static BluetoothAccessoryFragment newInstance(String deviceAddress, String deviceName,
1113cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            int deviceImgId) {
1123cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        final Bundle b = new Bundle(3);
113440517f61de3f603755b1e99c7581fe274314b47Tony Mantler        prepareArgs(b, deviceAddress, deviceName, deviceImgId);
1143cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        final BluetoothAccessoryFragment f = new BluetoothAccessoryFragment();
1153cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        f.setArguments(b);
1163cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        return f;
1173cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    }
1183cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
119440517f61de3f603755b1e99c7581fe274314b47Tony Mantler    public static void prepareArgs(Bundle b, String deviceAddress, String deviceName,
120440517f61de3f603755b1e99c7581fe274314b47Tony Mantler            int deviceImgId) {
121440517f61de3f603755b1e99c7581fe274314b47Tony Mantler        b.putString(ARG_ACCESSORY_ADDRESS, deviceAddress);
122440517f61de3f603755b1e99c7581fe274314b47Tony Mantler        b.putString(ARG_ACCESSORY_NAME, deviceName);
123440517f61de3f603755b1e99c7581fe274314b47Tony Mantler        b.putInt(ARG_ACCESSORY_ICON_ID, deviceImgId);
124440517f61de3f603755b1e99c7581fe274314b47Tony Mantler    }
125440517f61de3f603755b1e99c7581fe274314b47Tony Mantler
1263cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    @Override
1273cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    public void onCreate(Bundle savedInstanceState) {
1283cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        Bundle bundle = getArguments();
1293cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        if (bundle != null) {
1303cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            mDeviceAddress = bundle.getString(ARG_ACCESSORY_ADDRESS);
1313cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            mDeviceName = bundle.getString(ARG_ACCESSORY_NAME);
1323cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            mDeviceImgId = bundle.getInt(ARG_ACCESSORY_ICON_ID);
1333cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        } else {
1343cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            mDeviceName = getString(R.string.accessory_options);
1353cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            mDeviceImgId = R.drawable.ic_qs_bluetooth_not_connected;
1363cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        }
1373cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
1383cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
1393cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        mUnpairing = savedInstanceState != null
1403cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                && savedInstanceState.getBoolean(SAVE_STATE_UNPAIRING);
1413cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
1423cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
1433cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        if (btAdapter != null) {
1443cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            Set<BluetoothDevice> bondedDevices = btAdapter.getBondedDevices();
1453cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            for (BluetoothDevice device : bondedDevices) {
1463cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                if (mDeviceAddress.equals(device.getAddress())) {
1473cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                    mDevice = device;
1483cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                    break;
1493cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                }
1503cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            }
1513cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        }
1523cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
1533cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        if (mDevice == null) {
1543cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            navigateBack();
1553cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        }
1563cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
1573cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        super.onCreate(savedInstanceState);
1583cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    }
1593cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
1603cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    @Override
1613cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    public void onStart() {
1623cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        super.onStart();
1633cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        if (mDevice != null &&
1643cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                (mDevice.getType() == BluetoothDevice.DEVICE_TYPE_LE ||
1653cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                        mDevice.getType() == BluetoothDevice.DEVICE_TYPE_DUAL)) {
1663cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            // Only LE devices support GATT
1673cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            mDeviceGatt = mDevice.connectGatt(getActivity(), true, new GattBatteryCallbacks());
1683cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        }
1693cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        // Set a broadcast receiver to let us know when the device has been removed
1703cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        IntentFilter adapterIntentFilter = new IntentFilter();
1713cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        adapterIntentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
1723cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        getActivity().registerReceiver(mBroadcastReceiver, adapterIntentFilter);
1733cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        if (mDevice != null && mDevice.getBondState() == BluetoothDevice.BOND_NONE) {
1743cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            mMsgHandler.removeCallbacks(mTimeoutRunnable);
1753cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            navigateBack();
1763cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        }
1773cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    }
1783cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
1793cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    @Override
18005343fd6638c21c7c6287c7a18b41660be1121c9Tony Mantler    public void onPause() {
18105343fd6638c21c7c6287c7a18b41660be1121c9Tony Mantler        super.onPause();
18205343fd6638c21c7c6287c7a18b41660be1121c9Tony Mantler        mHandler.removeCallbacks(mBailoutRunnable);
18305343fd6638c21c7c6287c7a18b41660be1121c9Tony Mantler    }
18405343fd6638c21c7c6287c7a18b41660be1121c9Tony Mantler
18505343fd6638c21c7c6287c7a18b41660be1121c9Tony Mantler    @Override
1863cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    public void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
1873cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        super.onSaveInstanceState(savedInstanceState);
1883cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        savedInstanceState.putBoolean(SAVE_STATE_UNPAIRING, mUnpairing);
1893cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    }
1903cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
1913cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    @Override
1923cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    public void onStop() {
1933cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        super.onStop();
1943cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        if (mDeviceGatt != null) {
1953cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            mDeviceGatt.close();
1963cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        }
19723dc470d99fee1c31c86c7f15506b3c7df3f5035Tony Mantler        getActivity().unregisterReceiver(mBroadcastReceiver);
1983cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    }
1993cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
2003cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    @Override
2013cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
2023cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        final Context themedContext = getPreferenceManager().getContext();
2033cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        final PreferenceScreen screen =
2043cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                getPreferenceManager().createPreferenceScreen(themedContext);
2053cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        screen.setTitle(mDeviceName);
2063cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
2073cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        mUnpairPref = new Preference(themedContext);
2083cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        updateUnpairPref(mUnpairPref);
2093cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        mUnpairPref.setFragment(UnpairConfirmFragment.class.getName());
2103cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        UnpairConfirmFragment.prepareArgs(mUnpairPref.getExtras(), mDeviceName, mDeviceImgId);
2113cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        screen.addPreference(mUnpairPref);
2123cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
2133cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        mBatteryPref = new Preference(themedContext);
2143cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        screen.addPreference(mBatteryPref);
2153cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        mBatteryPref.setVisible(false);
2163cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
2173cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        setPreferenceScreen(screen);
2183cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    }
2193cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
2203cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    private void updateUnpairPref(Preference pref) {
2213cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        if (mUnpairing) {
2223cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            pref.setTitle(R.string.accessory_unpairing);
2233cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            pref.setEnabled(false);
2243cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        } else {
2253cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            pref.setTitle(R.string.accessory_unpair);
2263cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            pref.setEnabled(true);
2273cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        }
2283cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    }
2293cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
2303cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    private void navigateBack() {
23105343fd6638c21c7c6287c7a18b41660be1121c9Tony Mantler        // need to post this to avoid recursing in the fragment manager.
23205343fd6638c21c7c6287c7a18b41660be1121c9Tony Mantler        mHandler.removeCallbacks(mBailoutRunnable);
23305343fd6638c21c7c6287c7a18b41660be1121c9Tony Mantler        mHandler.post(mBailoutRunnable);
2343cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    }
2353cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
23605343fd6638c21c7c6287c7a18b41660be1121c9Tony Mantler    private void unpairDevice() {
2373cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        if (mDevice != null) {
2383cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            int state = mDevice.getBondState();
2393cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
2403cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            if (state == BluetoothDevice.BOND_BONDING) {
2413cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                mDevice.cancelBondProcess();
2423cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            }
2433cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
2443cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            if (state != BluetoothDevice.BOND_NONE) {
2453cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                mUnpairing = true;
2463cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                // Set a timeout, just in case we don't receive the unpair notification we
2473cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                // use to finish the activity
2483cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                mMsgHandler.postDelayed(mTimeoutRunnable, UNPAIR_TIMEOUT);
2493cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                final boolean successful = mDevice.removeBond();
2503cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                if (successful) {
2513cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                    if (DEBUG) {
2523cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                        Log.d(TAG, "Bluetooth device successfully unpaired.");
2533cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                    }
2543cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                    // set the dialog to a waiting state
2553cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                    if (mUnpairPref != null) {
2563cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                        updateUnpairPref(mUnpairPref);
2573cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                    }
2583cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                } else {
2593cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                    Log.e(TAG, "Failed to unpair Bluetooth Device: " + mDevice.getName());
2603cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                }
2613cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            }
2623cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        } else {
2633cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            Log.e(TAG, "Bluetooth device not found. Address = " + mDeviceAddress);
2643cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        }
2653cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    }
2663cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
2673cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    private class GattBatteryCallbacks extends BluetoothGattCallback {
2683cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        @Override
2693cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
2703cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            if (DEBUG) {
2713cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                Log.d(TAG, "Connection status:" + status + " state:" + newState);
2723cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            }
2733cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothGatt.STATE_CONNECTED) {
2743cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                gatt.discoverServices();
2753cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            }
2763cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        }
2773cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
2783cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        @Override
2793cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
2803cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            if (status != BluetoothGatt.GATT_SUCCESS) {
2813cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                if (DEBUG) {
2823cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                    Log.e(TAG, "Service discovery failure on " + gatt);
2833cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                }
2843cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                return;
2853cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            }
2863cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
2873cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            final BluetoothGattService battService = gatt.getService(GATT_BATTERY_SERVICE_UUID);
2883cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            if (battService == null) {
2893cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                if (DEBUG) {
2903cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                    Log.d(TAG, "No battery service");
2913cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                }
2923cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                return;
2933cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            }
2943cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
2953cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            final BluetoothGattCharacteristic battLevel =
2963cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                    battService.getCharacteristic(GATT_BATTERY_LEVEL_CHARACTERISTIC_UUID);
2973cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            if (battLevel == null) {
2983cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                if (DEBUG) {
2993cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                    Log.d(TAG, "No battery level");
3003cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                }
3013cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                return;
3023cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            }
3033cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
3043cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            gatt.readCharacteristic(battLevel);
3053cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        }
3063cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
3073cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        @Override
3083cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        public void onCharacteristicRead(BluetoothGatt gatt,
3093cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                BluetoothGattCharacteristic characteristic, int status) {
3103cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            if (status != BluetoothGatt.GATT_SUCCESS) {
3113cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                if (DEBUG) {
3123cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                    Log.e(TAG, "Read characteristic failure on " + gatt + " " + characteristic);
3133cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                }
3143cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                return;
3153cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            }
3163cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
3173cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            if (GATT_BATTERY_LEVEL_CHARACTERISTIC_UUID.equals(characteristic.getUuid())) {
3183cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                final int batteryLevel =
3193cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                        characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0);
3203cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                mMsgHandler.post(new Runnable() {
3213cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                    @Override
3223cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                    public void run() {
3233cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                        if (mBatteryPref != null && !mUnpairing) {
3243cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                            mBatteryPref.setTitle(getString(R.string.accessory_battery,
3253cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                                    batteryLevel));
3263cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                            mBatteryPref.setVisible(true);
3273cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                        }
3283cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                    }
3293cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                });
3303cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            }
3313cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        }
3323cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    }
3333cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
3343cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    public static class UnpairConfirmFragment extends GuidedStepFragment {
3353cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
3363cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        public static void prepareArgs(@NonNull Bundle args, String deviceName,
3373cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                @DrawableRes int deviceImgId) {
3383cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            args.putString(ARG_ACCESSORY_NAME, deviceName);
3393cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            args.putInt(ARG_ACCESSORY_ICON_ID, deviceImgId);
3403cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        }
3413cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
3423cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        @NonNull
3433cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        @Override
3443cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
3453cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            return new GuidanceStylist.Guidance(
3463cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                    getString(R.string.accessory_unpair),
3473cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                    null,
3483cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                    getArguments().getString(ARG_ACCESSORY_NAME),
3493cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                    getContext().getDrawable(getArguments().getInt(ARG_ACCESSORY_ICON_ID,
3503cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                            R.drawable.ic_qs_bluetooth_not_connected))
3513cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            );
3523cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        }
3533cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
3543cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        @Override
3553cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        public void onCreateActions(@NonNull List<GuidedAction> actions,
3563cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                Bundle savedInstanceState) {
3573cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            final Context context = getContext();
358846b83807749cd73aa1b75749e8d072e8f58860dDake Gu            actions.add(new GuidedAction.Builder(context)
359846b83807749cd73aa1b75749e8d072e8f58860dDake Gu                    .clickAction(GuidedAction.ACTION_ID_OK).build());
360846b83807749cd73aa1b75749e8d072e8f58860dDake Gu            actions.add(new GuidedAction.Builder(context)
361846b83807749cd73aa1b75749e8d072e8f58860dDake Gu                    .clickAction(GuidedAction.ACTION_ID_CANCEL).build());
3623cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        }
3633cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler
3643cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        @Override
3653cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        public void onGuidedActionClicked(GuidedAction action) {
3663cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            if (action.getId() == GuidedAction.ACTION_ID_OK) {
3673cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                final BluetoothAccessoryFragment fragment =
3683cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                        (BluetoothAccessoryFragment) getTargetFragment();
3693cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                fragment.unpairDevice();
3703cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            } else if (action.getId() == GuidedAction.ACTION_ID_CANCEL) {
3713cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                getFragmentManager().popBackStack();
3723cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            } else {
3733cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler                super.onGuidedActionClicked(action);
3743cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler            }
3753cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler        }
3763cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler    }
3773cec83eb4ef203c51b172e13d1b79d4ee81c4d81Tony Mantler}
378