BluetoothDeviceListAdapter.java revision b9104e0e877d1f2334e1cbb1715c7fd320f62345
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.Intent;
24843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.content.res.Resources;
25843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.os.AsyncTask;
26843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.os.Handler;
27843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.os.Looper;
28843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.util.Log;
29843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.util.Pair;
30843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.support.car.ui.PagedListView;
31843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.support.v7.widget.RecyclerView;
32843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.view.LayoutInflater;
33843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.view.View;
34843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.view.View.OnClickListener;
35843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.view.ViewGroup;
36843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.widget.ImageButton;
37843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.widget.ImageView;
38843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.widget.TextView;
39843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport android.widget.Toast;
40843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
41843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport com.android.car.settings.R;
42843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport com.android.settingslib.bluetooth.BluetoothCallback;
43843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport com.android.settingslib.bluetooth.BluetoothDeviceFilter;
44843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport com.android.settingslib.bluetooth.CachedBluetoothDevice;
45843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
46843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport com.android.settingslib.bluetooth.LocalBluetoothAdapter;
47843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport com.android.settingslib.bluetooth.LocalBluetoothManager;
48843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport com.android.settingslib.bluetooth.LocalBluetoothProfile;
49843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport com.android.settingslib.bluetooth.HidProfile;
50843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
51843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport java.util.ArrayList;
52843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport java.util.Collection;
53843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport java.util.Collections;
54843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport java.util.HashSet;
55843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport java.util.List;
56843fbc4b10a2c62cb3848836907630f0d56c647eroger xueimport java.util.Set;
57843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
58843fbc4b10a2c62cb3848836907630f0d56c647eroger xue/**
59843fbc4b10a2c62cb3848836907630f0d56c647eroger xue * Renders {@link android.bluetooth.BluetoothDevice} to a view to be displayed as a row in a list.
60843fbc4b10a2c62cb3848836907630f0d56c647eroger xue */
61843fbc4b10a2c62cb3848836907630f0d56c647eroger xuepublic class BluetoothDeviceListAdapter
62843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        extends RecyclerView.Adapter<BluetoothDeviceListAdapter.ViewHolder>
63843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        implements PagedListView.ItemCap, BluetoothCallback {
64843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private static final String TAG = "BluetoothDeviceListAdapter";
65843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private static final int DEVICE_ROW_TYPE = 1;
66843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private static final int BONDED_DEVICE_HEADER_TYPE = 2;
67843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private static final int AVAILABLE_DEVICE_HEADER_TYPE = 3;
68843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private static final int NUM_OF_HEADERS = 2;
69843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public static final int DELAY_MILLIS = 1000;
70843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
71843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private final Handler mHandler = new Handler(Looper.getMainLooper());
72843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private final HashSet<CachedBluetoothDevice> mBondedDevices = new HashSet<>();
73843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private final HashSet<CachedBluetoothDevice> mAvailableDevices = new HashSet<>();
74843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private final LocalBluetoothAdapter mLocalAdapter;
75843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private final LocalBluetoothManager mLocalManager;
76843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private final CachedBluetoothDeviceManager mDeviceManager;
77843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private final Context mContext;
78843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
79843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    /* Talk-back descriptions for various BT icons */
80843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public final String mComputerDescription;
81843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public final String mInputPeripheralDescription;
82843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public final String mHeadsetDescription;
83843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public final String mPhoneDescription;
84843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public final String mImagingDescription;
85843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public final String mHeadphoneDescription;
86843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public final String mBluetoothDescription;
87843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
88843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private SortTask mSortTask = new SortTask();
89843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
90843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private ArrayList<CachedBluetoothDevice> mBondedDevicesSorted = new ArrayList<>();
91843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private ArrayList<CachedBluetoothDevice> mAvailableDevicesSorted = new ArrayList<>();
92843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
93843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    class ViewHolder extends RecyclerView.ViewHolder {
94843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        private final ImageView mIcon;
95b9104e0e877d1f2334e1cbb1715c7fd320f62345roger xue        private final TextView mTitle;
96843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        private final TextView mDesc;
97843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        private final ImageButton mActionButton;
98843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        private final DeviceAttributeChangeCallback mCallback =
99843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                new DeviceAttributeChangeCallback(this);
100843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
101843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        public ViewHolder(View view) {
102843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            super(view);
103b9104e0e877d1f2334e1cbb1715c7fd320f62345roger xue            mTitle = (TextView) view.findViewById(R.id.title);
104843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            mDesc = (TextView) view.findViewById(R.id.desc);
105843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            mIcon = (ImageView) view.findViewById(R.id.icon);
106843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            mActionButton = (ImageButton) view.findViewById(R.id.action);
107843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            view.setOnClickListener(new BluetoothClickListener(this));
108843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
109843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
110843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
111843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public BluetoothDeviceListAdapter(
112843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            Context context, LocalBluetoothManager localBluetoothManager) {
113843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mContext = context;
114843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mLocalManager = localBluetoothManager;
115843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mLocalAdapter = mLocalManager.getBluetoothAdapter();
116843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mDeviceManager = mLocalManager.getCachedDeviceManager();
117843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
118843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        Resources r = context.getResources();
119843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mComputerDescription = r.getString(R.string.bluetooth_talkback_computer);
120843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mInputPeripheralDescription = r.getString(
121843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                R.string.bluetooth_talkback_input_peripheral);
122843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mHeadsetDescription = r.getString(R.string.bluetooth_talkback_headset);
123843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mPhoneDescription = r.getString(R.string.bluetooth_talkback_phone);
124843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mImagingDescription = r.getString(R.string.bluetooth_talkback_imaging);
125843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mHeadphoneDescription = r.getString(R.string.bluetooth_talkback_headphone);
126843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mBluetoothDescription = r.getString(R.string.bluetooth_talkback_bluetooth);
127843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
128843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
129843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public void start() {
130843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mLocalManager.getEventManager().registerCallback(this);
131843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (mLocalAdapter.isEnabled()) {
132843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            mLocalAdapter.startScanning(true);
133843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            addBondDevices();
134843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            addCachedDevices();
135843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
136843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mSortTask.execute();
137843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
138843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
139843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public void stop() {
140843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mLocalAdapter.stopScanning();
141843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mDeviceManager.clearNonBondedDevices();
142843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mLocalManager.getEventManager().unregisterCallback(this);
143843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mBondedDevices.clear();
144843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mAvailableDevices.clear();
145843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mSortTask.cancel(true);
146843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
147843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
148843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    @Override
149843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public BluetoothDeviceListAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
150843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            int viewType) {
151843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        View v;
152843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
153843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        switch (viewType) {
154843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            case BONDED_DEVICE_HEADER_TYPE:
155843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                v = layoutInflater.inflate(R.layout.in_list_header, parent, false);
156843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                ((TextView) v).setText(R.string.bluetooth_preference_paired_devices);
157843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                break;
158843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            case AVAILABLE_DEVICE_HEADER_TYPE:
159843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                v = layoutInflater.inflate(R.layout.in_list_header, parent, false);
160843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                ((TextView) v).setText(R.string.bluetooth_preference_found_devices);
161843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                break;
162843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            default:
163843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                v = layoutInflater.inflate(R.layout.list_item, parent, false);
164843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
165843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        return new ViewHolder(v);
166843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
167843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
168843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    @Override
169843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public int getItemCount() {
170843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        return mAvailableDevicesSorted.size() + NUM_OF_HEADERS + mBondedDevicesSorted.size();
171843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
172843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
173843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    @Override
174843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public void setMaxItems(int maxItems) {
175843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        // no limit in this list.
176843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
177843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
178843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    @Override
179843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public void onBindViewHolder(ViewHolder holder, int position) {
180843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        final CachedBluetoothDevice bluetoothDevice = getItem(position);
181843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (bluetoothDevice == null) {
182843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            // this row is for in-list headers
183843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            return;
184843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
185843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (holder.getOldPosition() != RecyclerView.NO_POSITION) {
186843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            getItem(holder.getOldPosition()).unregisterCallback(holder.mCallback);
187843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
188843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        bluetoothDevice.registerCallback(holder.mCallback);
189b9104e0e877d1f2334e1cbb1715c7fd320f62345roger xue        holder.mTitle.setText(bluetoothDevice.getName());
190843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        Pair<Integer, String> pair = getBtClassDrawableWithDescription(bluetoothDevice);
191843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        holder.mIcon.setImageResource(pair.first);
192843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        int summaryResourceId = bluetoothDevice.getConnectionSummary();
193843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (summaryResourceId != 0) {
194843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            holder.mDesc.setText(summaryResourceId);
195843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            holder.mDesc.setVisibility(View.VISIBLE);
196843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        } else {
197843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            holder.mDesc.setVisibility(View.GONE);
198843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
199843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (BluetoothDeviceFilter.BONDED_DEVICE_FILTER.matches(bluetoothDevice.getDevice())) {
200843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            holder.mActionButton.setVisibility(View.VISIBLE);
201843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            holder.mActionButton.setOnClickListener(v -> {
202843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                    Intent intent = new Intent(mContext, BluetoothDetailActivity.class);
203843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                    intent.putExtra(
204843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                            BluetoothDetailActivity.BT_DEVICE_KEY, bluetoothDevice.getDevice());
205843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                    mContext.startActivity(intent);
206843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                });
207843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        } else {
208843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            holder.mActionButton.setVisibility(View.GONE);
209843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
210843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
211843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
212843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    @Override
213843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public int getItemViewType(int position) {
214843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        // the first row is the header for the bonded device list;
215843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (position == 0) {
216843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            return BONDED_DEVICE_HEADER_TYPE;
217843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
218843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        // after the end of the bonded device list is the header of the available device list.
219843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (position == mBondedDevicesSorted.size() + 1) {
220843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            return AVAILABLE_DEVICE_HEADER_TYPE;
221843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
222843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        return DEVICE_ROW_TYPE;
223843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
224843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
225843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private CachedBluetoothDevice getItem(int position) {
226843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (position > 0 && position <= mBondedDevicesSorted.size()) {
227843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            // off set the header row
228843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            return mBondedDevicesSorted.get(position - 1);
229843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
230843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (position > mBondedDevicesSorted.size() + 1
231843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                && position <= mBondedDevicesSorted.size() + 1 + mAvailableDevicesSorted.size()) {
232843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            // off set two header row and the size of bonded device list.
233843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            return mAvailableDevicesSorted.get(
234843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                    position - NUM_OF_HEADERS - mBondedDevicesSorted.size());
235843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
236843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        // otherwise it's a in list header
237843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        return null;
238843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
239843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
240843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    // callback functions
241843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    @Override
242843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
243843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (addDevice(cachedDevice)) {
244843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            ArrayList<CachedBluetoothDevice> devices = new ArrayList<>(mBondedDevices);
245843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            Collections.sort(devices);
246843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            mBondedDevicesSorted = devices;
247843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            notifyDataSetChanged();
248843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
249843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
250843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
251843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    @Override
252843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {
253843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        onDeviceDeleted(cachedDevice, true /* refresh */);
254843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
255843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
256843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    @Override
257843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public void onBluetoothStateChanged(int bluetoothState) {
258843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        switch (bluetoothState) {
259843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            case BluetoothAdapter.STATE_OFF:
260843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                mBondedDevices.clear();
261843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                mBondedDevicesSorted.clear();
262843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                mAvailableDevices.clear();
263843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                mAvailableDevicesSorted.clear();
264843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                notifyDataSetChanged();
265843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                break;
266843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            case BluetoothAdapter.STATE_ON:
267843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                mLocalAdapter.startScanning(true);
268843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                addBondDevices();
269843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                addCachedDevices();
270843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                break;
271843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            default:
272843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
273843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
274843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
275843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    @Override
276843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public void onScanningStateChanged(boolean started) {
277843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        // don't care
278843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
279843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
280843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    @Override
281843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
282843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        onDeviceDeleted(cachedDevice, false /* refresh */);
283843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        onDeviceAdded(cachedDevice);
284843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
285843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
286843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    /**
287843fbc4b10a2c62cb3848836907630f0d56c647eroger xue     * Call back for the first connection or the last connection to ANY device/profile. Not
288843fbc4b10a2c62cb3848836907630f0d56c647eroger xue     * suitable for monitor per device level connection.
289843fbc4b10a2c62cb3848836907630f0d56c647eroger xue     */
290843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    @Override
291843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
292843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        onDeviceDeleted(cachedDevice, false);
293843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        onDeviceAdded(cachedDevice);
294843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
295843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
296843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private void onDeviceDeleted(CachedBluetoothDevice cachedDevice, boolean refresh) {
297843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        // the device might changed bonding state, so need to remove from both sets.
298843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (mBondedDevices.remove(cachedDevice)) {
299843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            mBondedDevicesSorted.remove(cachedDevice);
300843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
301843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        mAvailableDevices.remove(cachedDevice);
302843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (refresh) {
303843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            notifyDataSetChanged();
304843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
305843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
306843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
307843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private void addDevices(Collection<CachedBluetoothDevice> cachedDevices) {
308843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        boolean needSort = false;
309843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        for (CachedBluetoothDevice device : cachedDevices) {
310843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            if (addDevice(device)) {
311843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                needSort = true;
312843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            }
313843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
314843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (needSort) {
315843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            ArrayList<CachedBluetoothDevice> devices =
316843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                    new ArrayList<CachedBluetoothDevice>(mBondedDevices);
317843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            Collections.sort(devices);
318843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            mBondedDevicesSorted = devices;
319843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            notifyDataSetChanged();
320843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
321843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
322843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
323843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    /**
324843fbc4b10a2c62cb3848836907630f0d56c647eroger xue     * @return {@code true} if list changed and needed sort again.
325843fbc4b10a2c62cb3848836907630f0d56c647eroger xue     */
326843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private boolean addDevice(CachedBluetoothDevice cachedDevice) {
327843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        boolean needSort = false;
328843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (BluetoothDeviceFilter.BONDED_DEVICE_FILTER.matches(cachedDevice.getDevice())) {
329843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            if (mBondedDevices.add(cachedDevice)) {
330843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                needSort = true;
331843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            }
332843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
333843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (BluetoothDeviceFilter.UNBONDED_DEVICE_FILTER.matches(cachedDevice.getDevice())) {
334843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            // refresh is done at SortTask.
335843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            mAvailableDevices.add(cachedDevice);
336843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
337843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        return needSort;
338843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
339843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
340843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private void addBondDevices() {
341843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        Set<BluetoothDevice> bondedDevices = mLocalAdapter.getBondedDevices();
342843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (bondedDevices == null) {
343843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            return;
344843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
345843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        ArrayList<CachedBluetoothDevice> cachedBluetoothDevices = new ArrayList<>();
346843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        for (BluetoothDevice device : bondedDevices) {
347843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
348843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            if (cachedDevice == null) {
349843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                cachedDevice = mDeviceManager.addDevice(
350843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                        mLocalAdapter, mLocalManager.getProfileManager(), device);
351843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            }
352843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            cachedBluetoothDevices.add(cachedDevice);
353843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
354843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        addDevices(cachedBluetoothDevices);
355843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
356843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
357843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private void addCachedDevices() {
358843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        addDevices(mDeviceManager.getCachedDevicesCopy());
359843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
360843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
361843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private Pair<Integer, String> getBtClassDrawableWithDescription(
362843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            CachedBluetoothDevice bluetoothDevice) {
363843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        BluetoothClass btClass = bluetoothDevice.getBtClass();
364843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (btClass != null) {
365843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            switch (btClass.getMajorDeviceClass()) {
366843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                case BluetoothClass.Device.Major.COMPUTER:
367843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                    return new Pair<>(R.drawable.ic_bt_laptop, mComputerDescription);
368843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
369843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                case BluetoothClass.Device.Major.PHONE:
370843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                    return new Pair<>(R.drawable.ic_bt_cellphone, mPhoneDescription);
371843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
372843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                case BluetoothClass.Device.Major.PERIPHERAL:
373843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                    return new Pair<>(HidProfile.getHidClassDrawable(btClass),
374843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                            mInputPeripheralDescription);
375843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
376843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                case BluetoothClass.Device.Major.IMAGING:
377843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                    return new Pair<>(R.drawable.ic_bt_imaging, mImagingDescription);
378843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
379843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                default:
380843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                    // unrecognized device class; continue
381843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            }
382843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        } else {
383843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            Log.w(TAG, "btClass is null");
384843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
385843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
386843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        List<LocalBluetoothProfile> profiles = bluetoothDevice.getProfiles();
387843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        for (LocalBluetoothProfile profile : profiles) {
388843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            int resId = profile.getDrawableResource(btClass);
389843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            if (resId != 0) {
390843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                return new Pair<Integer, String>(resId, null);
391843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            }
392843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
393843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        if (btClass != null) {
394843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            if (btClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
395843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                return new Pair<Integer, String>(R.drawable.ic_bt_headset_hfp, mHeadsetDescription);
396843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            }
397843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            if (btClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
398843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                return new Pair<Integer, String>(R.drawable.ic_bt_headphones_a2dp,
399843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                        mHeadphoneDescription);
400843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            }
401843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
402843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        return new Pair<Integer, String>(R.drawable.ic_settings_bluetooth, mBluetoothDescription);
403843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
404843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
405843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    /**
406843fbc4b10a2c62cb3848836907630f0d56c647eroger xue     * Updates device render upon device attribute change.
407843fbc4b10a2c62cb3848836907630f0d56c647eroger xue     */
408843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    // TODO: This is a walk around for handling attribute callback. Since the callback doesn't
409843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    // contain the information about which device needs to be updated, we have to maintain a
410843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    // local reference to the device. Fix the code in CachedBluetoothDevice.Callback to return
411843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    // a reference of the device been updated.
412843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private class DeviceAttributeChangeCallback implements CachedBluetoothDevice.Callback {
413843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
414843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        private final ViewHolder mViewHolder;
415843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
416843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        DeviceAttributeChangeCallback(ViewHolder viewHolder) {
417843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            mViewHolder = viewHolder;
418843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
419843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
420843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        @Override
421843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        public void onDeviceAttributesChanged() {
422843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            notifyItemChanged(mViewHolder.getAdapterPosition());
423843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
424843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
425843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
426843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private class BluetoothClickListener implements OnClickListener {
427843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        private final ViewHolder mViewHolder;
428843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
429843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        BluetoothClickListener(ViewHolder viewHolder) {
430843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            mViewHolder = viewHolder;
431843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
432843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
433843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        @Override
434843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        public void onClick(View v) {
435843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            CachedBluetoothDevice device = getItem(mViewHolder.getAdapterPosition());
436843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            int bondState = device.getBondState();
437843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
438843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            if (device.isConnected()) {
439843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                // TODO: ask user for confirmation
440843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                device.disconnect();
441843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            } else if (bondState == BluetoothDevice.BOND_BONDED) {
442843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                device.connect(true);
443843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            } else if (bondState == BluetoothDevice.BOND_NONE) {
444843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                if (!device.startPairing()) {
445843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                    showError(device.getName(),
446843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                            R.string.bluetooth_pairing_error_message);
447843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                }
448843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            }
449843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
450843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
451843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
452843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private void showError(String name, int messageResId) {
453843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        String message = mContext.getString(messageResId, name);
454843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
455843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
456843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
457843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    /**
458843fbc4b10a2c62cb3848836907630f0d56c647eroger xue     * Provides an ordered bt device list periodically.
459843fbc4b10a2c62cb3848836907630f0d56c647eroger xue     */
460843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    // TODO: improve the way we sort BT devices. Ideally we should keep all devices in a TreeSet
461843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    // and as devices are added the correct order is maintained, that requires a consistent
462843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    // logic between equals and compareTo function, unfortunately it's not the case in
463843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    // CachedBluetoothDevice class. Fix that and improve the way we order devices.
464843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    private class SortTask extends AsyncTask<Void, Void, ArrayList<CachedBluetoothDevice>> {
465843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
466843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        /**
467843fbc4b10a2c62cb3848836907630f0d56c647eroger xue         * Returns {code null} if no changed are made.
468843fbc4b10a2c62cb3848836907630f0d56c647eroger xue         */
469843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        @Override
470843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        protected ArrayList<CachedBluetoothDevice> doInBackground(Void... v) {
471843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            if (mAvailableDevicesSorted != null
472843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                    && mAvailableDevicesSorted.size() == mAvailableDevices.size()) {
473843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                return null;
474843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            }
475843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            ArrayList<CachedBluetoothDevice> devices =
476843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                    new ArrayList<CachedBluetoothDevice>(mAvailableDevices);
477843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            Collections.sort(devices);
478843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            return devices;
479843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
480843fbc4b10a2c62cb3848836907630f0d56c647eroger xue
481843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        @Override
482843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        protected void onPostExecute(ArrayList<CachedBluetoothDevice> devices) {
483843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            // skip if no changes are made.
484843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            if (devices != null) {
485843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                mAvailableDevicesSorted = devices;
486843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                notifyDataSetChanged();
487843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            }
488843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            mHandler.postDelayed(new Runnable() {
489843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                public void run() {
490843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                    mSortTask = new SortTask();
491843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                    mSortTask.execute();
492843fbc4b10a2c62cb3848836907630f0d56c647eroger xue                }
493843fbc4b10a2c62cb3848836907630f0d56c647eroger xue            }, DELAY_MILLIS);
494843fbc4b10a2c62cb3848836907630f0d56c647eroger xue        }
495843fbc4b10a2c62cb3848836907630f0d56c647eroger xue    }
496843fbc4b10a2c62cb3848836907630f0d56c647eroger xue}
497