1843fbc4b10a2c62cb3848836907630f0d56c647eroger xue/*
2843fbc4b10a2c62cb3848836907630f0d56c647eroger xue * Copyright (C) 2017 The Android Open Source Project
3843fbc4b10a2c62cb3848836907630f0d56c647eroger xue *
4843fbc4b10a2c62cb3848836907630f0d56c647eroger xue * Licensed under the Apache License, Version 2.0 (the "License");
5843fbc4b10a2c62cb3848836907630f0d56c647eroger xue * you may not use this file except in compliance with the License.
6843fbc4b10a2c62cb3848836907630f0d56c647eroger xue * You may obtain a copy of the License at
7843fbc4b10a2c62cb3848836907630f0d56c647eroger xue *
8843fbc4b10a2c62cb3848836907630f0d56c647eroger xue *      http://www.apache.org/licenses/LICENSE-2.0
9843fbc4b10a2c62cb3848836907630f0d56c647eroger xue *
10843fbc4b10a2c62cb3848836907630f0d56c647eroger xue * Unless required by applicable law or agreed to in writing, software
11843fbc4b10a2c62cb3848836907630f0d56c647eroger xue * distributed under the License is distributed on an "AS IS" BASIS,
12843fbc4b10a2c62cb3848836907630f0d56c647eroger xue * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13843fbc4b10a2c62cb3848836907630f0d56c647eroger xue * See the License for the specific language governing permissions and
14843fbc4b10a2c62cb3848836907630f0d56c647eroger xue * limitations under the License.
15843fbc4b10a2c62cb3848836907630f0d56c647eroger xue */
16843fbc4b10a2c62cb3848836907630f0d56c647eroger xuepackage com.android.car.settings.bluetooth;
17843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
18843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.annotation.NonNull;
19843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.bluetooth.BluetoothAdapter;
20843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.bluetooth.BluetoothClass;
21843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.bluetooth.BluetoothDevice;
22843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.content.Context;
23843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.content.res.Resources;
24843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.os.AsyncTask;
25843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.os.Handler;
26843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.os.Looper;
27262c78bd157917a1898cf46fdbf1d2c02c8493d7Lujiang Xueimport android.text.TextUtils;
28843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.util.Log;
29843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.util.Pair;
30843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.support.v7.widget.RecyclerView;
31843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.view.LayoutInflater;
32843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.view.View;
33843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.view.View.OnClickListener;
34843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.view.ViewGroup;
35843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.widget.ImageButton;
36843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.widget.ImageView;
37843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.widget.TextView;
38843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.widget.Toast;
39843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
40843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport com.android.car.settings.R;
4124b50803fa603b04e74752beb19f85038515919aroger xueimport com.android.car.settings.common.BaseFragment;
42d28162c2c6c760c760125f444069841f1e4d21d9Rakesh Iyerimport com.android.car.view.PagedListView;
43843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport com.android.settingslib.bluetooth.BluetoothCallback;
44843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport com.android.settingslib.bluetooth.BluetoothDeviceFilter;
45843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport com.android.settingslib.bluetooth.CachedBluetoothDevice;
46843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
47843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport com.android.settingslib.bluetooth.LocalBluetoothAdapter;
48843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport com.android.settingslib.bluetooth.LocalBluetoothManager;
49843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport com.android.settingslib.bluetooth.LocalBluetoothProfile;
50843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport com.android.settingslib.bluetooth.HidProfile;
51843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
52843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport java.util.ArrayList;
53843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport java.util.Collection;
54843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport java.util.Collections;
55843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport java.util.HashSet;
56843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport java.util.List;
57843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport java.util.Set;
58843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
59843fbc4b10a2c62cb3848836907630f0d56c647eroger xue/**
60843fbc4b10a2c62cb3848836907630f0d56c647eroger xue * Renders {@link android.bluetooth.BluetoothDevice} to a view to be displayed as a row in a list.
61843fbc4b10a2c62cb3848836907630f0d56c647eroger xue */
62843fbc4b10a2c62cb3848836907630f0d56c647eroger xuepublic class BluetoothDeviceListAdapter
63843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        extends RecyclerView.Adapter<BluetoothDeviceListAdapter.ViewHolder>
64843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        implements PagedListView.ItemCap, BluetoothCallback {
65843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private static final String TAG = "BluetoothDeviceListAdapter";
66843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private static final int DEVICE_ROW_TYPE = 1;
67843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private static final int BONDED_DEVICE_HEADER_TYPE = 2;
68843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private static final int AVAILABLE_DEVICE_HEADER_TYPE = 3;
69843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private static final int NUM_OF_HEADERS = 2;
70843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public static final int DELAY_MILLIS = 1000;
71843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
72843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private final Handler mHandler = new Handler(Looper.getMainLooper());
73843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private final HashSet<CachedBluetoothDevice> mBondedDevices = new HashSet<>();
74843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private final HashSet<CachedBluetoothDevice> mAvailableDevices = new HashSet<>();
75843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private final LocalBluetoothAdapter mLocalAdapter;
76843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private final LocalBluetoothManager mLocalManager;
77843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private final CachedBluetoothDeviceManager mDeviceManager;
78843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private final Context mContext;
7924b50803fa603b04e74752beb19f85038515919aroger xue    private final BaseFragment.FragmentController mFragmentController;
80843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
81843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    /* Talk-back descriptions for various BT icons */
82843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public final String mComputerDescription;
83843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public final String mInputPeripheralDescription;
84843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public final String mHeadsetDescription;
85843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public final String mPhoneDescription;
86843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public final String mImagingDescription;
87843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public final String mHeadphoneDescription;
88843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public final String mBluetoothDescription;
89843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
90c232398a88c4caffafc601dc2482ed3ad430df3bLujiang Xue    private SortTask mSortTask;
91843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
92843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private ArrayList<CachedBluetoothDevice> mBondedDevicesSorted = new ArrayList<>();
93843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private ArrayList<CachedBluetoothDevice> mAvailableDevicesSorted = new ArrayList<>();
94843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
95843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    class ViewHolder extends RecyclerView.ViewHolder {
96843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        private final ImageView mIcon;
97b9104e0e877d1f2334e1cbb1715c7fd320f62345roger xue        private final TextView mTitle;
98843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        private final TextView mDesc;
99843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        private final ImageButton mActionButton;
100843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        private final DeviceAttributeChangeCallback mCallback =
101843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                new DeviceAttributeChangeCallback(this);
102843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
103843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        public ViewHolder(View view) {
104843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            super(view);
105b9104e0e877d1f2334e1cbb1715c7fd320f62345roger xue            mTitle = (TextView) view.findViewById(R.id.title);
106843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            mDesc = (TextView) view.findViewById(R.id.desc);
107843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            mIcon = (ImageView) view.findViewById(R.id.icon);
108843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            mActionButton = (ImageButton) view.findViewById(R.id.action);
109843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            view.setOnClickListener(new BluetoothClickListener(this));
110843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
111843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
112843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
113843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public BluetoothDeviceListAdapter(
11424b50803fa603b04e74752beb19f85038515919aroger xue            Context context,
11524b50803fa603b04e74752beb19f85038515919aroger xue            LocalBluetoothManager localBluetoothManager,
11624b50803fa603b04e74752beb19f85038515919aroger xue            BaseFragment.FragmentController fragmentController) {
117843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mContext = context;
118843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mLocalManager = localBluetoothManager;
11924b50803fa603b04e74752beb19f85038515919aroger xue        mFragmentController = fragmentController;
120843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mLocalAdapter = mLocalManager.getBluetoothAdapter();
121843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mDeviceManager = mLocalManager.getCachedDeviceManager();
122843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
123843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        Resources r = context.getResources();
124843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mComputerDescription = r.getString(R.string.bluetooth_talkback_computer);
125843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mInputPeripheralDescription = r.getString(
126843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                R.string.bluetooth_talkback_input_peripheral);
127843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mHeadsetDescription = r.getString(R.string.bluetooth_talkback_headset);
128843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mPhoneDescription = r.getString(R.string.bluetooth_talkback_phone);
129843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mImagingDescription = r.getString(R.string.bluetooth_talkback_imaging);
130843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mHeadphoneDescription = r.getString(R.string.bluetooth_talkback_headphone);
131843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mBluetoothDescription = r.getString(R.string.bluetooth_talkback_bluetooth);
132843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
133843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
134843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public void start() {
135843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mLocalManager.getEventManager().registerCallback(this);
136843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (mLocalAdapter.isEnabled()) {
137843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            mLocalAdapter.startScanning(true);
138843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            addBondDevices();
139843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            addCachedDevices();
140843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
141c232398a88c4caffafc601dc2482ed3ad430df3bLujiang Xue        // create task here to avoid re-executing existing tasks.
142c232398a88c4caffafc601dc2482ed3ad430df3bLujiang Xue        mSortTask = new SortTask();
143843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mSortTask.execute();
144843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
145843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
146843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public void stop() {
147843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mLocalAdapter.stopScanning();
148843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mDeviceManager.clearNonBondedDevices();
149843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mLocalManager.getEventManager().unregisterCallback(this);
150843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mBondedDevices.clear();
151843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mAvailableDevices.clear();
152843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mSortTask.cancel(true);
153843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
154843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
155843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    @Override
156843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public BluetoothDeviceListAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
157843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            int viewType) {
158843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        View v;
159843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
160843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        switch (viewType) {
161843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            case BONDED_DEVICE_HEADER_TYPE:
1621e632a5036e3d29d7fbf9078939ba247aec1ea57roger xue                v = layoutInflater.inflate(R.layout.single_text_line_item, parent, false);
1635e02a7c64fa4dbcbbef81e1e582ff381287f2e34roger xue                v.setEnabled(false);
1641e632a5036e3d29d7fbf9078939ba247aec1ea57roger xue                ((TextView) v.findViewById(R.id.title)).setText(
16524b50803fa603b04e74752beb19f85038515919aroger xue                        R.string.bluetooth_preference_paired_devices);
166843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                break;
167843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            case AVAILABLE_DEVICE_HEADER_TYPE:
1681e632a5036e3d29d7fbf9078939ba247aec1ea57roger xue                v = layoutInflater.inflate(R.layout.single_text_line_item, parent, false);
1695e02a7c64fa4dbcbbef81e1e582ff381287f2e34roger xue                v.setEnabled(false);
1701e632a5036e3d29d7fbf9078939ba247aec1ea57roger xue                ((TextView) v.findViewById(R.id.title)).setText(
17124b50803fa603b04e74752beb19f85038515919aroger xue                        R.string.bluetooth_preference_found_devices);
172843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                break;
173843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            default:
174ddb7544cfc9f5263d719c9d2760451e9316d3bderoger xue                v = layoutInflater.inflate(R.layout.icon_widget_line_item, parent, false);
175843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
176843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        return new ViewHolder(v);
177843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
178843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
179843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    @Override
180843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public int getItemCount() {
181843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        return mAvailableDevicesSorted.size() + NUM_OF_HEADERS + mBondedDevicesSorted.size();
182843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
183843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
184843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    @Override
185843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public void setMaxItems(int maxItems) {
186843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        // no limit in this list.
187843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
188843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
189843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    @Override
190843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public void onBindViewHolder(ViewHolder holder, int position) {
191843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        final CachedBluetoothDevice bluetoothDevice = getItem(position);
192843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (bluetoothDevice == null) {
193843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            // this row is for in-list headers
194843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            return;
195843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
196843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (holder.getOldPosition() != RecyclerView.NO_POSITION) {
197843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            getItem(holder.getOldPosition()).unregisterCallback(holder.mCallback);
198843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
199843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        bluetoothDevice.registerCallback(holder.mCallback);
200b9104e0e877d1f2334e1cbb1715c7fd320f62345roger xue        holder.mTitle.setText(bluetoothDevice.getName());
201843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        Pair<Integer, String> pair = getBtClassDrawableWithDescription(bluetoothDevice);
202843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        holder.mIcon.setImageResource(pair.first);
20309a303c04a4e679d752e7469dfcdce41e8e02debJack He        String summaryText = bluetoothDevice.getConnectionSummary();
20409a303c04a4e679d752e7469dfcdce41e8e02debJack He        if (summaryText != null) {
20509a303c04a4e679d752e7469dfcdce41e8e02debJack He            holder.mDesc.setText(summaryText);
206843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            holder.mDesc.setVisibility(View.VISIBLE);
207843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        } else {
208843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            holder.mDesc.setVisibility(View.GONE);
209843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
210843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (BluetoothDeviceFilter.BONDED_DEVICE_FILTER.matches(bluetoothDevice.getDevice())) {
211843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            holder.mActionButton.setVisibility(View.VISIBLE);
212843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            holder.mActionButton.setOnClickListener(v -> {
21324b50803fa603b04e74752beb19f85038515919aroger xue                mFragmentController.launchFragment(
21424b50803fa603b04e74752beb19f85038515919aroger xue                        BluetoothDetailFragment.getInstance(bluetoothDevice.getDevice()));
215843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                });
216843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        } else {
217843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            holder.mActionButton.setVisibility(View.GONE);
218843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
219843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
220843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
221843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    @Override
222843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public int getItemViewType(int position) {
223843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        // the first row is the header for the bonded device list;
224843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (position == 0) {
225843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            return BONDED_DEVICE_HEADER_TYPE;
226843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
227843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        // after the end of the bonded device list is the header of the available device list.
228843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (position == mBondedDevicesSorted.size() + 1) {
229843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            return AVAILABLE_DEVICE_HEADER_TYPE;
230843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
231843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        return DEVICE_ROW_TYPE;
232843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
233843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
234843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private CachedBluetoothDevice getItem(int position) {
235843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (position > 0 && position <= mBondedDevicesSorted.size()) {
236843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            // off set the header row
237843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            return mBondedDevicesSorted.get(position - 1);
238843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
239843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (position > mBondedDevicesSorted.size() + 1
240843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                && position <= mBondedDevicesSorted.size() + 1 + mAvailableDevicesSorted.size()) {
241843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            // off set two header row and the size of bonded device list.
242843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            return mAvailableDevicesSorted.get(
243843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                    position - NUM_OF_HEADERS - mBondedDevicesSorted.size());
244843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
245843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        // otherwise it's a in list header
246843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        return null;
247843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
248843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
249843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    // callback functions
250843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    @Override
251843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
252843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (addDevice(cachedDevice)) {
253843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            ArrayList<CachedBluetoothDevice> devices = new ArrayList<>(mBondedDevices);
254843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            Collections.sort(devices);
255843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            mBondedDevicesSorted = devices;
256843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            notifyDataSetChanged();
257843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
258843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
259843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
260843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    @Override
261843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {
262a76987ecf243fa3ff76f7ea8a40597baeb763e3croger xue        onDeviceDeleted(cachedDevice, true /* reset */);
263843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
264843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
265843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    @Override
266843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public void onBluetoothStateChanged(int bluetoothState) {
267843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        switch (bluetoothState) {
268843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            case BluetoothAdapter.STATE_OFF:
269843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                mBondedDevices.clear();
270843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                mBondedDevicesSorted.clear();
271843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                mAvailableDevices.clear();
272843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                mAvailableDevicesSorted.clear();
273843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                notifyDataSetChanged();
274843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                break;
275843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            case BluetoothAdapter.STATE_ON:
276843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                mLocalAdapter.startScanning(true);
277843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                addBondDevices();
278843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                addCachedDevices();
279843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                break;
280843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            default:
281843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
282843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
283843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
284a76987ecf243fa3ff76f7ea8a40597baeb763e3croger xue    public void reset() {
285a76987ecf243fa3ff76f7ea8a40597baeb763e3croger xue        mBondedDevices.clear();
286a76987ecf243fa3ff76f7ea8a40597baeb763e3croger xue        mBondedDevicesSorted.clear();
287a76987ecf243fa3ff76f7ea8a40597baeb763e3croger xue        mAvailableDevices.clear();
288a76987ecf243fa3ff76f7ea8a40597baeb763e3croger xue        mAvailableDevicesSorted.clear();
289a76987ecf243fa3ff76f7ea8a40597baeb763e3croger xue        mLocalAdapter.startScanning(true);
290a76987ecf243fa3ff76f7ea8a40597baeb763e3croger xue        addBondDevices();
291a76987ecf243fa3ff76f7ea8a40597baeb763e3croger xue        addCachedDevices();
292a76987ecf243fa3ff76f7ea8a40597baeb763e3croger xue    }
293a76987ecf243fa3ff76f7ea8a40597baeb763e3croger xue
294843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    @Override
295843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public void onScanningStateChanged(boolean started) {
296843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        // don't care
297843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
298843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
299843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    @Override
300843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
301a76987ecf243fa3ff76f7ea8a40597baeb763e3croger xue        onDeviceDeleted(cachedDevice, false /* reset */);
302843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        onDeviceAdded(cachedDevice);
303843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
304843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
305843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    /**
306843fbc4b10a2c62cb3848836907630f0d56c647eroger xue     * Call back for the first connection or the last connection to ANY device/profile. Not
307843fbc4b10a2c62cb3848836907630f0d56c647eroger xue     * suitable for monitor per device level connection.
308843fbc4b10a2c62cb3848836907630f0d56c647eroger xue     */
309843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    @Override
310843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
311843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        onDeviceDeleted(cachedDevice, false);
312843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        onDeviceAdded(cachedDevice);
313843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
314843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
315843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private void onDeviceDeleted(CachedBluetoothDevice cachedDevice, boolean refresh) {
316843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        // the device might changed bonding state, so need to remove from both sets.
317843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (mBondedDevices.remove(cachedDevice)) {
318843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            mBondedDevicesSorted.remove(cachedDevice);
319843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
320843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mAvailableDevices.remove(cachedDevice);
321843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (refresh) {
322843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            notifyDataSetChanged();
323843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
324843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
325843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
326843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private void addDevices(Collection<CachedBluetoothDevice> cachedDevices) {
327843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        boolean needSort = false;
328843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        for (CachedBluetoothDevice device : cachedDevices) {
329843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            if (addDevice(device)) {
330843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                needSort = true;
331843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            }
332843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
333843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (needSort) {
334843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            ArrayList<CachedBluetoothDevice> devices =
335843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                    new ArrayList<CachedBluetoothDevice>(mBondedDevices);
336843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            Collections.sort(devices);
337843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            mBondedDevicesSorted = devices;
338843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            notifyDataSetChanged();
339843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
340843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
341843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
342843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    /**
343843fbc4b10a2c62cb3848836907630f0d56c647eroger xue     * @return {@code true} if list changed and needed sort again.
344843fbc4b10a2c62cb3848836907630f0d56c647eroger xue     */
345843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private boolean addDevice(CachedBluetoothDevice cachedDevice) {
346843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        boolean needSort = false;
347843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (BluetoothDeviceFilter.BONDED_DEVICE_FILTER.matches(cachedDevice.getDevice())) {
348843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            if (mBondedDevices.add(cachedDevice)) {
349843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                needSort = true;
350843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            }
351843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
352843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (BluetoothDeviceFilter.UNBONDED_DEVICE_FILTER.matches(cachedDevice.getDevice())) {
353a76987ecf243fa3ff76f7ea8a40597baeb763e3croger xue            // reset is done at SortTask.
354843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            mAvailableDevices.add(cachedDevice);
355843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
356843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        return needSort;
357843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
358843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
359843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private void addBondDevices() {
360843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        Set<BluetoothDevice> bondedDevices = mLocalAdapter.getBondedDevices();
361843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (bondedDevices == null) {
362843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            return;
363843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
364843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        ArrayList<CachedBluetoothDevice> cachedBluetoothDevices = new ArrayList<>();
365843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        for (BluetoothDevice device : bondedDevices) {
366843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
367843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            if (cachedDevice == null) {
368843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                cachedDevice = mDeviceManager.addDevice(
369843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                        mLocalAdapter, mLocalManager.getProfileManager(), device);
370843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            }
371843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            cachedBluetoothDevices.add(cachedDevice);
372843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
373843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        addDevices(cachedBluetoothDevices);
374843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
375843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
376843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private void addCachedDevices() {
377843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        addDevices(mDeviceManager.getCachedDevicesCopy());
378843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
379843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
380843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private Pair<Integer, String> getBtClassDrawableWithDescription(
381843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            CachedBluetoothDevice bluetoothDevice) {
382843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        BluetoothClass btClass = bluetoothDevice.getBtClass();
383843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (btClass != null) {
384843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            switch (btClass.getMajorDeviceClass()) {
385843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                case BluetoothClass.Device.Major.COMPUTER:
386843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                    return new Pair<>(R.drawable.ic_bt_laptop, mComputerDescription);
387843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
388843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                case BluetoothClass.Device.Major.PHONE:
389843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                    return new Pair<>(R.drawable.ic_bt_cellphone, mPhoneDescription);
390843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
391843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                case BluetoothClass.Device.Major.PERIPHERAL:
392843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                    return new Pair<>(HidProfile.getHidClassDrawable(btClass),
393843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                            mInputPeripheralDescription);
394843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
395843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                case BluetoothClass.Device.Major.IMAGING:
396843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                    return new Pair<>(R.drawable.ic_bt_imaging, mImagingDescription);
397843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
398843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                default:
399843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                    // unrecognized device class; continue
400843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            }
401843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        } else {
402843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            Log.w(TAG, "btClass is null");
403843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
404843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
405843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        List<LocalBluetoothProfile> profiles = bluetoothDevice.getProfiles();
406843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        for (LocalBluetoothProfile profile : profiles) {
407843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            int resId = profile.getDrawableResource(btClass);
408843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            if (resId != 0) {
409843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                return new Pair<Integer, String>(resId, null);
410843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            }
411843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
412843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (btClass != null) {
413843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            if (btClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
414843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                return new Pair<Integer, String>(R.drawable.ic_bt_headset_hfp, mHeadsetDescription);
415843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            }
416843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            if (btClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
417843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                return new Pair<Integer, String>(R.drawable.ic_bt_headphones_a2dp,
418843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                        mHeadphoneDescription);
419843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            }
420843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
421843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        return new Pair<Integer, String>(R.drawable.ic_settings_bluetooth, mBluetoothDescription);
422843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
423843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
424843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    /**
425843fbc4b10a2c62cb3848836907630f0d56c647eroger xue     * Updates device render upon device attribute change.
426843fbc4b10a2c62cb3848836907630f0d56c647eroger xue     */
427843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    // TODO: This is a walk around for handling attribute callback. Since the callback doesn't
428843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    // contain the information about which device needs to be updated, we have to maintain a
429843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    // local reference to the device. Fix the code in CachedBluetoothDevice.Callback to return
430843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    // a reference of the device been updated.
431843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private class DeviceAttributeChangeCallback implements CachedBluetoothDevice.Callback {
432843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
433843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        private final ViewHolder mViewHolder;
434843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
435843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        DeviceAttributeChangeCallback(ViewHolder viewHolder) {
436843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            mViewHolder = viewHolder;
437843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
438843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
439843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        @Override
440843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        public void onDeviceAttributesChanged() {
441843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            notifyItemChanged(mViewHolder.getAdapterPosition());
442843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
443843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
444843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
445843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private class BluetoothClickListener implements OnClickListener {
446843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        private final ViewHolder mViewHolder;
447843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
448843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        BluetoothClickListener(ViewHolder viewHolder) {
449843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            mViewHolder = viewHolder;
450843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
451843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
452843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        @Override
453843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        public void onClick(View v) {
454843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            CachedBluetoothDevice device = getItem(mViewHolder.getAdapterPosition());
455843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            int bondState = device.getBondState();
456843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
457843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            if (device.isConnected()) {
458843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                // TODO: ask user for confirmation
459843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                device.disconnect();
460843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            } else if (bondState == BluetoothDevice.BOND_BONDED) {
461843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                device.connect(true);
462843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            } else if (bondState == BluetoothDevice.BOND_NONE) {
463843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                if (!device.startPairing()) {
464843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                    showError(device.getName(),
465843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                            R.string.bluetooth_pairing_error_message);
4661a97e52eb4864e79f1b643dbb3cf64ca9658f7cdLujiang Xue                    return;
467843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                }
4681a97e52eb4864e79f1b643dbb3cf64ca9658f7cdLujiang Xue                // allow MAP and PBAP since this is client side, permission should be handled on
4691a97e52eb4864e79f1b643dbb3cf64ca9658f7cdLujiang Xue                // server side. i.e. the phone side.
4701a97e52eb4864e79f1b643dbb3cf64ca9658f7cdLujiang Xue                device.setPhonebookPermissionChoice(CachedBluetoothDevice.ACCESS_ALLOWED);
4711a97e52eb4864e79f1b643dbb3cf64ca9658f7cdLujiang Xue                device.setMessagePermissionChoice(CachedBluetoothDevice.ACCESS_ALLOWED);
472843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            }
473843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
474843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
475843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
476843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private void showError(String name, int messageResId) {
477843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        String message = mContext.getString(messageResId, name);
478843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
479843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
480843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
481843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    /**
482843fbc4b10a2c62cb3848836907630f0d56c647eroger xue     * Provides an ordered bt device list periodically.
483843fbc4b10a2c62cb3848836907630f0d56c647eroger xue     */
484843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    // TODO: improve the way we sort BT devices. Ideally we should keep all devices in a TreeSet
485843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    // and as devices are added the correct order is maintained, that requires a consistent
486843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    // logic between equals and compareTo function, unfortunately it's not the case in
487843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    // CachedBluetoothDevice class. Fix that and improve the way we order devices.
488843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private class SortTask extends AsyncTask<Void, Void, ArrayList<CachedBluetoothDevice>> {
489843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
490843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        /**
491843fbc4b10a2c62cb3848836907630f0d56c647eroger xue         * Returns {code null} if no changed are made.
492843fbc4b10a2c62cb3848836907630f0d56c647eroger xue         */
493843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        @Override
494843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        protected ArrayList<CachedBluetoothDevice> doInBackground(Void... v) {
495843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            if (mAvailableDevicesSorted != null
496843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                    && mAvailableDevicesSorted.size() == mAvailableDevices.size()) {
497843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                return null;
498843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            }
499843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            ArrayList<CachedBluetoothDevice> devices =
500843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                    new ArrayList<CachedBluetoothDevice>(mAvailableDevices);
501843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            Collections.sort(devices);
502843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            return devices;
503843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
504843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
505843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        @Override
506843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        protected void onPostExecute(ArrayList<CachedBluetoothDevice> devices) {
507843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            // skip if no changes are made.
508843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            if (devices != null) {
509843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                mAvailableDevicesSorted = devices;
510843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                notifyDataSetChanged();
511843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            }
512843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            mHandler.postDelayed(new Runnable() {
513843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                public void run() {
514843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                    mSortTask = new SortTask();
515843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                    mSortTask.execute();
516843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                }
517843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            }, DELAY_MILLIS);
518843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
519843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
520843fbc4b10a2c62cb3848836907630f0d56c647eroger xue}
521