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 com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.EXTRA_BT_DEVICE_TO_AUTO_ADD_SOURCE; 20 import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.EXTRA_PAIR_AND_JOIN_SHARING; 21 22 import static com.google.common.truth.Truth.assertThat; 23 24 import static org.mockito.ArgumentMatchers.any; 25 import static org.mockito.ArgumentMatchers.anyInt; 26 import static org.mockito.ArgumentMatchers.eq; 27 import static org.mockito.Mockito.doNothing; 28 import static org.mockito.Mockito.doReturn; 29 import static org.mockito.Mockito.mock; 30 import static org.mockito.Mockito.never; 31 import static org.mockito.Mockito.spy; 32 import static org.mockito.Mockito.verify; 33 import static org.mockito.Mockito.when; 34 import static org.robolectric.Shadows.shadowOf; 35 36 import android.app.Activity; 37 import android.bluetooth.BluetoothAdapter; 38 import android.bluetooth.BluetoothDevice; 39 import android.bluetooth.BluetoothProfile; 40 import android.bluetooth.BluetoothStatusCodes; 41 import android.content.Context; 42 import android.content.Intent; 43 import android.content.res.Resources; 44 import android.graphics.drawable.Drawable; 45 import android.os.Bundle; 46 import android.os.Looper; 47 import android.platform.test.annotations.DisableFlags; 48 import android.platform.test.annotations.EnableFlags; 49 import android.platform.test.flag.junit.SetFlagsRule; 50 import android.util.Pair; 51 52 import androidx.annotation.NonNull; 53 import androidx.annotation.Nullable; 54 import androidx.fragment.app.DialogFragment; 55 import androidx.fragment.app.FragmentActivity; 56 import androidx.fragment.app.FragmentManager; 57 import androidx.fragment.app.FragmentTransaction; 58 import androidx.lifecycle.Lifecycle; 59 import androidx.test.core.app.ApplicationProvider; 60 61 import com.android.settings.R; 62 import com.android.settings.SettingsActivity; 63 import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; 64 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; 65 import com.android.settings.testutils.shadow.ShadowFragment; 66 import com.android.settingslib.bluetooth.CachedBluetoothDevice; 67 import com.android.settingslib.bluetooth.LocalBluetoothManager; 68 import com.android.settingslib.flags.Flags; 69 70 import org.junit.Before; 71 import org.junit.Rule; 72 import org.junit.Test; 73 import org.junit.runner.RunWith; 74 import org.mockito.Answers; 75 import org.mockito.ArgumentCaptor; 76 import org.mockito.Mock; 77 import org.mockito.junit.MockitoJUnit; 78 import org.mockito.junit.MockitoRule; 79 import org.robolectric.Robolectric; 80 import org.robolectric.RobolectricTestRunner; 81 import org.robolectric.annotation.Config; 82 import org.robolectric.annotation.Implementation; 83 import org.robolectric.annotation.Implements; 84 import org.robolectric.annotation.RealObject; 85 import org.robolectric.annotation.Resetter; 86 import org.robolectric.shadow.api.Shadow; 87 88 import java.util.HashMap; 89 import java.util.Map; 90 import java.util.concurrent.Executor; 91 92 /** Tests for {@link BluetoothDevicePairingDetailBase}. */ 93 @RunWith(RobolectricTestRunner.class) 94 @Config(shadows = { 95 ShadowBluetoothAdapter.class, 96 ShadowAlertDialogCompat.class, 97 ShadowFragment.class, 98 }) 99 public class BluetoothDevicePairingDetailBaseTest { 100 101 @Rule 102 public final MockitoRule mMockitoRule = MockitoJUnit.rule(); 103 @Rule 104 public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); 105 106 public static final String KEY_DEVICE_LIST_GROUP = "test_key"; 107 108 private static final String TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1"; 109 private static final String TEST_DEVICE_ADDRESS_B = "00:B1:B1:B1:B1:B1"; 110 private final Context mContext = ApplicationProvider.getApplicationContext(); 111 112 @Mock 113 private Resources mResource; 114 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 115 private LocalBluetoothManager mLocalManager; 116 @Mock 117 private CachedBluetoothDevice mCachedBluetoothDevice; 118 @Mock 119 private Drawable mDrawable; 120 private BluetoothAdapter mBluetoothAdapter; 121 private ShadowBluetoothAdapter mShadowBluetoothAdapter; 122 private BluetoothProgressCategory mAvailableDevicesCategory; 123 private BluetoothDevice mBluetoothDevice; 124 private TestBluetoothDevicePairingDetailBase mFragment; 125 126 @Before setUp()127 public void setUp() { 128 mAvailableDevicesCategory = spy(new BluetoothProgressCategory(mContext)); 129 mBluetoothAdapter = spy(BluetoothAdapter.getDefaultAdapter()); 130 mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); 131 mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported( 132 BluetoothStatusCodes.FEATURE_SUPPORTED); 133 mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( 134 BluetoothStatusCodes.FEATURE_SUPPORTED); 135 when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS); 136 final Pair<Drawable, String> pairs = new Pair<>(mDrawable, "fake_device"); 137 when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs); 138 mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS); 139 140 mFragment = spy(new TestBluetoothDevicePairingDetailBase()); 141 when(mFragment.findPreference(KEY_DEVICE_LIST_GROUP)).thenReturn(mAvailableDevicesCategory); 142 doReturn(mContext).when(mFragment).getContext(); 143 doReturn(mResource).when(mFragment).getResources(); 144 mFragment.mDeviceListGroup = mAvailableDevicesCategory; 145 mFragment.mLocalManager = mLocalManager; 146 mFragment.mBluetoothAdapter = mBluetoothAdapter; 147 mFragment.initPreferencesFromPreferenceScreen(); 148 } 149 150 @Test startScanning_startScanAndRemoveDevices()151 public void startScanning_startScanAndRemoveDevices() { 152 mFragment.enableScanning(); 153 154 verify(mFragment).startScanning(); 155 verify(mAvailableDevicesCategory).removeAll(); 156 } 157 158 @Test updateContent_stateOn()159 public void updateContent_stateOn() { 160 mFragment.updateContent(BluetoothAdapter.STATE_ON); 161 162 assertThat(mBluetoothAdapter.isEnabled()).isTrue(); 163 verify(mFragment).enableScanning(); 164 } 165 166 @Test updateContent_stateOff_finish()167 public void updateContent_stateOff_finish() { 168 mFragment.updateContent(BluetoothAdapter.STATE_OFF); 169 170 verify(mFragment).finish(); 171 } 172 173 @Test updateBluetooth_bluetoothOff_turnOnBluetooth()174 public void updateBluetooth_bluetoothOff_turnOnBluetooth() { 175 mShadowBluetoothAdapter.setEnabled(false); 176 177 mFragment.updateBluetooth(); 178 179 assertThat(mBluetoothAdapter.isEnabled()).isTrue(); 180 } 181 182 @Test updateBluetooth_bluetoothOn_updateState()183 public void updateBluetooth_bluetoothOn_updateState() { 184 mShadowBluetoothAdapter.setEnabled(true); 185 doNothing().when(mFragment).updateContent(anyInt()); 186 187 mFragment.updateBluetooth(); 188 189 verify(mFragment).updateContent(anyInt()); 190 } 191 192 @Test onBluetoothStateChanged_whenTurnedOnBTShowToast()193 public void onBluetoothStateChanged_whenTurnedOnBTShowToast() { 194 doNothing().when(mFragment).updateContent(anyInt()); 195 196 mFragment.onBluetoothStateChanged(BluetoothAdapter.STATE_ON); 197 198 verify(mFragment).showBluetoothTurnedOnToast(); 199 } 200 201 @Test 202 @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING) 203 @DisableFlags(Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI) onDeviceBondStateChanged_bonded_notPairInSharing_finish()204 public void onDeviceBondStateChanged_bonded_notPairInSharing_finish() { 205 when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); 206 mFragment.mSelectedList.add(mBluetoothDevice); 207 setUpFragmentWithShareThenPairIntent(false); 208 mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDED); 209 210 verify(mFragment).finish(); 211 } 212 213 @Test 214 @Config(shadows = ShadowDialogFragment.class) 215 @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING) 216 @DisableFlags(Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI) onDeviceBondStateChanged_bonded_shareThenPair_handle()217 public void onDeviceBondStateChanged_bonded_shareThenPair_handle() { 218 ShadowDialogFragment.reset(); 219 when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); 220 mFragment.mSelectedList.add(mBluetoothDevice); 221 setUpFragmentWithShareThenPairIntent(true); 222 mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDED); 223 shadowOf(Looper.getMainLooper()).idle(); 224 225 ProgressDialogFragment progressDialog = mFragment.mProgressDialog; 226 assertThat(progressDialog).isNotNull(); 227 assertThat(progressDialog.getMessage()).isEqualTo( 228 mContext.getString(R.string.progress_dialog_connect_device_content, 229 TEST_DEVICE_ADDRESS)); 230 assertThat( 231 ShadowDialogFragment.isIsShowing(ProgressDialogFragment.class.getName())).isTrue(); 232 verify(mFragment, never()).finish(); 233 234 ShadowDialogFragment.reset(); 235 } 236 237 @Test 238 @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING) 239 @DisableFlags(Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI) onDeviceBondStateChanged_bonding_notPairInSharing_doNothing()240 public void onDeviceBondStateChanged_bonding_notPairInSharing_doNothing() { 241 when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); 242 mFragment.mSelectedList.add(mBluetoothDevice); 243 setUpFragmentWithShareThenPairIntent(false); 244 mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDING); 245 246 verify(mBluetoothAdapter, never()).addOnMetadataChangedListener(any(BluetoothDevice.class), 247 any(Executor.class), any(BluetoothAdapter.OnMetadataChangedListener.class)); 248 } 249 250 @Test 251 @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING) 252 @DisableFlags(Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI) onDeviceBondStateChanged_bonding_shareThenPair_addListener()253 public void onDeviceBondStateChanged_bonding_shareThenPair_addListener() { 254 when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); 255 mFragment.mSelectedList.add(mBluetoothDevice); 256 setUpFragmentWithShareThenPairIntent(true); 257 mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDING); 258 259 verify(mBluetoothAdapter).addOnMetadataChangedListener(eq(mBluetoothDevice), 260 any(Executor.class), 261 any(BluetoothAdapter.OnMetadataChangedListener.class)); 262 } 263 264 @Test 265 @DisableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI}) onDeviceBondStateChanged_unbonded_notPairInSharing_doNothing()266 public void onDeviceBondStateChanged_unbonded_notPairInSharing_doNothing() { 267 when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); 268 mFragment.mSelectedList.add(mBluetoothDevice); 269 mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_NONE); 270 271 verify(mBluetoothAdapter, never()).removeOnMetadataChangedListener( 272 any(BluetoothDevice.class), any(BluetoothAdapter.OnMetadataChangedListener.class)); 273 } 274 275 @Test 276 @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING) 277 @DisableFlags(Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI) onDeviceBondStateChanged_unbonded_shareThenPair_removeListener()278 public void onDeviceBondStateChanged_unbonded_shareThenPair_removeListener() { 279 when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); 280 mFragment.mSelectedList.add(mBluetoothDevice); 281 setUpFragmentWithShareThenPairIntent(true); 282 mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDING); 283 mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_NONE); 284 285 verify(mBluetoothAdapter).removeOnMetadataChangedListener(eq(mBluetoothDevice), 286 any(BluetoothAdapter.OnMetadataChangedListener.class)); 287 } 288 289 @Test 290 @DisableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI}) 291 public void onProfileConnectionStateChanged_deviceInSelectedListAndConnected_notInSharing_finish()292 onProfileConnectionStateChanged_deviceInSelectedListAndConnected_notInSharing_finish() { 293 final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B); 294 mFragment.mSelectedList.add(mBluetoothDevice); 295 mFragment.mSelectedList.add(device); 296 297 when(mCachedBluetoothDevice.isConnected()).thenReturn(true); 298 when(mCachedBluetoothDevice.getDevice()).thenReturn(device); 299 300 mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice, 301 BluetoothAdapter.STATE_CONNECTED, BluetoothProfile.A2DP); 302 shadowOf(Looper.getMainLooper()).idle(); 303 304 verify(mFragment).finish(); 305 } 306 307 @Test 308 @Config(shadows = ShadowDialogFragment.class) 309 @EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI}) 310 public void onProfileConnectionStateChanged_inSelectedListAndConnected_shareThenPair_handle()311 onProfileConnectionStateChanged_inSelectedListAndConnected_shareThenPair_handle() { 312 ShadowDialogFragment.reset(); 313 BluetoothDevice device = spy(mBluetoothDevice); 314 when(mCachedBluetoothDevice.getDevice()).thenReturn(device); 315 mFragment.mSelectedList.add(device); 316 setUpFragmentWithShareThenPairIntent(true); 317 mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDED); 318 shadowOf(Looper.getMainLooper()).idle(); 319 320 when(mCachedBluetoothDevice.isConnected()).thenReturn(true); 321 when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true); 322 when(mCachedBluetoothDevice.isConnectedLeAudioBroadcastAssistantDevice()).thenReturn(true); 323 when(mCachedBluetoothDevice.isConnectedVolumeControlDevice()).thenReturn(true); 324 325 mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice, 326 BluetoothAdapter.STATE_CONNECTED, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT); 327 shadowOf(Looper.getMainLooper()).idle(); 328 329 ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); 330 verify(mFragment.getActivity()).setResult(eq(Activity.RESULT_OK), captor.capture()); 331 Intent intent = captor.getValue(); 332 BluetoothDevice btDevice = 333 intent != null 334 ? intent.getParcelableExtra(EXTRA_BT_DEVICE_TO_AUTO_ADD_SOURCE, 335 BluetoothDevice.class) 336 : null; 337 assertThat(btDevice).isNotNull(); 338 assertThat(btDevice).isEqualTo(device); 339 verify(mFragment).finish(); 340 341 ShadowDialogFragment.reset(); 342 } 343 344 @Test 345 @DisableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI}) onProfileConnectionStateChanged_deviceNotInSelectedList_doNothing()346 public void onProfileConnectionStateChanged_deviceNotInSelectedList_doNothing() { 347 final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B); 348 mFragment.mSelectedList.add(device); 349 350 when(mCachedBluetoothDevice.isConnected()).thenReturn(true); 351 when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); 352 353 mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice, 354 BluetoothAdapter.STATE_CONNECTED, BluetoothProfile.A2DP); 355 356 // not crash 357 } 358 359 @Test 360 @DisableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI}) onProfileConnectionStateChanged_deviceDisconnected_doNothing()361 public void onProfileConnectionStateChanged_deviceDisconnected_doNothing() { 362 final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B); 363 mFragment.mSelectedList.add(mBluetoothDevice); 364 mFragment.mSelectedList.add(device); 365 366 when(mCachedBluetoothDevice.isConnected()).thenReturn(false); 367 when(mCachedBluetoothDevice.getDevice()).thenReturn(device); 368 369 mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice, 370 BluetoothAdapter.STATE_DISCONNECTED, BluetoothProfile.A2DP); 371 372 // not crash 373 } 374 375 @Test 376 @DisableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI}) onProfileConnectionStateChanged_deviceInPreferenceMapAndConnected_removed()377 public void onProfileConnectionStateChanged_deviceInPreferenceMapAndConnected_removed() { 378 final BluetoothDevicePreference preference = 379 new BluetoothDevicePreference(mContext, mCachedBluetoothDevice, 380 true, BluetoothDevicePreference.SortType.TYPE_FIFO); 381 final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS); 382 mFragment.getDevicePreferenceMap().put(mCachedBluetoothDevice, preference); 383 384 when(mCachedBluetoothDevice.isConnected()).thenReturn(true); 385 when(mCachedBluetoothDevice.getDevice()).thenReturn(device); 386 387 mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice, 388 BluetoothAdapter.STATE_CONNECTED, BluetoothProfile.A2DP); 389 390 assertThat(mFragment.getDevicePreferenceMap().size()).isEqualTo(0); 391 } 392 393 @Test 394 @DisableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI}) onProfileConnectionStateChanged_deviceNotInPreferenceMap_doNothing()395 public void onProfileConnectionStateChanged_deviceNotInPreferenceMap_doNothing() { 396 final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class); 397 final BluetoothDevicePreference preference = 398 new BluetoothDevicePreference(mContext, mCachedBluetoothDevice, 399 true, BluetoothDevicePreference.SortType.TYPE_FIFO); 400 final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS); 401 final BluetoothDevice device2 = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B); 402 mFragment.getDevicePreferenceMap().put(mCachedBluetoothDevice, preference); 403 404 when(mCachedBluetoothDevice.isConnected()).thenReturn(true); 405 when(mCachedBluetoothDevice.getDevice()).thenReturn(device); 406 when(cachedDevice.isConnected()).thenReturn(true); 407 when(cachedDevice.getDevice()).thenReturn(device2); 408 when(cachedDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS_B); 409 when(cachedDevice.getIdentityAddress()).thenReturn(TEST_DEVICE_ADDRESS_B); 410 411 mFragment.onProfileConnectionStateChanged(cachedDevice, BluetoothAdapter.STATE_CONNECTED, 412 BluetoothProfile.A2DP); 413 414 // not crash 415 } 416 setUpFragmentWithShareThenPairIntent(boolean enableShareThenPair)417 private void setUpFragmentWithShareThenPairIntent(boolean enableShareThenPair) { 418 Bundle args = new Bundle(); 419 args.putBoolean(EXTRA_PAIR_AND_JOIN_SHARING, enableShareThenPair); 420 Intent intent = new Intent(); 421 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, args); 422 FragmentActivity activity = spy(Robolectric.setupActivity(FragmentActivity.class)); 423 doReturn(intent).when(activity).getIntent(); 424 doReturn(activity).when(mFragment).getActivity(); 425 FragmentManager fragmentManager = mock(FragmentManager.class); 426 FragmentTransaction fragmentTransaction = mock(FragmentTransaction.class); 427 doReturn(fragmentTransaction).when(fragmentManager).beginTransaction(); 428 doReturn(fragmentManager).when(mFragment).getFragmentManager(); 429 doReturn(fragmentManager).when(mFragment).getChildFragmentManager(); 430 Lifecycle lifecycle = mock(Lifecycle.class); 431 when(lifecycle.getCurrentState()).thenReturn(Lifecycle.State.RESUMED); 432 doReturn(lifecycle).when(mFragment).getLifecycle(); 433 mFragment.mShouldTriggerShareThenPairFlow = mFragment.shouldTriggerShareThenPairFlow(); 434 } 435 436 private static class TestBluetoothDevicePairingDetailBase extends 437 BluetoothDevicePairingDetailBase { 438 TestBluetoothDevicePairingDetailBase()439 TestBluetoothDevicePairingDetailBase() { 440 super(); 441 } 442 443 @Override getMetricsCategory()444 public int getMetricsCategory() { 445 return 0; 446 } 447 448 @Override getDeviceListKey()449 public String getDeviceListKey() { 450 return KEY_DEVICE_LIST_GROUP; 451 } 452 453 @Override getPreferenceScreenResId()454 protected int getPreferenceScreenResId() { 455 return 0; 456 } 457 458 @Override getLogTag()459 protected String getLogTag() { 460 return "test_tag"; 461 } 462 } 463 464 /** Shadow of DialogFragment. */ 465 @Implements(value = DialogFragment.class) 466 public static class ShadowDialogFragment { 467 @RealObject 468 private DialogFragment mDialogFragment; 469 private static Map<String, Boolean> sDialogStatus = new HashMap<>(); 470 471 /** Resetter of the shadow. */ 472 @Resetter reset()473 public static void reset() { 474 sDialogStatus.clear(); 475 } 476 477 /** Implementation for DialogFragment#show. */ 478 @Implementation show(@onNull FragmentManager manager, @Nullable String tag)479 public void show(@NonNull FragmentManager manager, @Nullable String tag) { 480 sDialogStatus.put(mDialogFragment.getClass().getName(), true); 481 } 482 483 /** Implementation for DialogFragment#dismissAllowingStateLoss. */ 484 @Implementation dismissAllowingStateLoss()485 public void dismissAllowingStateLoss() { 486 sDialogStatus.put(mDialogFragment.getClass().getName(), false); 487 } 488 489 /** Implementation for DialogFragment#dismiss. */ 490 @Implementation dismiss()491 public void dismiss() { 492 sDialogStatus.put(mDialogFragment.getClass().getName(), false); 493 } 494 495 /** Check if DialogFragment is showing. */ isIsShowing(String clazzName)496 public static boolean isIsShowing(String clazzName) { 497 return sDialogStatus.getOrDefault(clazzName, false); 498 } 499 } 500 } 501