1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.settings.bluetooth;
18
19import android.bluetooth.BluetoothAdapter;
20import android.bluetooth.BluetoothDevice;
21import android.os.Bundle;
22import android.support.v7.preference.Preference;
23import android.support.v7.preference.PreferenceCategory;
24import android.support.v7.preference.PreferenceGroup;
25import android.util.Log;
26
27import com.android.settings.RestrictedSettingsFragment;
28import com.android.settingslib.bluetooth.BluetoothCallback;
29import com.android.settingslib.bluetooth.BluetoothDeviceFilter;
30import com.android.settingslib.bluetooth.CachedBluetoothDevice;
31import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
32import com.android.settingslib.bluetooth.LocalBluetoothManager;
33
34import java.util.Collection;
35import java.util.WeakHashMap;
36
37/**
38 * Parent class for settings fragments that contain a list of Bluetooth
39 * devices.
40 *
41 * @see BluetoothSettings
42 * @see DevicePickerFragment
43 */
44public abstract class DeviceListPreferenceFragment extends
45        RestrictedSettingsFragment implements BluetoothCallback {
46
47    private static final String TAG = "DeviceListPreferenceFragment";
48
49    private static final String KEY_BT_DEVICE_LIST = "bt_device_list";
50    private static final String KEY_BT_SCAN = "bt_scan";
51
52    private BluetoothDeviceFilter.Filter mFilter;
53
54    BluetoothDevice mSelectedDevice;
55
56    LocalBluetoothAdapter mLocalAdapter;
57    LocalBluetoothManager mLocalManager;
58
59    private PreferenceGroup mDeviceListGroup;
60
61    final WeakHashMap<CachedBluetoothDevice, BluetoothDevicePreference> mDevicePreferenceMap =
62            new WeakHashMap<CachedBluetoothDevice, BluetoothDevicePreference>();
63
64    DeviceListPreferenceFragment(String restrictedKey) {
65        super(restrictedKey);
66        mFilter = BluetoothDeviceFilter.ALL_FILTER;
67    }
68
69    final void setFilter(BluetoothDeviceFilter.Filter filter) {
70        mFilter = filter;
71    }
72
73    final void setFilter(int filterType) {
74        mFilter = BluetoothDeviceFilter.getFilter(filterType);
75    }
76
77    @Override
78    public void onCreate(Bundle savedInstanceState) {
79        super.onCreate(savedInstanceState);
80
81        mLocalManager = Utils.getLocalBtManager(getActivity());
82        if (mLocalManager == null) {
83            Log.e(TAG, "Bluetooth is not supported on this device");
84            return;
85        }
86        mLocalAdapter = mLocalManager.getBluetoothAdapter();
87
88        addPreferencesForActivity();
89
90        mDeviceListGroup = (PreferenceCategory) findPreference(KEY_BT_DEVICE_LIST);
91    }
92
93    void setDeviceListGroup(PreferenceGroup preferenceGroup) {
94        mDeviceListGroup = preferenceGroup;
95    }
96
97    /** Add preferences from the subclass. */
98    abstract void addPreferencesForActivity();
99
100    @Override
101    public void onResume() {
102        super.onResume();
103        if (mLocalManager == null || isUiRestricted()) return;
104
105        mLocalManager.setForegroundActivity(getActivity());
106        mLocalManager.getEventManager().registerCallback(this);
107
108        updateProgressUi(mLocalAdapter.isDiscovering());
109    }
110
111    @Override
112    public void onPause() {
113        super.onPause();
114        if (mLocalManager == null || isUiRestricted()) {
115            return;
116        }
117
118        removeAllDevices();
119        mLocalManager.setForegroundActivity(null);
120        mLocalManager.getEventManager().unregisterCallback(this);
121    }
122
123    void removeAllDevices() {
124        mLocalAdapter.stopScanning();
125        mDevicePreferenceMap.clear();
126        mDeviceListGroup.removeAll();
127    }
128
129    void addCachedDevices() {
130        Collection<CachedBluetoothDevice> cachedDevices =
131                mLocalManager.getCachedDeviceManager().getCachedDevicesCopy();
132        for (CachedBluetoothDevice cachedDevice : cachedDevices) {
133            onDeviceAdded(cachedDevice);
134        }
135    }
136
137    @Override
138    public boolean onPreferenceTreeClick(Preference preference) {
139        if (KEY_BT_SCAN.equals(preference.getKey())) {
140            mLocalAdapter.startScanning(true);
141            return true;
142        }
143
144        if (preference instanceof BluetoothDevicePreference) {
145            BluetoothDevicePreference btPreference = (BluetoothDevicePreference) preference;
146            CachedBluetoothDevice device = btPreference.getCachedDevice();
147            mSelectedDevice = device.getDevice();
148            onDevicePreferenceClick(btPreference);
149            return true;
150        }
151
152        return super.onPreferenceTreeClick(preference);
153    }
154
155    void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
156        btPreference.onClicked();
157    }
158
159    public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
160        if (mDevicePreferenceMap.get(cachedDevice) != null) {
161            return;
162        }
163
164        // Prevent updates while the list shows one of the state messages
165        if (mLocalAdapter.getBluetoothState() != BluetoothAdapter.STATE_ON) return;
166
167        if (mFilter.matches(cachedDevice.getDevice())) {
168            createDevicePreference(cachedDevice);
169        }
170    }
171
172    void createDevicePreference(CachedBluetoothDevice cachedDevice) {
173        if (mDeviceListGroup == null) {
174            Log.w(TAG, "Trying to create a device preference before the list group/category "
175                    + "exists!");
176            return;
177        }
178
179        String key = cachedDevice.getDevice().getAddress();
180        BluetoothDevicePreference preference = (BluetoothDevicePreference) getCachedPreference(key);
181
182        if (preference == null) {
183            preference = new BluetoothDevicePreference(getPrefContext(), cachedDevice);
184            preference.setKey(key);
185            mDeviceListGroup.addPreference(preference);
186        } else {
187            // Tell the preference it is being re-used in case there is new info in the
188            // cached device.
189            preference.rebind();
190        }
191
192        initDevicePreference(preference);
193        mDevicePreferenceMap.put(cachedDevice, preference);
194    }
195
196    /**
197     * Overridden in {@link BluetoothSettings} to add a listener.
198     * @param preference the newly added preference
199     */
200    void initDevicePreference(BluetoothDevicePreference preference) {
201        // Does nothing by default
202    }
203
204    public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {
205        BluetoothDevicePreference preference = mDevicePreferenceMap.remove(cachedDevice);
206        if (preference != null) {
207            mDeviceListGroup.removePreference(preference);
208        }
209    }
210
211    public void onScanningStateChanged(boolean started) {
212        updateProgressUi(started);
213    }
214
215    private void updateProgressUi(boolean start) {
216        if (mDeviceListGroup instanceof BluetoothProgressCategory) {
217            ((BluetoothProgressCategory) mDeviceListGroup).setProgress(start);
218        }
219    }
220
221    public void onBluetoothStateChanged(int bluetoothState) {
222        if (bluetoothState == BluetoothAdapter.STATE_OFF) {
223            updateProgressUi(false);
224        }
225    }
226
227    public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { }
228}
229