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