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.app.settings.SettingsEnums; 20 import android.bluetooth.BluetoothCsipSetCoordinator; 21 import android.bluetooth.BluetoothDevice; 22 import android.content.Context; 23 import android.media.AudioManager; 24 import android.util.Log; 25 import android.widget.SeekBar; 26 27 import androidx.annotation.Nullable; 28 import androidx.preference.Preference; 29 30 import com.android.settings.bluetooth.BluetoothDevicePreference; 31 import com.android.settings.bluetooth.BluetoothDeviceUpdater; 32 import com.android.settings.bluetooth.Utils; 33 import com.android.settings.connecteddevice.DevicePreferenceCallback; 34 import com.android.settingslib.bluetooth.BluetoothUtils; 35 import com.android.settingslib.bluetooth.CachedBluetoothDevice; 36 import com.android.settingslib.bluetooth.LocalBluetoothManager; 37 import com.android.settingslib.bluetooth.VolumeControlProfile; 38 39 public class AudioSharingDeviceVolumeControlUpdater extends BluetoothDeviceUpdater 40 implements Preference.OnPreferenceClickListener { 41 42 private static final String TAG = "AudioSharingDeviceVolumeControlUpdater"; 43 44 private static final String PREF_KEY = "audio_sharing_volume_control"; 45 46 @Nullable private final LocalBluetoothManager mBtManager; 47 @Nullable private final VolumeControlProfile mVolumeControl; 48 AudioSharingDeviceVolumeControlUpdater( Context context, DevicePreferenceCallback devicePreferenceCallback, int metricsCategory)49 public AudioSharingDeviceVolumeControlUpdater( 50 Context context, 51 DevicePreferenceCallback devicePreferenceCallback, 52 int metricsCategory) { 53 super(context, devicePreferenceCallback, metricsCategory); 54 mBtManager = Utils.getLocalBluetoothManager(context); 55 mVolumeControl = 56 mBtManager == null 57 ? null 58 : mBtManager.getProfileManager().getVolumeControlProfile(); 59 } 60 61 @Override isFilterMatched(CachedBluetoothDevice cachedDevice)62 public boolean isFilterMatched(CachedBluetoothDevice cachedDevice) { 63 boolean isFilterMatched = false; 64 if (isDeviceConnected(cachedDevice) && isDeviceInCachedDevicesList(cachedDevice)) { 65 // If device is LE audio device and in a sharing session on current sharing device, 66 // it would show in volume control group. 67 if (cachedDevice.isConnectedLeAudioDevice() 68 && AudioSharingUtils.isBroadcasting(mBtManager) 69 && BluetoothUtils.hasConnectedBroadcastSource(cachedDevice, mBtManager)) { 70 isFilterMatched = true; 71 } 72 } 73 Log.d( 74 TAG, 75 "isFilterMatched() device : " 76 + cachedDevice.getName() 77 + ", isFilterMatched : " 78 + isFilterMatched); 79 return isFilterMatched; 80 } 81 82 @Override onPreferenceClick(Preference preference)83 public boolean onPreferenceClick(Preference preference) { 84 return true; 85 } 86 87 @Override addPreference(CachedBluetoothDevice cachedDevice)88 protected void addPreference(CachedBluetoothDevice cachedDevice) { 89 if (cachedDevice == null) return; 90 final BluetoothDevice device = cachedDevice.getDevice(); 91 if (!mPreferenceMap.containsKey(device)) { 92 SeekBar.OnSeekBarChangeListener listener = 93 new SeekBar.OnSeekBarChangeListener() { 94 @Override 95 public void onProgressChanged( 96 SeekBar seekBar, int progress, boolean fromUser) {} 97 98 @Override 99 public void onStartTrackingTouch(SeekBar seekBar) {} 100 101 @Override 102 public void onStopTrackingTouch(SeekBar seekBar) { 103 int progress = seekBar.getProgress(); 104 int groupId = AudioSharingUtils.getGroupId(cachedDevice); 105 if (groupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID 106 && groupId 107 == AudioSharingUtils.getFallbackActiveGroupId( 108 mContext)) { 109 // Set media stream volume for primary buds, audio manager will 110 // update all buds volume in the audio sharing. 111 setAudioManagerStreamVolume(progress); 112 } else { 113 // Set buds volume for other buds. 114 setDeviceVolume(cachedDevice, progress); 115 } 116 } 117 }; 118 AudioSharingDeviceVolumePreference vPreference = 119 new AudioSharingDeviceVolumePreference(mPrefContext, cachedDevice); 120 vPreference.initialize(); 121 vPreference.setOnSeekBarChangeListener(listener); 122 vPreference.setKey(getPreferenceKey()); 123 vPreference.setIcon(com.android.settingslib.R.drawable.ic_bt_untethered_earbuds); 124 vPreference.setTitle(cachedDevice.getName()); 125 mPreferenceMap.put(device, vPreference); 126 mDevicePreferenceCallback.onDeviceAdded(vPreference); 127 } 128 } 129 130 @Override getPreferenceKey()131 protected String getPreferenceKey() { 132 return PREF_KEY; 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 @Override refreshPreference()154 public void refreshPreference() {} 155 setDeviceVolume(CachedBluetoothDevice cachedDevice, int progress)156 private void setDeviceVolume(CachedBluetoothDevice cachedDevice, int progress) { 157 if (mVolumeControl != null) { 158 mVolumeControl.setDeviceVolume( 159 cachedDevice.getDevice(), progress, /* isGroupOp= */ true); 160 mMetricsFeatureProvider.action( 161 mContext, 162 SettingsEnums.ACTION_AUDIO_SHARING_CHANGE_MEDIA_DEVICE_VOLUME, 163 /* isPrimary= */ false); 164 } 165 } 166 setAudioManagerStreamVolume(int progress)167 private void setAudioManagerStreamVolume(int progress) { 168 int seekbarRange = 169 AudioSharingDeviceVolumePreference.MAX_VOLUME 170 - AudioSharingDeviceVolumePreference.MIN_VOLUME; 171 try { 172 AudioManager audioManager = mContext.getSystemService(AudioManager.class); 173 int streamVolumeRange = 174 audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC) 175 - audioManager.getStreamMinVolume(AudioManager.STREAM_MUSIC); 176 int volume = Math.round((float) progress * streamVolumeRange / seekbarRange); 177 audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); 178 mMetricsFeatureProvider.action( 179 mContext, 180 SettingsEnums.ACTION_AUDIO_SHARING_CHANGE_MEDIA_DEVICE_VOLUME, 181 /* isPrimary= */ true); 182 } catch (RuntimeException e) { 183 Log.e(TAG, "Fail to setAudioManagerStreamVolumeForFallbackDevice, error = " + e); 184 } 185 } 186 } 187