1 /* 2 * Copyright (C) 2017 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 static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH; 20 21 import android.app.settings.SettingsEnums; 22 import android.bluetooth.BluetoothAdapter; 23 import android.bluetooth.BluetoothDevice; 24 import android.content.Context; 25 import android.os.Bundle; 26 import android.util.Log; 27 import android.widget.Toast; 28 29 import androidx.annotation.VisibleForTesting; 30 31 import com.android.settings.R; 32 import com.android.settings.search.Indexable; 33 import com.android.settingslib.bluetooth.BluetoothDeviceFilter; 34 import com.android.settingslib.bluetooth.CachedBluetoothDevice; 35 import com.android.settingslib.widget.FooterPreference; 36 37 /** 38 * BluetoothPairingDetail is a page to scan bluetooth devices and pair them. 39 */ 40 public class BluetoothPairingDetail extends DeviceListPreferenceFragment implements 41 Indexable { 42 private static final String TAG = "BluetoothPairingDetail"; 43 44 @VisibleForTesting 45 static final String KEY_AVAIL_DEVICES = "available_devices"; 46 @VisibleForTesting 47 static final String KEY_FOOTER_PREF = "footer_preference"; 48 49 @VisibleForTesting 50 BluetoothProgressCategory mAvailableDevicesCategory; 51 @VisibleForTesting 52 FooterPreference mFooterPreference; 53 @VisibleForTesting 54 AlwaysDiscoverable mAlwaysDiscoverable; 55 56 private boolean mInitialScanStarted; 57 BluetoothPairingDetail()58 public BluetoothPairingDetail() { 59 super(DISALLOW_CONFIG_BLUETOOTH); 60 } 61 62 @Override onActivityCreated(Bundle savedInstanceState)63 public void onActivityCreated(Bundle savedInstanceState) { 64 super.onActivityCreated(savedInstanceState); 65 mInitialScanStarted = false; 66 mAlwaysDiscoverable = new AlwaysDiscoverable(getContext()); 67 } 68 69 @Override onStart()70 public void onStart() { 71 super.onStart(); 72 if (mLocalManager == null){ 73 Log.e(TAG, "Bluetooth is not supported on this device"); 74 return; 75 } 76 updateBluetooth(); 77 mAvailableDevicesCategory.setProgress(mBluetoothAdapter.isDiscovering()); 78 } 79 80 @Override onAttach(Context context)81 public void onAttach(Context context) { 82 super.onAttach(context); 83 use(BluetoothDeviceRenamePreferenceController.class).setFragment(this); 84 } 85 86 @VisibleForTesting updateBluetooth()87 void updateBluetooth() { 88 if (mBluetoothAdapter.isEnabled()) { 89 updateContent(mBluetoothAdapter.getState()); 90 } else { 91 // Turn on bluetooth if it is disabled 92 mBluetoothAdapter.enable(); 93 } 94 } 95 96 @Override onStop()97 public void onStop() { 98 super.onStop(); 99 if (mLocalManager == null){ 100 Log.e(TAG, "Bluetooth is not supported on this device"); 101 return; 102 } 103 // Make the device only visible to connected devices. 104 mAlwaysDiscoverable.stop(); 105 disableScanning(); 106 } 107 108 @Override initPreferencesFromPreferenceScreen()109 void initPreferencesFromPreferenceScreen() { 110 mAvailableDevicesCategory = (BluetoothProgressCategory) findPreference(KEY_AVAIL_DEVICES); 111 mFooterPreference = (FooterPreference) findPreference(KEY_FOOTER_PREF); 112 mFooterPreference.setSelectable(false); 113 } 114 115 @Override getMetricsCategory()116 public int getMetricsCategory() { 117 return SettingsEnums.BLUETOOTH_PAIRING; 118 } 119 120 @Override enableScanning()121 void enableScanning() { 122 // Clear all device states before first scan 123 if (!mInitialScanStarted) { 124 if (mAvailableDevicesCategory != null) { 125 removeAllDevices(); 126 } 127 mLocalManager.getCachedDeviceManager().clearNonBondedDevices(); 128 mInitialScanStarted = true; 129 } 130 super.enableScanning(); 131 } 132 133 @Override onDevicePreferenceClick(BluetoothDevicePreference btPreference)134 void onDevicePreferenceClick(BluetoothDevicePreference btPreference) { 135 disableScanning(); 136 super.onDevicePreferenceClick(btPreference); 137 } 138 139 @Override onScanningStateChanged(boolean started)140 public void onScanningStateChanged(boolean started) { 141 super.onScanningStateChanged(started); 142 started |= mScanEnabled; 143 mAvailableDevicesCategory.setProgress(started); 144 } 145 146 @VisibleForTesting updateContent(int bluetoothState)147 void updateContent(int bluetoothState) { 148 switch (bluetoothState) { 149 case BluetoothAdapter.STATE_ON: 150 mDevicePreferenceMap.clear(); 151 mBluetoothAdapter.enable(); 152 153 addDeviceCategory(mAvailableDevicesCategory, 154 R.string.bluetooth_preference_found_media_devices, 155 BluetoothDeviceFilter.ALL_FILTER, mInitialScanStarted); 156 updateFooterPreference(mFooterPreference); 157 mAlwaysDiscoverable.start(); 158 enableScanning(); 159 break; 160 161 case BluetoothAdapter.STATE_OFF: 162 finish(); 163 break; 164 } 165 } 166 167 @Override onBluetoothStateChanged(int bluetoothState)168 public void onBluetoothStateChanged(int bluetoothState) { 169 super.onBluetoothStateChanged(bluetoothState); 170 updateContent(bluetoothState); 171 if (bluetoothState == BluetoothAdapter.STATE_ON) { 172 showBluetoothTurnedOnToast(); 173 } 174 } 175 176 @Override onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState)177 public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) { 178 if (bondState == BluetoothDevice.BOND_BONDED) { 179 // If one device is connected(bonded), then close this fragment. 180 finish(); 181 return; 182 } 183 if (mSelectedDevice != null && cachedDevice != null) { 184 BluetoothDevice device = cachedDevice.getDevice(); 185 if (device != null && mSelectedDevice.equals(device) 186 && bondState == BluetoothDevice.BOND_NONE) { 187 // If currently selected device failed to bond, restart scanning 188 enableScanning(); 189 } 190 } 191 } 192 193 @Override onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state)194 public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { 195 if (mSelectedDevice != null) { 196 BluetoothDevice device = cachedDevice.getDevice(); 197 if (device != null && mSelectedDevice.equals(device) 198 && state == BluetoothAdapter.STATE_CONNECTED) { 199 finish(); 200 } 201 } 202 } 203 204 @Override getHelpResource()205 public int getHelpResource() { 206 return R.string.help_url_bluetooth; 207 } 208 209 @Override getLogTag()210 protected String getLogTag() { 211 return TAG; 212 } 213 214 @Override getPreferenceScreenResId()215 protected int getPreferenceScreenResId() { 216 return R.xml.bluetooth_pairing_detail; 217 } 218 219 @Override getDeviceListKey()220 public String getDeviceListKey() { 221 return KEY_AVAIL_DEVICES; 222 } 223 224 @VisibleForTesting showBluetoothTurnedOnToast()225 void showBluetoothTurnedOnToast() { 226 Toast.makeText(getContext(), R.string.connected_device_bluetooth_turned_on_toast, 227 Toast.LENGTH_SHORT).show(); 228 } 229 } 230