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