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.connecteddevice.audiosharing; 18 19 import android.bluetooth.BluetoothDevice; 20 import android.content.Context; 21 import android.util.Log; 22 23 import androidx.annotation.Nullable; 24 import androidx.annotation.VisibleForTesting; 25 import androidx.preference.Preference; 26 27 import com.android.settings.bluetooth.BluetoothDevicePreference; 28 import com.android.settings.bluetooth.BluetoothDeviceUpdater; 29 import com.android.settings.bluetooth.Utils; 30 import com.android.settings.connecteddevice.DevicePreferenceCallback; 31 import com.android.settingslib.bluetooth.BluetoothUtils; 32 import com.android.settingslib.bluetooth.CachedBluetoothDevice; 33 import com.android.settingslib.bluetooth.LocalBluetoothManager; 34 35 public class AudioSharingDeviceVolumeControlUpdater extends BluetoothDeviceUpdater 36 implements Preference.OnPreferenceClickListener { 37 38 private static final String TAG = "AudioSharingVolUpdater"; 39 40 @VisibleForTesting 41 static final String PREF_KEY_PREFIX = "audio_sharing_volume_control_"; 42 43 @Nullable private final LocalBluetoothManager mBtManager; 44 AudioSharingDeviceVolumeControlUpdater( Context context, DevicePreferenceCallback devicePreferenceCallback, int metricsCategory)45 public AudioSharingDeviceVolumeControlUpdater( 46 Context context, 47 DevicePreferenceCallback devicePreferenceCallback, 48 int metricsCategory) { 49 super(context, devicePreferenceCallback, metricsCategory); 50 mBtManager = Utils.getLocalBluetoothManager(context); 51 } 52 53 @Override isFilterMatched(CachedBluetoothDevice cachedDevice)54 public boolean isFilterMatched(CachedBluetoothDevice cachedDevice) { 55 boolean isFilterMatched = false; 56 if (isDeviceConnected(cachedDevice) && isDeviceInCachedDevicesList(cachedDevice)) { 57 // If device is LE audio device and in a sharing session on current sharing device, 58 // it would show in volume control group. 59 if ((cachedDevice.isConnectedLeAudioDevice() 60 || cachedDevice.hasConnectedLeAudioMemberDevice()) 61 && BluetoothUtils.isBroadcasting(mBtManager) 62 && BluetoothUtils.hasConnectedBroadcastSource(cachedDevice, mBtManager)) { 63 isFilterMatched = true; 64 } 65 } 66 Log.d( 67 TAG, 68 "isFilterMatched() device : " 69 + cachedDevice.getName() 70 + ", isFilterMatched : " 71 + isFilterMatched); 72 return isFilterMatched; 73 } 74 75 @Override onPreferenceClick(Preference preference)76 public boolean onPreferenceClick(Preference preference) { 77 return true; 78 } 79 80 @Override addPreference(CachedBluetoothDevice cachedDevice)81 protected void addPreference(CachedBluetoothDevice cachedDevice) { 82 if (cachedDevice == null) return; 83 final BluetoothDevice device = cachedDevice.getDevice(); 84 if (!mPreferenceMap.containsKey(device)) { 85 AudioSharingDeviceVolumePreference vPreference = 86 new AudioSharingDeviceVolumePreference(mPrefContext, cachedDevice); 87 vPreference.initialize(); 88 vPreference.setKey(getPreferenceKeyPrefix() + cachedDevice.hashCode()); 89 vPreference.setIcon(com.android.settingslib.R.drawable.ic_bt_untethered_earbuds); 90 vPreference.setTitle(cachedDevice.getName()); 91 mPreferenceMap.put(device, vPreference); 92 mDevicePreferenceCallback.onDeviceAdded(vPreference); 93 } 94 } 95 96 @Override refreshPreference()97 public void refreshPreference() { 98 mPreferenceMap.forEach((key, preference) -> { 99 if (isDeviceOfMapInCachedDevicesList(key)) { 100 ((AudioSharingDeviceVolumePreference) preference).onPreferenceAttributesChanged(); 101 } else { 102 // Remove staled preference. 103 Log.d(TAG, "removePreference key: " + key.getAnonymizedAddress()); 104 removePreference(key); 105 } 106 }); 107 } 108 109 @Override removePreference(BluetoothDevice device)110 protected void removePreference(BluetoothDevice device) { 111 if (mPreferenceMap.containsKey(device)) { 112 if (mPreferenceMap.get(device) instanceof AudioSharingDeviceVolumePreference pref) { 113 BluetoothDevice prefDevice = pref.getCachedDevice().getDevice(); 114 // For CSIP device, when it {@link CachedBluetoothDevice}#switchMemberDeviceContent, 115 // it will change its mDevice and lead to the hashcode change for this preference. 116 // This will cause unintended remove preference, see b/394765052 117 if (device.equals(prefDevice) || !mPreferenceMap.containsKey(prefDevice)) { 118 mDevicePreferenceCallback.onDeviceRemoved(pref); 119 } else { 120 Log.w(TAG, "Inconsistent key and preference when removePreference"); 121 } 122 mPreferenceMap.remove(device); 123 } else { 124 mDevicePreferenceCallback.onDeviceRemoved(mPreferenceMap.get(device)); 125 mPreferenceMap.remove(device); 126 } 127 } 128 } 129 130 @Override getPreferenceKeyPrefix()131 protected String getPreferenceKeyPrefix() { 132 return PREF_KEY_PREFIX; 133 } 134 135 @Override getLogTag()136 protected String getLogTag() { 137 return TAG; 138 } 139 140 @Override update(CachedBluetoothDevice cachedBluetoothDevice)141 protected void update(CachedBluetoothDevice cachedBluetoothDevice) { 142 super.update(cachedBluetoothDevice); 143 Log.d(TAG, "Map : " + mPreferenceMap); 144 } 145 146 @Override addPreference( CachedBluetoothDevice cachedDevice, @BluetoothDevicePreference.SortType int type)147 protected void addPreference( 148 CachedBluetoothDevice cachedDevice, @BluetoothDevicePreference.SortType int type) {} 149 150 @Override launchDeviceDetails(Preference preference)151 protected void launchDeviceDetails(Preference preference) {} 152 } 153