1 /* 2 * Copyright (C) 2024 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 static com.android.settings.connecteddevice.audiosharing.AudioSharingUtils.SETTINGS_KEY_FALLBACK_DEVICE_GROUP_ID; 20 import static com.android.settings.core.BasePreferenceController.AVAILABLE; 21 import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE; 22 23 import static com.google.common.truth.Truth.assertThat; 24 25 import static org.mockito.ArgumentMatchers.any; 26 import static org.mockito.ArgumentMatchers.anyInt; 27 import static org.mockito.ArgumentMatchers.eq; 28 import static org.mockito.Mockito.never; 29 import static org.mockito.Mockito.verify; 30 import static org.mockito.Mockito.when; 31 import static org.robolectric.Shadows.shadowOf; 32 33 import android.app.settings.SettingsEnums; 34 import android.bluetooth.BluetoothAdapter; 35 import android.bluetooth.BluetoothCsipSetCoordinator; 36 import android.bluetooth.BluetoothDevice; 37 import android.bluetooth.BluetoothLeBroadcastAssistant; 38 import android.bluetooth.BluetoothLeBroadcastMetadata; 39 import android.bluetooth.BluetoothLeBroadcastReceiveState; 40 import android.bluetooth.BluetoothProfile; 41 import android.bluetooth.BluetoothStatusCodes; 42 import android.content.ContentResolver; 43 import android.content.Context; 44 import android.database.ContentObserver; 45 import android.os.Looper; 46 import android.platform.test.flag.junit.SetFlagsRule; 47 import android.provider.Settings; 48 import android.view.View; 49 import android.widget.CheckedTextView; 50 51 import androidx.appcompat.app.AlertDialog; 52 import androidx.fragment.app.Fragment; 53 import androidx.fragment.app.FragmentActivity; 54 import androidx.lifecycle.LifecycleOwner; 55 import androidx.preference.Preference; 56 import androidx.preference.PreferenceScreen; 57 import androidx.test.core.app.ApplicationProvider; 58 59 import com.android.settings.R; 60 import com.android.settings.bluetooth.Utils; 61 import com.android.settings.testutils.FakeFeatureFactory; 62 import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; 63 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; 64 import com.android.settings.testutils.shadow.ShadowBluetoothUtils; 65 import com.android.settings.testutils.shadow.ShadowThreadUtils; 66 import com.android.settingslib.bluetooth.BluetoothEventManager; 67 import com.android.settingslib.bluetooth.CachedBluetoothDevice; 68 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; 69 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast; 70 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; 71 import com.android.settingslib.bluetooth.LocalBluetoothManager; 72 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; 73 import com.android.settingslib.bluetooth.VolumeControlProfile; 74 import com.android.settingslib.core.lifecycle.Lifecycle; 75 import com.android.settingslib.flags.Flags; 76 77 import com.google.common.collect.ImmutableList; 78 import com.google.common.collect.ImmutableSet; 79 import com.google.common.collect.Iterables; 80 81 import org.junit.After; 82 import org.junit.Before; 83 import org.junit.Rule; 84 import org.junit.Test; 85 import org.junit.runner.RunWith; 86 import org.mockito.Mock; 87 import org.mockito.Spy; 88 import org.mockito.junit.MockitoJUnit; 89 import org.mockito.junit.MockitoRule; 90 import org.robolectric.RobolectricTestRunner; 91 import org.robolectric.annotation.Config; 92 import org.robolectric.shadow.api.Shadow; 93 import org.robolectric.shadows.androidx.fragment.FragmentController; 94 95 import java.util.ArrayList; 96 import java.util.List; 97 98 @RunWith(RobolectricTestRunner.class) 99 @Config( 100 shadows = { 101 ShadowBluetoothAdapter.class, 102 ShadowBluetoothUtils.class, 103 ShadowThreadUtils.class, 104 ShadowAlertDialogCompat.class, 105 }) 106 public class AudioSharingCallAudioPreferenceControllerTest { 107 private static final String PREF_KEY = "calls_and_alarms"; 108 private static final String TEST_DEVICE_NAME1 = "test1"; 109 private static final String TEST_DEVICE_NAME2 = "test2"; 110 private static final int TEST_DEVICE_GROUP_ID1 = 1; 111 private static final int TEST_DEVICE_GROUP_ID2 = 2; 112 113 private static final String TEST_SETTINGS_KEY = 114 "bluetooth_le_broadcast_fallback_active_group_id"; 115 116 @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); 117 @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); 118 119 @Spy Context mContext = ApplicationProvider.getApplicationContext(); 120 @Mock private PreferenceScreen mScreen; 121 @Mock private LocalBluetoothManager mLocalBtManager; 122 @Mock private BluetoothEventManager mBtEventManager; 123 @Mock private LocalBluetoothProfileManager mBtProfileManager; 124 @Mock private CachedBluetoothDeviceManager mCacheManager; 125 @Mock private LocalBluetoothLeBroadcast mBroadcast; 126 @Mock private LocalBluetoothLeBroadcastAssistant mAssistant; 127 @Mock private VolumeControlProfile mVolumeControl; 128 @Mock private BluetoothDevice mDevice1; 129 @Mock private BluetoothDevice mDevice2; 130 @Mock private BluetoothDevice mDevice3; 131 @Mock private CachedBluetoothDevice mCachedDevice1; 132 @Mock private CachedBluetoothDevice mCachedDevice2; 133 @Mock private CachedBluetoothDevice mCachedDevice3; 134 @Mock private BluetoothLeBroadcastReceiveState mState; 135 @Mock private BluetoothLeBroadcastMetadata mSource; 136 @Mock private ContentResolver mContentResolver; 137 private AudioSharingCallAudioPreferenceController mController; 138 @Spy private ContentObserver mContentObserver; 139 private ShadowBluetoothAdapter mShadowBluetoothAdapter; 140 private FakeFeatureFactory mFeatureFactory; 141 private Lifecycle mLifecycle; 142 private LifecycleOwner mLifecycleOwner; 143 private Preference mPreference; 144 private Fragment mParentFragment; 145 146 @Before setUp()147 public void setUp() { 148 ShadowAlertDialogCompat.reset(); 149 mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); 150 mShadowBluetoothAdapter.setEnabled(true); 151 mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported( 152 BluetoothStatusCodes.FEATURE_SUPPORTED); 153 mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( 154 BluetoothStatusCodes.FEATURE_SUPPORTED); 155 mLifecycleOwner = () -> mLifecycle; 156 mLifecycle = new Lifecycle(mLifecycleOwner); 157 mParentFragment = new Fragment(); 158 FragmentController.setupFragment( 159 mParentFragment, 160 FragmentActivity.class, 161 0 /* containerViewId */, 162 null /* bundle */); 163 ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager; 164 LocalBluetoothManager btManager = Utils.getLocalBtManager(mContext); 165 mFeatureFactory = FakeFeatureFactory.setupForTest(); 166 when(btManager.getEventManager()).thenReturn(mBtEventManager); 167 when(btManager.getProfileManager()).thenReturn(mBtProfileManager); 168 when(btManager.getCachedDeviceManager()).thenReturn(mCacheManager); 169 when(mBtProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast); 170 when(mBtProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant); 171 when(mBtProfileManager.getVolumeControlProfile()).thenReturn(mVolumeControl); 172 when(mBroadcast.isProfileReady()).thenReturn(true); 173 when(mAssistant.isProfileReady()).thenReturn(true); 174 when(mVolumeControl.isProfileReady()).thenReturn(true); 175 List<Long> bisSyncState = new ArrayList<>(); 176 bisSyncState.add(1L); 177 when(mState.getBisSyncState()).thenReturn(bisSyncState); 178 when(mContext.getContentResolver()).thenReturn(mContentResolver); 179 mController = new AudioSharingCallAudioPreferenceController(mContext); 180 mController.init(null); 181 mContentObserver = mController.getSettingsObserver(); 182 mPreference = new Preference(mContext); 183 when(mScreen.findPreference(PREF_KEY)).thenReturn(mPreference); 184 } 185 186 @After tearDown()187 public void tearDown() { 188 ShadowAlertDialogCompat.reset(); 189 ShadowThreadUtils.reset(); 190 ShadowBluetoothUtils.reset(); 191 } 192 193 @Test onStart_flagOff_doNothing()194 public void onStart_flagOff_doNothing() { 195 mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); 196 mController.onStart(mLifecycleOwner); 197 verify(mBtEventManager, never()).registerCallback(mController); 198 verify(mContentResolver, never()) 199 .registerContentObserver( 200 Settings.Secure.getUriFor(SETTINGS_KEY_FALLBACK_DEVICE_GROUP_ID), 201 false, 202 mContentObserver); 203 verify(mAssistant, never()) 204 .registerServiceCallBack(any(), any(BluetoothLeBroadcastAssistant.Callback.class)); 205 } 206 207 @Test onStart_flagOn_registerCallback()208 public void onStart_flagOn_registerCallback() { 209 mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); 210 mController.onStart(mLifecycleOwner); 211 verify(mBtEventManager).registerCallback(mController); 212 verify(mContentResolver) 213 .registerContentObserver( 214 Settings.Secure.getUriFor(SETTINGS_KEY_FALLBACK_DEVICE_GROUP_ID), 215 false, 216 mContentObserver); 217 verify(mAssistant) 218 .registerServiceCallBack(any(), any(BluetoothLeBroadcastAssistant.Callback.class)); 219 } 220 221 @Test onStop_flagOff_doNothing()222 public void onStop_flagOff_doNothing() { 223 mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); 224 mController.setCallbacksRegistered(true); 225 mController.onStop(mLifecycleOwner); 226 verify(mBtEventManager, never()).unregisterCallback(mController); 227 verify(mContentResolver, never()).unregisterContentObserver(mContentObserver); 228 verify(mAssistant, never()) 229 .unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class)); 230 } 231 232 @Test onStop_flagOn_notRegistered_doNothing()233 public void onStop_flagOn_notRegistered_doNothing() { 234 mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); 235 mController.setCallbacksRegistered(false); 236 mController.onStop(mLifecycleOwner); 237 verify(mBtEventManager, never()).unregisterCallback(mController); 238 verify(mContentResolver, never()).unregisterContentObserver(mContentObserver); 239 verify(mAssistant, never()) 240 .unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class)); 241 } 242 243 @Test onStop_flagOn_registered_unregisterCallback()244 public void onStop_flagOn_registered_unregisterCallback() { 245 mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); 246 mController.setCallbacksRegistered(true); 247 mController.onStop(mLifecycleOwner); 248 verify(mBtEventManager).unregisterCallback(mController); 249 verify(mContentResolver).unregisterContentObserver(mContentObserver); 250 verify(mAssistant) 251 .unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class)); 252 } 253 254 @Test getAvailabilityStatus_flagOn()255 public void getAvailabilityStatus_flagOn() { 256 mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); 257 assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); 258 } 259 260 @Test getAvailabilityStatus_flagOff()261 public void getAvailabilityStatus_flagOff() { 262 mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); 263 assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE); 264 } 265 266 @Test updateVisibility_flagOff_invisible()267 public void updateVisibility_flagOff_invisible() { 268 mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); 269 when(mBroadcast.isEnabled(any())).thenReturn(true); 270 mController.displayPreference(mScreen); 271 mController.updateVisibility(); 272 shadowOf(Looper.getMainLooper()).idle(); 273 assertThat(mPreference.isVisible()).isFalse(); 274 } 275 276 @Test updateVisibility_broadcastOffBluetoothOff_invisible()277 public void updateVisibility_broadcastOffBluetoothOff_invisible() { 278 mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); 279 when(mBroadcast.isEnabled(any())).thenReturn(false); 280 mShadowBluetoothAdapter.setEnabled(false); 281 mController.displayPreference(mScreen); 282 mController.updateVisibility(); 283 shadowOf(Looper.getMainLooper()).idle(); 284 assertThat(mPreference.isVisible()).isFalse(); 285 } 286 287 @Test updateVisibility_broadcastOnBluetoothOff_invisible()288 public void updateVisibility_broadcastOnBluetoothOff_invisible() { 289 mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); 290 when(mBroadcast.isEnabled(any())).thenReturn(true); 291 mShadowBluetoothAdapter.setEnabled(false); 292 mController.displayPreference(mScreen); 293 mController.updateVisibility(); 294 shadowOf(Looper.getMainLooper()).idle(); 295 assertThat(mPreference.isVisible()).isFalse(); 296 } 297 298 @Test updateVisibility_broadcastOffBluetoothOn_invisible()299 public void updateVisibility_broadcastOffBluetoothOn_invisible() { 300 mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); 301 when(mBroadcast.isEnabled(any())).thenReturn(false); 302 mController.displayPreference(mScreen); 303 mController.updateVisibility(); 304 shadowOf(Looper.getMainLooper()).idle(); 305 assertThat(mPreference.isVisible()).isFalse(); 306 } 307 308 @Test updateVisibility_broadcastOnBluetoothOn_visible()309 public void updateVisibility_broadcastOnBluetoothOn_visible() { 310 mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); 311 when(mBroadcast.isEnabled(any())).thenReturn(true); 312 mController.displayPreference(mScreen); 313 mController.updateVisibility(); 314 shadowOf(Looper.getMainLooper()).idle(); 315 assertThat(mPreference.isVisible()).isTrue(); 316 } 317 318 @Test onProfileConnectionStateChanged_noDeviceInSharing_updateSummary()319 public void onProfileConnectionStateChanged_noDeviceInSharing_updateSummary() { 320 Settings.Secure.putInt(mContentResolver, TEST_SETTINGS_KEY, TEST_DEVICE_GROUP_ID1); 321 when(mBroadcast.isEnabled(any())).thenReturn(true); 322 when(mAssistant.getDevicesMatchingConnectionStates( 323 new int[] {BluetoothProfile.STATE_CONNECTED})) 324 .thenReturn(ImmutableList.of()); 325 mController.displayPreference(mScreen); 326 mPreference.setSummary("test"); 327 mController.onProfileConnectionStateChanged( 328 mCachedDevice1, 329 BluetoothAdapter.STATE_DISCONNECTED, 330 BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT); 331 shadowOf(Looper.getMainLooper()).idle(); 332 assertThat(mPreference.getSummary().toString()).isEmpty(); 333 } 334 335 @Test onFallbackDeviceChanged_updateSummary()336 public void onFallbackDeviceChanged_updateSummary() { 337 Settings.Secure.putInt(mContentResolver, TEST_SETTINGS_KEY, TEST_DEVICE_GROUP_ID1); 338 when(mCachedDevice1.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID1); 339 when(mCachedDevice1.getDevice()).thenReturn(mDevice1); 340 when(mCachedDevice1.getName()).thenReturn(TEST_DEVICE_NAME1); 341 when(mCacheManager.findDevice(mDevice1)).thenReturn(mCachedDevice1); 342 when(mBroadcast.isEnabled(any())).thenReturn(true); 343 when(mAssistant.getDevicesMatchingConnectionStates( 344 new int[] {BluetoothProfile.STATE_CONNECTED})) 345 .thenReturn(ImmutableList.of(mDevice1)); 346 when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mState)); 347 mController.displayPreference(mScreen); 348 mContentObserver.onChange(true); 349 shadowOf(Looper.getMainLooper()).idle(); 350 assertThat(mPreference.getSummary().toString()) 351 .isEqualTo( 352 mContext.getString( 353 R.string.audio_sharing_call_audio_description, TEST_DEVICE_NAME1)); 354 } 355 356 @Test displayPreference_showCorrectSummary()357 public void displayPreference_showCorrectSummary() { 358 Settings.Secure.putInt(mContentResolver, TEST_SETTINGS_KEY, TEST_DEVICE_GROUP_ID1); 359 when(mCachedDevice1.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID1); 360 when(mCachedDevice1.getDevice()).thenReturn(mDevice1); 361 when(mCachedDevice2.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID1); 362 when(mCachedDevice2.getDevice()).thenReturn(mDevice2); 363 when(mCachedDevice1.getMemberDevice()).thenReturn(ImmutableSet.of(mCachedDevice2)); 364 when(mCachedDevice1.getName()).thenReturn(TEST_DEVICE_NAME1); 365 when(mCachedDevice3.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID2); 366 when(mCachedDevice3.getDevice()).thenReturn(mDevice3); 367 when(mCachedDevice3.getName()).thenReturn(TEST_DEVICE_NAME2); 368 when(mCacheManager.findDevice(mDevice1)).thenReturn(mCachedDevice1); 369 when(mCacheManager.findDevice(mDevice2)).thenReturn(mCachedDevice2); 370 when(mCacheManager.findDevice(mDevice3)).thenReturn(mCachedDevice3); 371 when(mBroadcast.isEnabled(any())).thenReturn(true); 372 when(mAssistant.getDevicesMatchingConnectionStates( 373 new int[] {BluetoothProfile.STATE_CONNECTED})) 374 .thenReturn(ImmutableList.of(mDevice1, mDevice2, mDevice3)); 375 when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mState)); 376 mController.displayPreference(mScreen); 377 shadowOf(Looper.getMainLooper()).idle(); 378 assertThat(mPreference.getSummary().toString()) 379 .isEqualTo( 380 mContext.getString( 381 R.string.audio_sharing_call_audio_description, TEST_DEVICE_NAME1)); 382 } 383 384 @Test displayPreference_noFallbackDeviceInSharing_showEmptySummary()385 public void displayPreference_noFallbackDeviceInSharing_showEmptySummary() { 386 Settings.Secure.putInt(mContentResolver, TEST_SETTINGS_KEY, TEST_DEVICE_GROUP_ID2); 387 when(mCachedDevice1.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID1); 388 when(mCachedDevice1.getDevice()).thenReturn(mDevice1); 389 when(mCachedDevice1.getName()).thenReturn(TEST_DEVICE_NAME1); 390 when(mCacheManager.findDevice(mDevice1)).thenReturn(mCachedDevice1); 391 when(mBroadcast.isEnabled(any())).thenReturn(true); 392 when(mAssistant.getDevicesMatchingConnectionStates( 393 new int[] {BluetoothProfile.STATE_CONNECTED})) 394 .thenReturn(ImmutableList.of(mDevice1)); 395 when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mState)); 396 mController.displayPreference(mScreen); 397 shadowOf(Looper.getMainLooper()).idle(); 398 assertThat(mPreference.getSummary().toString()).isEmpty(); 399 } 400 401 @Test displayPreference_noFallbackDevice_showEmptySummary()402 public void displayPreference_noFallbackDevice_showEmptySummary() { 403 Settings.Secure.putInt( 404 mContentResolver, TEST_SETTINGS_KEY, BluetoothCsipSetCoordinator.GROUP_ID_INVALID); 405 when(mBroadcast.isEnabled(any())).thenReturn(true); 406 when(mAssistant.getDevicesMatchingConnectionStates( 407 new int[] {BluetoothProfile.STATE_CONNECTED})) 408 .thenReturn(ImmutableList.of()); 409 mController.displayPreference(mScreen); 410 shadowOf(Looper.getMainLooper()).idle(); 411 assertThat(mPreference.getSummary().toString()).isEmpty(); 412 } 413 414 @Test displayPreference_clickToShowCorrectDialog()415 public void displayPreference_clickToShowCorrectDialog() { 416 AlertDialog latestAlertDialog = ShadowAlertDialogCompat.getLatestAlertDialog(); 417 if (latestAlertDialog != null) { 418 latestAlertDialog.dismiss(); 419 ShadowAlertDialogCompat.reset(); 420 } 421 Settings.Secure.putInt(mContentResolver, TEST_SETTINGS_KEY, TEST_DEVICE_GROUP_ID1); 422 when(mCachedDevice1.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID1); 423 when(mCachedDevice1.getName()).thenReturn(TEST_DEVICE_NAME1); 424 when(mCachedDevice1.getDevice()).thenReturn(mDevice1); 425 when(mCachedDevice2.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID2); 426 when(mCachedDevice2.getName()).thenReturn(TEST_DEVICE_NAME2); 427 when(mCachedDevice2.getDevice()).thenReturn(mDevice2); 428 when(mCacheManager.findDevice(mDevice1)).thenReturn(mCachedDevice1); 429 when(mCacheManager.findDevice(mDevice2)).thenReturn(mCachedDevice2); 430 mShadowBluetoothAdapter.setMostRecentlyConnectedDevices(List.of(mDevice1, mDevice2)); 431 when(mBroadcast.isEnabled(any())).thenReturn(true); 432 when(mAssistant.getDevicesMatchingConnectionStates( 433 new int[] {BluetoothProfile.STATE_CONNECTED})) 434 .thenReturn(ImmutableList.of(mDevice1, mDevice2)); 435 when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mState)); 436 mController.init(mParentFragment); 437 mController.displayPreference(mScreen); 438 shadowOf(Looper.getMainLooper()).idle(); 439 mPreference.performClick(); 440 shadowOf(Looper.getMainLooper()).idle(); 441 AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); 442 assertThat(dialog.isShowing()).isTrue(); 443 assertThat(dialog.getListView().getCount()).isEqualTo(2); 444 ArrayList<View> outViews = new ArrayList<>(); 445 dialog.getListView() 446 .findViewsWithText(outViews, TEST_DEVICE_NAME1, View.FIND_VIEWS_WITH_TEXT); 447 assertThat(outViews.size()).isEqualTo(1); 448 View view = Iterables.getOnlyElement(outViews); 449 assertThat(view instanceof CheckedTextView).isTrue(); 450 assertThat(((CheckedTextView) view).isChecked()).isTrue(); 451 verify(mFeatureFactory.metricsFeatureProvider) 452 .visible( 453 /* context= */ eq(null), 454 /* source= */ anyInt(), 455 eq(SettingsEnums.DIALOG_AUDIO_SHARING_CALL_AUDIO), 456 /* latency= */ anyInt()); 457 } 458 459 @Test logCallAudioDeviceChange_changeCallAudioToEarlierConnectedDevice()460 public void logCallAudioDeviceChange_changeCallAudioToEarlierConnectedDevice() { 461 when(mCachedDevice1.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID1); 462 when(mCachedDevice1.getDevice()).thenReturn(mDevice1); 463 when(mCachedDevice2.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID2); 464 when(mCachedDevice2.getDevice()).thenReturn(mDevice2); 465 when(mCacheManager.findDevice(mDevice1)).thenReturn(mCachedDevice1); 466 when(mCacheManager.findDevice(mDevice2)).thenReturn(mCachedDevice2); 467 mShadowBluetoothAdapter.setMostRecentlyConnectedDevices(List.of(mDevice1, mDevice2)); 468 mController.logCallAudioDeviceChange(TEST_DEVICE_GROUP_ID1, mCachedDevice2); 469 verify(mFeatureFactory.metricsFeatureProvider) 470 .action( 471 mContext, 472 SettingsEnums.ACTION_AUDIO_SHARING_CHANGE_CALL_AUDIO, 473 AudioSharingCallAudioPreferenceController.ChangeCallAudioType 474 .CONNECTED_EARLIER 475 .ordinal()); 476 } 477 478 @Test logCallAudioDeviceChange_changeCallAudioToLaterConnectedDevice()479 public void logCallAudioDeviceChange_changeCallAudioToLaterConnectedDevice() { 480 when(mCachedDevice1.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID1); 481 when(mCachedDevice1.getDevice()).thenReturn(mDevice1); 482 when(mCachedDevice2.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID2); 483 when(mCachedDevice2.getDevice()).thenReturn(mDevice2); 484 when(mCacheManager.findDevice(mDevice1)).thenReturn(mCachedDevice1); 485 when(mCacheManager.findDevice(mDevice2)).thenReturn(mCachedDevice2); 486 mShadowBluetoothAdapter.setMostRecentlyConnectedDevices(List.of(mDevice1, mDevice2)); 487 mController.logCallAudioDeviceChange(TEST_DEVICE_GROUP_ID2, mCachedDevice1); 488 verify(mFeatureFactory.metricsFeatureProvider) 489 .action( 490 mContext, 491 SettingsEnums.ACTION_AUDIO_SHARING_CHANGE_CALL_AUDIO, 492 AudioSharingCallAudioPreferenceController.ChangeCallAudioType 493 .CONNECTED_LATER 494 .ordinal()); 495 } 496 497 @Test logCallAudioDeviceChange_deviceNotFoundInRecentList_unknownChangeType()498 public void logCallAudioDeviceChange_deviceNotFoundInRecentList_unknownChangeType() { 499 when(mCachedDevice1.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID1); 500 when(mCachedDevice1.getDevice()).thenReturn(mDevice1); 501 when(mCachedDevice2.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID2); 502 when(mCachedDevice2.getDevice()).thenReturn(mDevice2); 503 when(mCacheManager.findDevice(mDevice1)).thenReturn(mCachedDevice1); 504 when(mCacheManager.findDevice(mDevice2)).thenReturn(mCachedDevice2); 505 mShadowBluetoothAdapter.setMostRecentlyConnectedDevices(List.of(mDevice1)); 506 mController.logCallAudioDeviceChange(TEST_DEVICE_GROUP_ID1, mCachedDevice2); 507 verify(mFeatureFactory.metricsFeatureProvider) 508 .action( 509 mContext, 510 SettingsEnums.ACTION_AUDIO_SHARING_CHANGE_CALL_AUDIO, 511 AudioSharingCallAudioPreferenceController.ChangeCallAudioType.UNKNOWN 512 .ordinal()); 513 } 514 515 @Test testBluetoothLeBroadcastAssistantCallbacks_updateSummary()516 public void testBluetoothLeBroadcastAssistantCallbacks_updateSummary() { 517 when(mCachedDevice1.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID1); 518 when(mCachedDevice1.getDevice()).thenReturn(mDevice1); 519 when(mCachedDevice1.getName()).thenReturn(TEST_DEVICE_NAME1); 520 when(mCacheManager.findDevice(mDevice1)).thenReturn(mCachedDevice1); 521 Settings.Secure.putInt( 522 mContentResolver, TEST_SETTINGS_KEY, BluetoothCsipSetCoordinator.GROUP_ID_INVALID); 523 when(mBroadcast.isEnabled(any())).thenReturn(true); 524 when(mAssistant.getDevicesMatchingConnectionStates( 525 new int[] {BluetoothProfile.STATE_CONNECTED})) 526 .thenReturn(ImmutableList.of()); 527 mController.displayPreference(mScreen); 528 shadowOf(Looper.getMainLooper()).idle(); 529 assertThat(mPreference.getSummary().toString()).isEmpty(); 530 531 // onReceiveStateChanged will update summary 532 Settings.Secure.putInt(mContentResolver, TEST_SETTINGS_KEY, TEST_DEVICE_GROUP_ID1); 533 when(mAssistant.getDevicesMatchingConnectionStates( 534 new int[] {BluetoothProfile.STATE_CONNECTED})) 535 .thenReturn(ImmutableList.of(mDevice1)); 536 when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mState)); 537 mController.mBroadcastAssistantCallback.onReceiveStateChanged( 538 mDevice1, /* sourceId= */ 1, mState); 539 shadowOf(Looper.getMainLooper()).idle(); 540 assertThat(mPreference.getSummary().toString()) 541 .isEqualTo( 542 mContext.getString( 543 R.string.audio_sharing_call_audio_description, TEST_DEVICE_NAME1)); 544 } 545 546 @Test testBluetoothLeBroadcastAssistantCallbacks_doNothing()547 public void testBluetoothLeBroadcastAssistantCallbacks_doNothing() { 548 when(mCachedDevice1.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID1); 549 when(mCachedDevice1.getDevice()).thenReturn(mDevice1); 550 when(mCachedDevice1.getName()).thenReturn(TEST_DEVICE_NAME1); 551 when(mCacheManager.findDevice(mDevice1)).thenReturn(mCachedDevice1); 552 Settings.Secure.putInt( 553 mContentResolver, TEST_SETTINGS_KEY, BluetoothCsipSetCoordinator.GROUP_ID_INVALID); 554 when(mBroadcast.isEnabled(any())).thenReturn(true); 555 when(mAssistant.getDevicesMatchingConnectionStates( 556 new int[] {BluetoothProfile.STATE_CONNECTED})) 557 .thenReturn(ImmutableList.of()); 558 mController.displayPreference(mScreen); 559 shadowOf(Looper.getMainLooper()).idle(); 560 assertThat(mPreference.getSummary().toString()).isEmpty(); 561 562 Settings.Secure.putInt(mContentResolver, TEST_SETTINGS_KEY, TEST_DEVICE_GROUP_ID1); 563 when(mAssistant.getDevicesMatchingConnectionStates( 564 new int[] {BluetoothProfile.STATE_CONNECTED})) 565 .thenReturn(ImmutableList.of(mDevice1)); 566 when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mState)); 567 mController.mBroadcastAssistantCallback.onSearchStarted(/* reason= */ 1); 568 mController.mBroadcastAssistantCallback.onSearchStartFailed(/* reason= */ 1); 569 mController.mBroadcastAssistantCallback.onSearchStopped(/* reason= */ 1); 570 mController.mBroadcastAssistantCallback.onSearchStopFailed(/* reason= */ 1); 571 mController.mBroadcastAssistantCallback.onSourceAdded( 572 mDevice1, /* sourceId= */ 1, /* reason= */ 1); 573 mController.mBroadcastAssistantCallback.onSourceAddFailed( 574 mDevice1, mSource, /* reason= */ 1); 575 mController.mBroadcastAssistantCallback.onSourceRemoved( 576 mDevice1, /* sourceId= */ 1, /* reason= */ 1); 577 mController.mBroadcastAssistantCallback.onSourceRemoveFailed( 578 mDevice1, /* sourceId= */ 1, /* reason= */ 1); 579 mController.mBroadcastAssistantCallback.onSourceModified( 580 mDevice1, /* sourceId= */ 1, /* reason= */ 1); 581 mController.mBroadcastAssistantCallback.onSourceModifyFailed( 582 mDevice1, /* sourceId= */ 1, /* reason= */ 1); 583 mController.mBroadcastAssistantCallback.onSourceFound(mSource); 584 mController.mBroadcastAssistantCallback.onSourceLost(/* broadcastId= */ 1); 585 shadowOf(Looper.getMainLooper()).idle(); 586 587 // Above callbacks won't update summary. 588 assertThat(mPreference.getSummary().toString()).isEmpty(); 589 } 590 } 591