1 /* 2 * Copyright (C) 2023 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.bluetooth.BluetoothAdapter; 22 import android.bluetooth.BluetoothDevice; 23 import android.os.Bundle; 24 import android.util.Log; 25 import android.view.View; 26 import android.widget.Toast; 27 28 import androidx.annotation.NonNull; 29 import androidx.annotation.Nullable; 30 import androidx.annotation.VisibleForTesting; 31 32 import com.android.settings.R; 33 import com.android.settings.accessibility.AccessibilityStatsLogUtils; 34 import com.android.settings.overlay.FeatureFactory; 35 import com.android.settingslib.bluetooth.CachedBluetoothDevice; 36 import com.android.settingslib.bluetooth.HearingAidStatsLogUtils; 37 38 /** 39 * Abstract class for providing basic interaction for a list of Bluetooth devices in bluetooth 40 * device pairing detail page. 41 */ 42 public abstract class BluetoothDevicePairingDetailBase extends DeviceListPreferenceFragment { 43 44 protected boolean mInitialScanStarted; 45 @VisibleForTesting 46 protected BluetoothProgressCategory mAvailableDevicesCategory; 47 BluetoothDevicePairingDetailBase()48 public BluetoothDevicePairingDetailBase() { 49 super(DISALLOW_CONFIG_BLUETOOTH); 50 } 51 52 @Override initPreferencesFromPreferenceScreen()53 public void initPreferencesFromPreferenceScreen() { 54 mAvailableDevicesCategory = findPreference(getDeviceListKey()); 55 } 56 57 @Override onViewCreated(@onNull View view, @Nullable Bundle savedInstanceState)58 public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 59 mInitialScanStarted = false; 60 super.onViewCreated(view, savedInstanceState); 61 } 62 63 @Override onStart()64 public void onStart() { 65 super.onStart(); 66 if (mLocalManager == null) { 67 Log.e(getLogTag(), "Bluetooth is not supported on this device"); 68 return; 69 } 70 updateBluetooth(); 71 } 72 73 @Override onStop()74 public void onStop() { 75 super.onStop(); 76 if (mLocalManager == null) { 77 Log.e(getLogTag(), "Bluetooth is not supported on this device"); 78 return; 79 } 80 disableScanning(); 81 } 82 83 @Override onBluetoothStateChanged(int bluetoothState)84 public void onBluetoothStateChanged(int bluetoothState) { 85 super.onBluetoothStateChanged(bluetoothState); 86 updateContent(bluetoothState); 87 if (bluetoothState == BluetoothAdapter.STATE_ON) { 88 showBluetoothTurnedOnToast(); 89 } 90 } 91 92 @Override onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState)93 public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) { 94 if (bondState == BluetoothDevice.BOND_BONDED) { 95 // If one device is connected(bonded), then close this fragment. 96 finish(); 97 return; 98 } else if (bondState == BluetoothDevice.BOND_BONDING) { 99 // Set the bond entry where binding process starts for logging hearing aid device info 100 final int pageId = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider() 101 .getAttribution(getActivity()); 102 final int bondEntry = AccessibilityStatsLogUtils.convertToHearingAidInfoBondEntry( 103 pageId); 104 HearingAidStatsLogUtils.setBondEntryForDevice(bondEntry, cachedDevice); 105 } 106 if (mSelectedDevice != null && cachedDevice != null) { 107 BluetoothDevice device = cachedDevice.getDevice(); 108 if (device != null && mSelectedDevice.equals(device) 109 && bondState == BluetoothDevice.BOND_NONE) { 110 // If currently selected device failed to bond, restart scanning 111 enableScanning(); 112 } 113 } 114 } 115 116 @Override onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state, int bluetoothProfile)117 public void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state, 118 int bluetoothProfile) { 119 // This callback is used to handle the case that bonded device is connected in pairing list. 120 // 1. If user selected multiple bonded devices in pairing list, after connected 121 // finish this page. 122 // 2. If the bonded devices auto connected in paring list, after connected it will be 123 // removed from paring list. 124 if (cachedDevice != null && cachedDevice.isConnected()) { 125 final BluetoothDevice device = cachedDevice.getDevice(); 126 if (device != null && mSelectedList.contains(device)) { 127 finish(); 128 } else { 129 onDeviceDeleted(cachedDevice); 130 } 131 } 132 } 133 134 @Override enableScanning()135 public void enableScanning() { 136 // Clear all device states before first scan 137 if (!mInitialScanStarted) { 138 if (mAvailableDevicesCategory != null) { 139 removeAllDevices(); 140 } 141 mLocalManager.getCachedDeviceManager().clearNonBondedDevices(); 142 mInitialScanStarted = true; 143 } 144 super.enableScanning(); 145 } 146 147 @Override onDevicePreferenceClick(BluetoothDevicePreference btPreference)148 public void onDevicePreferenceClick(BluetoothDevicePreference btPreference) { 149 disableScanning(); 150 super.onDevicePreferenceClick(btPreference); 151 } 152 153 @VisibleForTesting updateBluetooth()154 void updateBluetooth() { 155 if (mBluetoothAdapter.isEnabled()) { 156 updateContent(mBluetoothAdapter.getState()); 157 } else { 158 // Turn on bluetooth if it is disabled 159 mBluetoothAdapter.enable(); 160 } 161 } 162 163 /** 164 * Enables the scanning when {@code bluetoothState} is on, or finish the page when 165 * {@code bluetoothState} is off. 166 * 167 * @param bluetoothState the current Bluetooth state, the possible values that will handle here: 168 * {@link android.bluetooth.BluetoothAdapter#STATE_OFF}, 169 * {@link android.bluetooth.BluetoothAdapter#STATE_ON}, 170 */ 171 @VisibleForTesting updateContent(int bluetoothState)172 public void updateContent(int bluetoothState) { 173 switch (bluetoothState) { 174 case BluetoothAdapter.STATE_ON: 175 mBluetoothAdapter.enable(); 176 enableScanning(); 177 break; 178 179 case BluetoothAdapter.STATE_OFF: 180 finish(); 181 break; 182 } 183 } 184 185 @VisibleForTesting showBluetoothTurnedOnToast()186 void showBluetoothTurnedOnToast() { 187 Toast.makeText(getContext(), R.string.connected_device_bluetooth_turned_on_toast, 188 Toast.LENGTH_SHORT).show(); 189 } 190 } 191