• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.settings.bluetooth;
18 
19 import android.bluetooth.BluetoothAdapter;
20 import android.bluetooth.BluetoothDevice;
21 import android.os.Bundle;
22 import android.os.SystemProperties;
23 import android.text.BidiFormatter;
24 import android.util.Log;
25 
26 import androidx.annotation.VisibleForTesting;
27 import androidx.preference.Preference;
28 import androidx.preference.PreferenceCategory;
29 import androidx.preference.PreferenceGroup;
30 
31 import com.android.settings.R;
32 import com.android.settings.dashboard.RestrictedDashboardFragment;
33 import com.android.settingslib.bluetooth.BluetoothCallback;
34 import com.android.settingslib.bluetooth.BluetoothDeviceFilter;
35 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
36 import com.android.settingslib.bluetooth.LocalBluetoothManager;
37 
38 import java.util.ArrayList;
39 import java.util.Collection;
40 import java.util.HashMap;
41 import java.util.List;
42 
43 /**
44  * Parent class for settings fragments that contain a list of Bluetooth
45  * devices.
46  *
47  * @see DevicePickerFragment
48  */
49 // TODO: Refactor this fragment
50 public abstract class DeviceListPreferenceFragment extends
51         RestrictedDashboardFragment implements BluetoothCallback {
52 
53     private static final String TAG = "DeviceListPreferenceFragment";
54 
55     private static final String KEY_BT_SCAN = "bt_scan";
56 
57     // Copied from BluetoothDeviceNoNamePreferenceController.java
58     private static final String BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY =
59             "persist.bluetooth.showdeviceswithoutnames";
60 
61     private BluetoothDeviceFilter.Filter mFilter;
62 
63     @VisibleForTesting
64     boolean mScanEnabled;
65 
66     BluetoothDevice mSelectedDevice;
67 
68     BluetoothAdapter mBluetoothAdapter;
69     LocalBluetoothManager mLocalManager;
70 
71     @VisibleForTesting
72     PreferenceGroup mDeviceListGroup;
73 
74     final HashMap<CachedBluetoothDevice, BluetoothDevicePreference> mDevicePreferenceMap =
75             new HashMap<>();
76     final List<BluetoothDevice> mSelectedList = new ArrayList<>();
77 
78     boolean mShowDevicesWithoutNames;
79 
DeviceListPreferenceFragment(String restrictedKey)80     DeviceListPreferenceFragment(String restrictedKey) {
81         super(restrictedKey);
82         mFilter = BluetoothDeviceFilter.ALL_FILTER;
83     }
84 
setFilter(BluetoothDeviceFilter.Filter filter)85     final void setFilter(BluetoothDeviceFilter.Filter filter) {
86         mFilter = filter;
87     }
88 
setFilter(int filterType)89     final void setFilter(int filterType) {
90         mFilter = BluetoothDeviceFilter.getFilter(filterType);
91     }
92 
93     @Override
onCreate(Bundle savedInstanceState)94     public void onCreate(Bundle savedInstanceState) {
95         super.onCreate(savedInstanceState);
96 
97         mLocalManager = Utils.getLocalBtManager(getActivity());
98         if (mLocalManager == null) {
99             Log.e(TAG, "Bluetooth is not supported on this device");
100             return;
101         }
102         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
103         mShowDevicesWithoutNames = SystemProperties.getBoolean(
104                 BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY, false);
105 
106         initPreferencesFromPreferenceScreen();
107 
108         mDeviceListGroup = (PreferenceCategory) findPreference(getDeviceListKey());
109     }
110 
111     /** find and update preference that already existed in preference screen */
initPreferencesFromPreferenceScreen()112     abstract void initPreferencesFromPreferenceScreen();
113 
114     @Override
onStart()115     public void onStart() {
116         super.onStart();
117         if (mLocalManager == null || isUiRestricted()) return;
118 
119         mLocalManager.setForegroundActivity(getActivity());
120         mLocalManager.getEventManager().registerCallback(this);
121     }
122 
123     @Override
onStop()124     public void onStop() {
125         super.onStop();
126         if (mLocalManager == null || isUiRestricted()) {
127             return;
128         }
129 
130         removeAllDevices();
131         mLocalManager.setForegroundActivity(null);
132         mLocalManager.getEventManager().unregisterCallback(this);
133     }
134 
removeAllDevices()135     void removeAllDevices() {
136         mDevicePreferenceMap.clear();
137         mDeviceListGroup.removeAll();
138     }
139 
addCachedDevices()140     void addCachedDevices() {
141         Collection<CachedBluetoothDevice> cachedDevices =
142                 mLocalManager.getCachedDeviceManager().getCachedDevicesCopy();
143         for (CachedBluetoothDevice cachedDevice : cachedDevices) {
144             onDeviceAdded(cachedDevice);
145         }
146     }
147 
148     @Override
onPreferenceTreeClick(Preference preference)149     public boolean onPreferenceTreeClick(Preference preference) {
150         if (KEY_BT_SCAN.equals(preference.getKey())) {
151             startScanning();
152             return true;
153         }
154 
155         if (preference instanceof BluetoothDevicePreference) {
156             BluetoothDevicePreference btPreference = (BluetoothDevicePreference) preference;
157             CachedBluetoothDevice device = btPreference.getCachedDevice();
158             mSelectedDevice = device.getDevice();
159             mSelectedList.add(mSelectedDevice);
160             onDevicePreferenceClick(btPreference);
161             return true;
162         }
163 
164         return super.onPreferenceTreeClick(preference);
165     }
166 
onDevicePreferenceClick(BluetoothDevicePreference btPreference)167     void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
168         btPreference.onClicked();
169     }
170 
171     @Override
onDeviceAdded(CachedBluetoothDevice cachedDevice)172     public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
173         if (mDevicePreferenceMap.get(cachedDevice) != null) {
174             return;
175         }
176 
177         // Prevent updates while the list shows one of the state messages
178         if (mBluetoothAdapter.getState() != BluetoothAdapter.STATE_ON) return;
179 
180         if (mFilter.matches(cachedDevice.getDevice())) {
181             createDevicePreference(cachedDevice);
182         }
183     }
184 
createDevicePreference(CachedBluetoothDevice cachedDevice)185     void createDevicePreference(CachedBluetoothDevice cachedDevice) {
186         if (mDeviceListGroup == null) {
187             Log.w(TAG, "Trying to create a device preference before the list group/category "
188                     + "exists!");
189             return;
190         }
191 
192         String key = cachedDevice.getDevice().getAddress();
193         BluetoothDevicePreference preference = (BluetoothDevicePreference) getCachedPreference(key);
194 
195         if (preference == null) {
196             preference = new BluetoothDevicePreference(getPrefContext(), cachedDevice,
197                     mShowDevicesWithoutNames, BluetoothDevicePreference.SortType.TYPE_FIFO);
198             preference.setKey(key);
199             //Set hideSecondTarget is true if it's bonded device.
200             preference.hideSecondTarget(true);
201             mDeviceListGroup.addPreference(preference);
202         }
203 
204         initDevicePreference(preference);
205         mDevicePreferenceMap.put(cachedDevice, preference);
206     }
207 
initDevicePreference(BluetoothDevicePreference preference)208     protected void initDevicePreference(BluetoothDevicePreference preference) {
209         // Does nothing by default
210     }
211 
212     @VisibleForTesting
updateFooterPreference(Preference myDevicePreference)213     void updateFooterPreference(Preference myDevicePreference) {
214         final BidiFormatter bidiFormatter = BidiFormatter.getInstance();
215 
216         myDevicePreference.setTitle(getString(
217                 R.string.bluetooth_footer_mac_message,
218                 bidiFormatter.unicodeWrap(mBluetoothAdapter.getAddress())));
219     }
220 
221     @Override
onDeviceDeleted(CachedBluetoothDevice cachedDevice)222     public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {
223         BluetoothDevicePreference preference = mDevicePreferenceMap.remove(cachedDevice);
224         if (preference != null) {
225             mDeviceListGroup.removePreference(preference);
226         }
227     }
228 
229     @VisibleForTesting
enableScanning()230     void enableScanning() {
231         // BluetoothAdapter already handles repeated scan requests
232         startScanning();
233         mScanEnabled = true;
234     }
235 
236     @VisibleForTesting
disableScanning()237     void disableScanning() {
238         stopScanning();
239         mScanEnabled = false;
240     }
241 
242     @Override
onScanningStateChanged(boolean started)243     public void onScanningStateChanged(boolean started) {
244         if (!started && mScanEnabled) {
245             startScanning();
246         }
247     }
248 
249     /**
250      * Add bluetooth device preferences to {@code preferenceGroup} which satisfy the {@code filter}
251      *
252      * This method will also (1) set the title for {@code preferenceGroup} and (2) change the
253      * default preferenceGroup and filter
254      * @param preferenceGroup
255      * @param titleId
256      * @param filter
257      * @param addCachedDevices
258      */
addDeviceCategory(PreferenceGroup preferenceGroup, int titleId, BluetoothDeviceFilter.Filter filter, boolean addCachedDevices)259     public void addDeviceCategory(PreferenceGroup preferenceGroup, int titleId,
260             BluetoothDeviceFilter.Filter filter, boolean addCachedDevices) {
261         cacheRemoveAllPrefs(preferenceGroup);
262         preferenceGroup.setTitle(titleId);
263         mDeviceListGroup = preferenceGroup;
264         if (addCachedDevices) {
265             // Don't show bonded devices when screen turned back on
266             setFilter(BluetoothDeviceFilter.UNBONDED_DEVICE_FILTER);
267             addCachedDevices();
268         }
269         setFilter(filter);
270         preferenceGroup.setEnabled(true);
271         removeCachedPrefs(preferenceGroup);
272     }
273 
274     /**
275      * Return the key of the {@link PreferenceGroup} that contains the bluetooth devices
276      */
getDeviceListKey()277     public abstract String getDeviceListKey();
278 
shouldShowDevicesWithoutNames()279     public boolean shouldShowDevicesWithoutNames() {
280         return mShowDevicesWithoutNames;
281     }
282 
startScanning()283     void startScanning() {
284         if (!mBluetoothAdapter.isDiscovering()) {
285             mBluetoothAdapter.startDiscovery();
286         }
287     }
288 
stopScanning()289     void stopScanning() {
290         if (mBluetoothAdapter.isDiscovering()) {
291             mBluetoothAdapter.cancelDiscovery();
292         }
293     }
294 }
295