1 /* 2 * Copyright (C) 2020 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.systemui.media.dialog; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.ArgumentMatchers.any; 22 import static org.mockito.ArgumentMatchers.anyBoolean; 23 import static org.mockito.ArgumentMatchers.anyInt; 24 import static org.mockito.ArgumentMatchers.anyLong; 25 import static org.mockito.ArgumentMatchers.anyString; 26 import static org.mockito.Mockito.atLeastOnce; 27 import static org.mockito.Mockito.doReturn; 28 import static org.mockito.Mockito.mock; 29 import static org.mockito.Mockito.never; 30 import static org.mockito.Mockito.reset; 31 import static org.mockito.Mockito.spy; 32 import static org.mockito.Mockito.verify; 33 import static org.mockito.Mockito.verifyNoMoreInteractions; 34 import static org.mockito.Mockito.when; 35 36 import android.app.KeyguardManager; 37 import android.app.Notification; 38 import android.content.ComponentName; 39 import android.content.Context; 40 import android.content.Intent; 41 import android.content.pm.PackageManager; 42 import android.graphics.drawable.Drawable; 43 import android.graphics.drawable.Icon; 44 import android.media.AudioDeviceAttributes; 45 import android.media.AudioDeviceInfo; 46 import android.media.AudioManager; 47 import android.media.MediaDescription; 48 import android.media.MediaMetadata; 49 import android.media.MediaRoute2Info; 50 import android.media.NearbyDevice; 51 import android.media.RoutingSessionInfo; 52 import android.media.session.ISessionController; 53 import android.media.session.MediaController; 54 import android.media.session.MediaSession; 55 import android.media.session.MediaSessionManager; 56 import android.media.session.PlaybackState; 57 import android.os.Bundle; 58 import android.os.PowerExemptionManager; 59 import android.os.RemoteException; 60 import android.os.UserHandle; 61 import android.platform.test.annotations.DisableFlags; 62 import android.platform.test.annotations.EnableFlags; 63 import android.platform.test.flag.junit.FlagsParameterization; 64 import android.service.notification.StatusBarNotification; 65 import android.testing.TestableLooper; 66 import android.text.TextUtils; 67 import android.view.View; 68 69 import androidx.core.graphics.drawable.IconCompat; 70 import androidx.test.filters.SmallTest; 71 72 import com.android.media.flags.Flags; 73 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; 74 import com.android.settingslib.bluetooth.LocalBluetoothManager; 75 import com.android.settingslib.media.InputMediaDevice; 76 import com.android.settingslib.media.InputRouteManager; 77 import com.android.settingslib.media.LocalMediaManager; 78 import com.android.settingslib.media.MediaDevice; 79 import com.android.settingslib.utils.ThreadUtils; 80 import com.android.systemui.SysuiTestCase; 81 import com.android.systemui.SysuiTestCaseExtKt; 82 import com.android.systemui.animation.ActivityTransitionAnimator; 83 import com.android.systemui.animation.DialogTransitionAnimator; 84 import com.android.systemui.flags.FeatureFlags; 85 import com.android.systemui.kosmos.Kosmos; 86 import com.android.systemui.media.nearby.NearbyMediaDevicesManager; 87 import com.android.systemui.plugins.ActivityStarter; 88 import com.android.systemui.res.R; 89 import com.android.systemui.settings.UserTracker; 90 import com.android.systemui.statusbar.notification.collection.NotificationEntry; 91 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; 92 import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractor; 93 import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractorKosmosKt; 94 95 import com.google.common.collect.ImmutableList; 96 97 import org.junit.Before; 98 import org.junit.Test; 99 import org.junit.runner.RunWith; 100 import org.mockito.Mock; 101 import org.mockito.Mockito; 102 import org.mockito.MockitoAnnotations; 103 104 import platform.test.runner.parameterized.ParameterizedAndroidJunit4; 105 import platform.test.runner.parameterized.Parameters; 106 107 import java.util.ArrayList; 108 import java.util.Collections; 109 import java.util.List; 110 import java.util.concurrent.CountDownLatch; 111 import java.util.concurrent.TimeUnit; 112 import java.util.stream.Collectors; 113 114 @SmallTest 115 @RunWith(ParameterizedAndroidJunit4.class) 116 @TestableLooper.RunWithLooper(setAsMainLooper = true) 117 public class MediaSwitchingControllerTest extends SysuiTestCase { 118 private static final String TEST_DEVICE_1_ID = "test_device_1_id"; 119 private static final String TEST_DEVICE_2_ID = "test_device_2_id"; 120 private static final String TEST_DEVICE_3_ID = "test_device_3_id"; 121 private static final String TEST_DEVICE_4_ID = "test_device_4_id"; 122 private static final String TEST_DEVICE_5_ID = "test_device_5_id"; 123 private static final String TEST_ARTIST = "test_artist"; 124 private static final String TEST_SONG = "test_song"; 125 private static final String TEST_SESSION_ID = "test_session_id"; 126 private static final String TEST_SESSION_NAME = "test_session_name"; 127 private static final int MAX_VOLUME = 1; 128 private static final int CURRENT_VOLUME = 0; 129 private static final boolean VOLUME_FIXED_TRUE = true; 130 private static final int LATCH_COUNT_DOWN_TIME_IN_SECOND = 5; 131 private static final int LATCH_TIME_OUT_TIME_IN_SECOND = 10; 132 private static final String PRODUCT_NAME_BUILTIN_MIC = "Built-in Mic"; 133 private static final String PRODUCT_NAME_WIRED_HEADSET = "My Wired Headset"; 134 135 @Mock 136 private DialogTransitionAnimator mDialogTransitionAnimator; 137 @Mock 138 private ActivityTransitionAnimator.Controller mActivityTransitionAnimatorController; 139 @Mock 140 private NearbyMediaDevicesManager mNearbyMediaDevicesManager; 141 // Mock 142 @Mock 143 private MediaController mSessionMediaController; 144 @Mock 145 private MediaSessionManager mMediaSessionManager; 146 @Mock 147 private CachedBluetoothDeviceManager mCachedBluetoothDeviceManager; 148 @Mock 149 private LocalBluetoothManager mLocalBluetoothManager; 150 @Mock private MediaSwitchingController.Callback mCb; 151 @Mock 152 private MediaDevice mMediaDevice1; 153 @Mock 154 private MediaDevice mMediaDevice2; 155 @Mock private MediaDevice mMediaDevice3; 156 @Mock private MediaDevice mMediaDevice4; 157 @Mock private MediaDevice mMediaDevice5; 158 @Mock private NearbyDevice mNearbyDevice1; 159 @Mock 160 private NearbyDevice mNearbyDevice2; 161 @Mock 162 private MediaMetadata mMediaMetadata; 163 @Mock 164 private RoutingSessionInfo mRemoteSessionInfo; 165 @Mock 166 private ActivityStarter mStarter; 167 @Mock 168 private AudioManager mAudioManager; 169 @Mock 170 private KeyguardManager mKeyguardManager; 171 @Mock 172 private ActivityTransitionAnimator.Controller mController; 173 @Mock 174 private PowerExemptionManager mPowerExemptionManager; 175 @Mock 176 private CommonNotifCollection mNotifCollection; 177 @Mock 178 private PackageManager mPackageManager; 179 @Mock 180 private Drawable mDrawable; 181 @Mock 182 private PlaybackState mPlaybackState; 183 184 @Mock 185 private UserTracker mUserTracker; 186 187 private final Kosmos mKosmos = SysuiTestCaseExtKt.testKosmos(this); 188 189 private FeatureFlags mFlags = mock(FeatureFlags.class); 190 private View mDialogLaunchView = mock(View.class); 191 private MediaSwitchingController.Callback mCallback = 192 mock(MediaSwitchingController.Callback.class); 193 194 final Notification mNotification = mock(Notification.class); 195 private final VolumePanelGlobalStateInteractor mVolumePanelGlobalStateInteractor = 196 VolumePanelGlobalStateInteractorKosmosKt.getVolumePanelGlobalStateInteractor( 197 mKosmos); 198 199 private Context mSpyContext; 200 private String mPackageName = null; 201 private MediaSwitchingController mMediaSwitchingController; 202 private LocalMediaManager mLocalMediaManager; 203 private InputRouteManager mInputRouteManager; 204 private List<MediaController> mMediaControllers = new ArrayList<>(); 205 private List<MediaDevice> mMediaDevices = new ArrayList<>(); 206 private List<NearbyDevice> mNearbyDevices = new ArrayList<>(); 207 private MediaDescription mMediaDescription; 208 private List<RoutingSessionInfo> mRoutingSessionInfos = new ArrayList<>(); 209 210 @Parameters(name = "{0}") getParams()211 public static List<FlagsParameterization> getParams() { 212 return FlagsParameterization.allCombinationsOf( 213 Flags.FLAG_FIX_OUTPUT_MEDIA_ITEM_LIST_INDEX_OUT_OF_BOUNDS_EXCEPTION, 214 Flags.FLAG_ENABLE_OUTPUT_SWITCHER_DEVICE_GROUPING); 215 } 216 MediaSwitchingControllerTest(FlagsParameterization flags)217 public MediaSwitchingControllerTest(FlagsParameterization flags) { 218 mSetFlagsRule.setFlagsParameterization(flags); 219 } 220 221 @Before setUp()222 public void setUp() { 223 mPackageName = mContext.getPackageName(); 224 225 MockitoAnnotations.initMocks(this); 226 mContext.setMockPackageManager(mPackageManager); 227 mSpyContext = spy(mContext); 228 final UserHandle userHandle = mock(UserHandle.class); 229 when(mUserTracker.getUserHandle()).thenReturn(userHandle); 230 when(mSessionMediaController.getPackageName()).thenReturn(mPackageName); 231 when(mSessionMediaController.getPlaybackState()).thenReturn(mPlaybackState); 232 mMediaControllers.add(mSessionMediaController); 233 when(mMediaSessionManager.getActiveSessionsForUser(any(), 234 Mockito.eq(userHandle))).thenReturn( 235 mMediaControllers); 236 doReturn(mMediaSessionManager).when(mSpyContext).getSystemService( 237 MediaSessionManager.class); 238 when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn( 239 mCachedBluetoothDeviceManager); 240 241 mMediaSwitchingController = 242 new MediaSwitchingController( 243 mSpyContext, 244 mPackageName, 245 mContext.getUser(), 246 /* token */ null, 247 mMediaSessionManager, 248 mLocalBluetoothManager, 249 mStarter, 250 mNotifCollection, 251 mDialogTransitionAnimator, 252 mNearbyMediaDevicesManager, 253 mAudioManager, 254 mPowerExemptionManager, 255 mKeyguardManager, 256 mFlags, 257 mVolumePanelGlobalStateInteractor, 258 mUserTracker); 259 mLocalMediaManager = spy(mMediaSwitchingController.mLocalMediaManager); 260 when(mLocalMediaManager.isPreferenceRouteListingExist()).thenReturn(false); 261 mMediaSwitchingController.mLocalMediaManager = mLocalMediaManager; 262 263 mMediaSwitchingController.mInputRouteManager = 264 new InputRouteManager(mContext, mAudioManager); 265 mInputRouteManager = spy(mMediaSwitchingController.mInputRouteManager); 266 mMediaSwitchingController.mInputRouteManager = mInputRouteManager; 267 when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)) 268 .thenReturn(new AudioDeviceInfo[0]); 269 270 MediaDescription.Builder builder = new MediaDescription.Builder(); 271 builder.setTitle(TEST_SONG); 272 builder.setSubtitle(TEST_ARTIST); 273 mMediaDescription = builder.build(); 274 when(mMediaMetadata.getDescription()).thenReturn(mMediaDescription); 275 when(mMediaDevice1.getId()).thenReturn(TEST_DEVICE_1_ID); 276 when(mMediaDevice2.getId()).thenReturn(TEST_DEVICE_2_ID); 277 mMediaDevices.add(mMediaDevice1); 278 mMediaDevices.add(mMediaDevice2); 279 280 when(mNearbyDevice1.getMediaRoute2Id()).thenReturn(TEST_DEVICE_1_ID); 281 when(mNearbyDevice1.getRangeZone()).thenReturn(NearbyDevice.RANGE_FAR); 282 when(mNearbyDevice2.getMediaRoute2Id()).thenReturn(TEST_DEVICE_2_ID); 283 when(mNearbyDevice2.getRangeZone()).thenReturn(NearbyDevice.RANGE_CLOSE); 284 mNearbyDevices.add(mNearbyDevice1); 285 mNearbyDevices.add(mNearbyDevice2); 286 287 final List<NotificationEntry> entryList = new ArrayList<>(); 288 final NotificationEntry entry = mock(NotificationEntry.class); 289 final StatusBarNotification sbn = mock(StatusBarNotification.class); 290 final Bundle bundle = mock(Bundle.class); 291 final MediaSession.Token token = mock(MediaSession.Token.class); 292 final ISessionController binder = mock(ISessionController.class); 293 entryList.add(entry); 294 295 when(mNotification.isMediaNotification()).thenReturn(false); 296 when(mNotifCollection.getAllNotifs()).thenReturn(entryList); 297 when(entry.getSbn()).thenReturn(sbn); 298 when(sbn.getNotification()).thenReturn(mNotification); 299 when(sbn.getPackageName()).thenReturn(mPackageName); 300 mNotification.extras = bundle; 301 when(bundle.getParcelable(Notification.EXTRA_MEDIA_SESSION, 302 MediaSession.Token.class)).thenReturn(token); 303 when(token.getBinder()).thenReturn(binder); 304 } 305 306 @Test start_verifyLocalMediaManagerInit()307 public void start_verifyLocalMediaManagerInit() { 308 mMediaSwitchingController.start(mCb); 309 310 verify(mLocalMediaManager).registerCallback(mMediaSwitchingController); 311 verify(mLocalMediaManager).startScan(); 312 } 313 314 @Test stop_verifyLocalMediaManagerDeinit()315 public void stop_verifyLocalMediaManagerDeinit() { 316 mMediaSwitchingController.start(mCb); 317 reset(mLocalMediaManager); 318 319 mMediaSwitchingController.stop(); 320 321 verify(mLocalMediaManager).unregisterCallback(mMediaSwitchingController); 322 verify(mLocalMediaManager).stopScan(); 323 } 324 325 @Test start_notificationNotFound_mediaControllerInitFromSession()326 public void start_notificationNotFound_mediaControllerInitFromSession() { 327 mMediaSwitchingController.start(mCb); 328 329 verify(mSessionMediaController).registerCallback(any()); 330 } 331 332 @Test start_MediaNotificationFound_mediaControllerNotInitFromSession()333 public void start_MediaNotificationFound_mediaControllerNotInitFromSession() { 334 when(mNotification.isMediaNotification()).thenReturn(true); 335 mMediaSwitchingController.start(mCb); 336 337 verify(mSessionMediaController, never()).registerCallback(any()); 338 verifyNoMoreInteractions(mMediaSessionManager); 339 } 340 341 @Test start_withoutPackageName_verifyMediaControllerInit()342 public void start_withoutPackageName_verifyMediaControllerInit() { 343 mMediaSwitchingController = 344 new MediaSwitchingController( 345 mSpyContext, 346 null, 347 mContext.getUser(), 348 /* token */ null, 349 mMediaSessionManager, 350 mLocalBluetoothManager, 351 mStarter, 352 mNotifCollection, 353 mDialogTransitionAnimator, 354 mNearbyMediaDevicesManager, 355 mAudioManager, 356 mPowerExemptionManager, 357 mKeyguardManager, 358 mFlags, 359 mVolumePanelGlobalStateInteractor, 360 mUserTracker); 361 362 mMediaSwitchingController.start(mCb); 363 364 verify(mSessionMediaController, never()).registerCallback(any()); 365 } 366 367 @Test start_nearbyMediaDevicesManagerNotNull_registersNearbyDevicesCallback()368 public void start_nearbyMediaDevicesManagerNotNull_registersNearbyDevicesCallback() { 369 mMediaSwitchingController.start(mCb); 370 371 verify(mNearbyMediaDevicesManager).registerNearbyDevicesCallback(any()); 372 } 373 374 @Test stop_withPackageName_verifyMediaControllerDeinit()375 public void stop_withPackageName_verifyMediaControllerDeinit() { 376 mMediaSwitchingController.start(mCb); 377 reset(mSessionMediaController); 378 379 mMediaSwitchingController.stop(); 380 381 verify(mSessionMediaController).unregisterCallback(any()); 382 } 383 384 @Test stop_withoutPackageName_verifyMediaControllerDeinit()385 public void stop_withoutPackageName_verifyMediaControllerDeinit() { 386 mMediaSwitchingController = 387 new MediaSwitchingController( 388 mSpyContext, 389 null, 390 mSpyContext.getUser(), 391 /* token */ null, 392 mMediaSessionManager, 393 mLocalBluetoothManager, 394 mStarter, 395 mNotifCollection, 396 mDialogTransitionAnimator, 397 mNearbyMediaDevicesManager, 398 mAudioManager, 399 mPowerExemptionManager, 400 mKeyguardManager, 401 mFlags, 402 mVolumePanelGlobalStateInteractor, 403 mUserTracker); 404 405 mMediaSwitchingController.start(mCb); 406 407 mMediaSwitchingController.stop(); 408 409 verify(mSessionMediaController, never()).unregisterCallback(any()); 410 } 411 412 @Test stop_nearbyMediaDevicesManagerNotNull_unregistersNearbyDevicesCallback()413 public void stop_nearbyMediaDevicesManagerNotNull_unregistersNearbyDevicesCallback() { 414 mMediaSwitchingController.start(mCb); 415 reset(mSessionMediaController); 416 417 mMediaSwitchingController.stop(); 418 419 verify(mNearbyMediaDevicesManager).unregisterNearbyDevicesCallback(any()); 420 } 421 422 @Test tryToLaunchMediaApplication_nullIntent_skip()423 public void tryToLaunchMediaApplication_nullIntent_skip() { 424 mMediaSwitchingController.tryToLaunchMediaApplication(mDialogLaunchView); 425 426 verify(mCb, never()).dismissDialog(); 427 } 428 429 @Test tryToLaunchMediaApplication_intentNotNull_startActivity()430 public void tryToLaunchMediaApplication_intentNotNull_startActivity() { 431 when(mDialogTransitionAnimator.createActivityTransitionController(any(View.class))) 432 .thenReturn(mController); 433 Intent intent = new Intent(mPackageName); 434 doReturn(intent).when(mPackageManager).getLaunchIntentForPackage(mPackageName); 435 mMediaSwitchingController.start(mCallback); 436 437 mMediaSwitchingController.tryToLaunchMediaApplication(mDialogLaunchView); 438 439 verify(mStarter).startActivity(any(Intent.class), anyBoolean(), 440 Mockito.eq(mController)); 441 } 442 443 @Test tryToLaunchInAppRoutingIntent_componentNameNotNull_startActivity()444 public void tryToLaunchInAppRoutingIntent_componentNameNotNull_startActivity() { 445 when(mDialogTransitionAnimator.createActivityTransitionController(any(View.class))) 446 .thenReturn(mController); 447 mMediaSwitchingController.start(mCallback); 448 when(mLocalMediaManager.getLinkedItemComponentName()).thenReturn( 449 new ComponentName(mPackageName, "")); 450 451 mMediaSwitchingController.tryToLaunchInAppRoutingIntent( 452 TEST_DEVICE_1_ID, mDialogLaunchView); 453 454 verify(mStarter).startActivity(any(Intent.class), anyBoolean(), 455 Mockito.eq(mController)); 456 } 457 458 @Test onDevicesUpdated_unregistersNearbyDevicesCallback()459 public void onDevicesUpdated_unregistersNearbyDevicesCallback() throws RemoteException { 460 mMediaSwitchingController.start(mCb); 461 462 mMediaSwitchingController.onDevicesUpdated(ImmutableList.of()); 463 464 verify(mNearbyMediaDevicesManager).unregisterNearbyDevicesCallback(any()); 465 } 466 467 @Test onDeviceListUpdate_withNearbyDevices_updatesRangeInformation()468 public void onDeviceListUpdate_withNearbyDevices_updatesRangeInformation() 469 throws RemoteException { 470 mMediaSwitchingController.start(mCb); 471 reset(mCb); 472 473 mMediaSwitchingController.onDevicesUpdated(mNearbyDevices); 474 mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); 475 476 verify(mMediaDevice1).setRangeZone(NearbyDevice.RANGE_FAR); 477 verify(mMediaDevice2).setRangeZone(NearbyDevice.RANGE_CLOSE); 478 } 479 480 @Test onDeviceListUpdate_withNearbyDevices_rankByRangeInformation()481 public void onDeviceListUpdate_withNearbyDevices_rankByRangeInformation() 482 throws RemoteException { 483 mMediaSwitchingController.start(mCb); 484 reset(mCb); 485 486 mMediaSwitchingController.onDevicesUpdated(mNearbyDevices); 487 mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); 488 489 assertThat(mMediaDevices.get(0).getId()).isEqualTo(TEST_DEVICE_1_ID); 490 } 491 492 @Test routeProcessSupport_onDeviceListUpdate_preferenceExist_NotUpdatesRangeInformation()493 public void routeProcessSupport_onDeviceListUpdate_preferenceExist_NotUpdatesRangeInformation() 494 throws RemoteException { 495 when(mLocalMediaManager.isPreferenceRouteListingExist()).thenReturn(true); 496 mMediaSwitchingController.start(mCb); 497 reset(mCb); 498 499 mMediaSwitchingController.onDevicesUpdated(mNearbyDevices); 500 mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); 501 502 verify(mMediaDevice1, never()).setRangeZone(anyInt()); 503 verify(mMediaDevice2, never()).setRangeZone(anyInt()); 504 } 505 506 @DisableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) 507 @Test onDeviceListUpdate_verifyDeviceListCallback()508 public void onDeviceListUpdate_verifyDeviceListCallback() { 509 // This test relies on mMediaSwitchingController.start being called while the selected 510 // device list has exactly one item, and that item's id is: 511 // - Different from both ids in mMediaDevices. 512 // - Different from the id of the route published by the device under test (usually the 513 // built-in speakers). 514 // So mock the selected device to respect these two preconditions. 515 MediaDevice mockSelectedMediaDevice = Mockito.mock(MediaDevice.class); 516 when(mockSelectedMediaDevice.getId()).thenReturn(TEST_DEVICE_3_ID); 517 doReturn(List.of(mockSelectedMediaDevice)) 518 .when(mLocalMediaManager) 519 .getSelectedMediaDevice(); 520 521 mMediaSwitchingController.start(mCb); 522 reset(mCb); 523 524 mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); 525 final List<MediaDevice> devices = new ArrayList<>(); 526 for (MediaItem item : mMediaSwitchingController.getMediaItemList()) { 527 if (item.getMediaDevice().isPresent()) { 528 devices.add(item.getMediaDevice().get()); 529 } 530 } 531 532 assertThat(devices.containsAll(mMediaDevices)).isTrue(); 533 assertThat(devices.size()).isEqualTo(mMediaDevices.size()); 534 // There should be 2 non-MediaDevice items: the "Speakers & Display" title, and the "Connect 535 // a device" button. 536 assertThat(mMediaSwitchingController.getMediaItemList().size()) 537 .isEqualTo(mMediaDevices.size() + 2); 538 verify(mCb).onDeviceListChanged(); 539 } 540 541 @EnableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) 542 @Test onDeviceListUpdate_verifyDeviceListCallback_inputRouting()543 public void onDeviceListUpdate_verifyDeviceListCallback_inputRouting() { 544 // This test relies on mMediaSwitchingController.start being called while the selected 545 // device list has exactly one item, and that item's id is: 546 // - Different from both ids in mMediaDevices. 547 // - Different from the id of the route published by the device under test (usually the 548 // built-in speakers). 549 // So mock the selected device to respect these two preconditions. 550 MediaDevice mockSelectedMediaDevice = Mockito.mock(MediaDevice.class); 551 when(mockSelectedMediaDevice.getId()).thenReturn(TEST_DEVICE_3_ID); 552 doReturn(List.of(mockSelectedMediaDevice)) 553 .when(mLocalMediaManager) 554 .getSelectedMediaDevice(); 555 556 mMediaSwitchingController.start(mCb); 557 reset(mCb); 558 559 mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); 560 final List<MediaDevice> devices = new ArrayList<>(); 561 for (MediaItem item : mMediaSwitchingController.getMediaItemList()) { 562 if (item.getMediaDevice().isPresent()) { 563 devices.add(item.getMediaDevice().get()); 564 } 565 } 566 567 assertThat(devices.containsAll(mMediaDevices)).isTrue(); 568 assertThat(devices.size()).isEqualTo(mMediaDevices.size()); 569 // When input routing is enabled, there should be 4 non-MediaDevice items: one for 570 // the "Output" title, one for the "Speakers & Displays" title, one for the "Connect a 571 // device" button, and one for the "Input" title. 572 assertThat(mMediaSwitchingController.getMediaItemList().size()) 573 .isEqualTo(mMediaDevices.size() + 4); 574 verify(mCb).onDeviceListChanged(); 575 } 576 577 @DisableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) 578 @Test advanced_onDeviceListUpdateWithConnectedDeviceRemote_verifyItemSize()579 public void advanced_onDeviceListUpdateWithConnectedDeviceRemote_verifyItemSize() { 580 // This test relies on mMediaSwitchingController.start being called while the selected 581 // device list has exactly one item, and that item's id is: 582 // - Different from both ids in mMediaDevices. 583 // - Different from the id of the route published by the device under test (usually the 584 // built-in speakers). 585 // So mock the selected device to respect these two preconditions. 586 MediaDevice mockSelectedMediaDevice = Mockito.mock(MediaDevice.class); 587 when(mockSelectedMediaDevice.getId()).thenReturn(TEST_DEVICE_3_ID); 588 doReturn(List.of(mockSelectedMediaDevice)) 589 .when(mLocalMediaManager) 590 .getSelectedMediaDevice(); 591 592 when(mMediaDevice1.getFeatures()).thenReturn( 593 ImmutableList.of(MediaRoute2Info.FEATURE_REMOTE_PLAYBACK)); 594 when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice1); 595 mMediaSwitchingController.start(mCb); 596 reset(mCb); 597 598 mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); 599 final List<MediaDevice> devices = new ArrayList<>(); 600 for (MediaItem item : mMediaSwitchingController.getMediaItemList()) { 601 if (item.getMediaDevice().isPresent()) { 602 devices.add(item.getMediaDevice().get()); 603 } 604 } 605 606 assertThat(devices.containsAll(mMediaDevices)).isTrue(); 607 assertThat(devices.size()).isEqualTo(mMediaDevices.size()); 608 // There should be 1 non-MediaDevice item: the "Speakers & Display" title. 609 assertThat(mMediaSwitchingController.getMediaItemList().size()) 610 .isEqualTo(mMediaDevices.size() + 1); 611 verify(mCb).onDeviceListChanged(); 612 } 613 614 @EnableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) 615 @Test advanced_onDeviceListUpdateWithConnectedDeviceRemote_verifyItemSize_inputRouting()616 public void advanced_onDeviceListUpdateWithConnectedDeviceRemote_verifyItemSize_inputRouting() { 617 // This test relies on mMediaSwitchingController.start being called while the selected 618 // device list has exactly one item, and that item's id is: 619 // - Different from both ids in mMediaDevices. 620 // - Different from the id of the route published by the device under test (usually the 621 // built-in speakers). 622 // So mock the selected device to respect these two preconditions. 623 MediaDevice mockSelectedMediaDevice = Mockito.mock(MediaDevice.class); 624 when(mockSelectedMediaDevice.getId()).thenReturn(TEST_DEVICE_3_ID); 625 doReturn(List.of(mockSelectedMediaDevice)) 626 .when(mLocalMediaManager) 627 .getSelectedMediaDevice(); 628 629 when(mMediaDevice1.getFeatures()) 630 .thenReturn(ImmutableList.of(MediaRoute2Info.FEATURE_REMOTE_PLAYBACK)); 631 when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice1); 632 mMediaSwitchingController.start(mCb); 633 reset(mCb); 634 635 mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); 636 final List<MediaDevice> devices = new ArrayList<>(); 637 for (MediaItem item : mMediaSwitchingController.getMediaItemList()) { 638 if (item.getMediaDevice().isPresent()) { 639 devices.add(item.getMediaDevice().get()); 640 } 641 } 642 643 assertThat(devices.containsAll(mMediaDevices)).isTrue(); 644 assertThat(devices.size()).isEqualTo(mMediaDevices.size()); 645 // When input routing is enabled, there should be 3 non-MediaDevice items: one for 646 // the "Output" title, one for the "Speakers & Displays" title, and one for the "Input" 647 // title. 648 assertThat(mMediaSwitchingController.getMediaItemList().size()) 649 .isEqualTo(mMediaDevices.size() + 3); 650 verify(mCb).onDeviceListChanged(); 651 } 652 653 @EnableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) 654 @Test onInputDeviceListUpdate_verifyDeviceListCallback()655 public void onInputDeviceListUpdate_verifyDeviceListCallback() { 656 AudioDeviceInfo[] audioDeviceInfos = {}; 657 when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)) 658 .thenReturn(audioDeviceInfos); 659 mMediaSwitchingController.start(mCb); 660 661 // Output devices have changed. 662 mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); 663 664 final MediaDevice mediaDevice3 = 665 InputMediaDevice.create( 666 mContext, 667 TEST_DEVICE_3_ID, 668 AudioDeviceInfo.TYPE_BUILTIN_MIC, 669 MAX_VOLUME, 670 CURRENT_VOLUME, 671 VOLUME_FIXED_TRUE, 672 PRODUCT_NAME_BUILTIN_MIC); 673 final MediaDevice mediaDevice4 = 674 InputMediaDevice.create( 675 mContext, 676 TEST_DEVICE_4_ID, 677 AudioDeviceInfo.TYPE_WIRED_HEADSET, 678 MAX_VOLUME, 679 CURRENT_VOLUME, 680 VOLUME_FIXED_TRUE, 681 PRODUCT_NAME_WIRED_HEADSET); 682 final List<MediaDevice> inputDevices = new ArrayList<>(); 683 inputDevices.add(mediaDevice3); 684 inputDevices.add(mediaDevice4); 685 686 // Input devices have changed. 687 mMediaSwitchingController.mInputDeviceCallback.onInputDeviceListUpdated(inputDevices); 688 689 final List<MediaDevice> devices = new ArrayList<>(); 690 for (MediaItem item : mMediaSwitchingController.getMediaItemList()) { 691 if (item.getMediaDevice().isPresent()) { 692 devices.add(item.getMediaDevice().get()); 693 } 694 } 695 696 assertThat(devices).containsAtLeastElementsIn(mMediaDevices); 697 assertThat(devices).hasSize(mMediaDevices.size() + inputDevices.size()); 698 verify(mCb, atLeastOnce()).onDeviceListChanged(); 699 } 700 701 @Test advanced_categorizeMediaItems_withSuggestedDevice_verifyDeviceListSize()702 public void advanced_categorizeMediaItems_withSuggestedDevice_verifyDeviceListSize() { 703 when(mMediaDevice1.isSuggestedDevice()).thenReturn(true); 704 when(mMediaDevice2.isSuggestedDevice()).thenReturn(false); 705 706 mMediaSwitchingController.start(mCb); 707 reset(mCb); 708 mMediaSwitchingController.clearMediaItemList(); 709 mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); 710 final List<MediaDevice> devices = new ArrayList<>(); 711 int dividerSize = 0; 712 for (MediaItem item : mMediaSwitchingController.getMediaItemList()) { 713 if (item.getMediaDevice().isPresent()) { 714 devices.add(item.getMediaDevice().get()); 715 } 716 if (item.getMediaItemType() == MediaItem.MediaItemType.TYPE_GROUP_DIVIDER) { 717 dividerSize++; 718 } 719 } 720 721 assertThat(devices.containsAll(mMediaDevices)).isTrue(); 722 assertThat(devices.size()).isEqualTo(mMediaDevices.size()); 723 assertThat(dividerSize).isEqualTo(2); 724 verify(mCb).onDeviceListChanged(); 725 } 726 727 @Test onDeviceListUpdate_isRefreshing_updatesNeedRefreshToTrue()728 public void onDeviceListUpdate_isRefreshing_updatesNeedRefreshToTrue() { 729 mMediaSwitchingController.start(mCb); 730 reset(mCb); 731 mMediaSwitchingController.mIsRefreshing = true; 732 733 mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); 734 735 assertThat(mMediaSwitchingController.mNeedRefresh).isTrue(); 736 } 737 738 @Test advanced_onDeviceListUpdate_isRefreshing_updatesNeedRefreshToTrue()739 public void advanced_onDeviceListUpdate_isRefreshing_updatesNeedRefreshToTrue() { 740 mMediaSwitchingController.start(mCb); 741 reset(mCb); 742 mMediaSwitchingController.mIsRefreshing = true; 743 744 mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); 745 746 assertThat(mMediaSwitchingController.mNeedRefresh).isTrue(); 747 } 748 749 @Test cancelMuteAwaitConnection_cancelsWithMediaManager()750 public void cancelMuteAwaitConnection_cancelsWithMediaManager() { 751 when(mAudioManager.getMutingExpectedDevice()).thenReturn(mock(AudioDeviceAttributes.class)); 752 mMediaSwitchingController.start(mCb); 753 reset(mCb); 754 755 mMediaSwitchingController.cancelMuteAwaitConnection(); 756 757 verify(mAudioManager).cancelMuteAwaitConnection(any()); 758 } 759 760 @Test cancelMuteAwaitConnection_audioManagerIsNull_noAction()761 public void cancelMuteAwaitConnection_audioManagerIsNull_noAction() { 762 when(mAudioManager.getMutingExpectedDevice()).thenReturn(null); 763 mMediaSwitchingController.start(mCb); 764 reset(mCb); 765 mMediaSwitchingController.cancelMuteAwaitConnection(); 766 767 verify(mAudioManager, never()).cancelMuteAwaitConnection(any()); 768 } 769 770 @Test getAppSourceName_packageNameIsNull_returnsNull()771 public void getAppSourceName_packageNameIsNull_returnsNull() { 772 MediaSwitchingController testMediaSwitchingController = 773 new MediaSwitchingController( 774 mSpyContext, 775 "", 776 mSpyContext.getUser(), 777 /* token */ null, 778 mMediaSessionManager, 779 mLocalBluetoothManager, 780 mStarter, 781 mNotifCollection, 782 mDialogTransitionAnimator, 783 mNearbyMediaDevicesManager, 784 mAudioManager, 785 mPowerExemptionManager, 786 mKeyguardManager, 787 mFlags, 788 mVolumePanelGlobalStateInteractor, 789 mUserTracker); 790 testMediaSwitchingController.start(mCb); 791 reset(mCb); 792 793 testMediaSwitchingController.getAppSourceName(); 794 795 assertThat(testMediaSwitchingController.getAppSourceName()).isNull(); 796 } 797 798 @Test isActiveItem_deviceNotConnected_returnsFalse()799 public void isActiveItem_deviceNotConnected_returnsFalse() { 800 when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice2); 801 802 assertThat(mMediaSwitchingController.isActiveItem(mMediaDevice1)).isFalse(); 803 } 804 805 @Test getNotificationSmallIcon_packageNameIsNull_returnsNull()806 public void getNotificationSmallIcon_packageNameIsNull_returnsNull() { 807 MediaSwitchingController testMediaSwitchingController = 808 new MediaSwitchingController( 809 mSpyContext, 810 "", 811 mSpyContext.getUser(), 812 /* token */ null, 813 mMediaSessionManager, 814 mLocalBluetoothManager, 815 mStarter, 816 mNotifCollection, 817 mDialogTransitionAnimator, 818 mNearbyMediaDevicesManager, 819 mAudioManager, 820 mPowerExemptionManager, 821 mKeyguardManager, 822 mFlags, 823 mVolumePanelGlobalStateInteractor, 824 mUserTracker); 825 testMediaSwitchingController.start(mCb); 826 reset(mCb); 827 828 testMediaSwitchingController.getAppSourceName(); 829 830 assertThat(testMediaSwitchingController.getNotificationSmallIcon()).isNull(); 831 } 832 833 @Test refreshDataSetIfNeeded_needRefreshIsTrue_setsToFalse()834 public void refreshDataSetIfNeeded_needRefreshIsTrue_setsToFalse() { 835 mMediaSwitchingController.start(mCb); 836 reset(mCb); 837 mMediaSwitchingController.mNeedRefresh = true; 838 839 mMediaSwitchingController.refreshDataSetIfNeeded(); 840 841 assertThat(mMediaSwitchingController.mNeedRefresh).isFalse(); 842 } 843 844 @Test isCurrentConnectedDeviceRemote_containsFeatures_returnsTrue()845 public void isCurrentConnectedDeviceRemote_containsFeatures_returnsTrue() { 846 when(mMediaDevice1.getFeatures()).thenReturn( 847 ImmutableList.of(MediaRoute2Info.FEATURE_REMOTE_PLAYBACK)); 848 when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice1); 849 850 assertThat(mMediaSwitchingController.isCurrentConnectedDeviceRemote()).isTrue(); 851 } 852 853 @Test addDeviceToPlayMedia_callsLocalMediaManager()854 public void addDeviceToPlayMedia_callsLocalMediaManager() { 855 MediaSwitchingController testMediaSwitchingController = 856 new MediaSwitchingController( 857 mSpyContext, 858 null, 859 mSpyContext.getUser(), 860 /* token */ null, 861 mMediaSessionManager, 862 mLocalBluetoothManager, 863 mStarter, 864 mNotifCollection, 865 mDialogTransitionAnimator, 866 mNearbyMediaDevicesManager, 867 mAudioManager, 868 mPowerExemptionManager, 869 mKeyguardManager, 870 mFlags, 871 mVolumePanelGlobalStateInteractor, 872 mUserTracker); 873 874 LocalMediaManager mockLocalMediaManager = mock(LocalMediaManager.class); 875 testMediaSwitchingController.mLocalMediaManager = mockLocalMediaManager; 876 877 testMediaSwitchingController.addDeviceToPlayMedia(mMediaDevice2); 878 verify(mockLocalMediaManager).addDeviceToPlayMedia(mMediaDevice2); 879 } 880 881 @Test removeDeviceFromPlayMedia_callsLocalMediaManager()882 public void removeDeviceFromPlayMedia_callsLocalMediaManager() { 883 MediaSwitchingController testMediaSwitchingController = 884 new MediaSwitchingController( 885 mSpyContext, 886 null, 887 mSpyContext.getUser(), 888 /* token */ null, 889 mMediaSessionManager, 890 mLocalBluetoothManager, 891 mStarter, 892 mNotifCollection, 893 mDialogTransitionAnimator, 894 mNearbyMediaDevicesManager, 895 mAudioManager, 896 mPowerExemptionManager, 897 mKeyguardManager, 898 mFlags, 899 mVolumePanelGlobalStateInteractor, 900 mUserTracker); 901 902 LocalMediaManager mockLocalMediaManager = mock(LocalMediaManager.class); 903 testMediaSwitchingController.mLocalMediaManager = mockLocalMediaManager; 904 905 testMediaSwitchingController.removeDeviceFromPlayMedia(mMediaDevice2); 906 verify(mockLocalMediaManager).removeDeviceFromPlayMedia(mMediaDevice2); 907 } 908 909 @Test getTransferableMediaDevice_triggersFromLocalMediaManager()910 public void getTransferableMediaDevice_triggersFromLocalMediaManager() { 911 mMediaSwitchingController.getTransferableMediaDevices(); 912 913 verify(mLocalMediaManager).getTransferableMediaDevices(); 914 } 915 916 @Test getDeselectableMediaDevice_triggersFromLocalMediaManager()917 public void getDeselectableMediaDevice_triggersFromLocalMediaManager() { 918 mMediaSwitchingController.getDeselectableMediaDevice(); 919 920 verify(mLocalMediaManager).getDeselectableMediaDevice(); 921 } 922 923 @Test adjustSessionVolume_adjustWithoutId_triggersFromLocalMediaManager()924 public void adjustSessionVolume_adjustWithoutId_triggersFromLocalMediaManager() { 925 int testVolume = 10; 926 mMediaSwitchingController.adjustSessionVolume(testVolume); 927 928 verify(mLocalMediaManager).adjustSessionVolume(testVolume); 929 } 930 931 @Test logInteractionAdjustVolume_triggersFromMetricLogger()932 public void logInteractionAdjustVolume_triggersFromMetricLogger() { 933 MediaOutputMetricLogger spyMediaOutputMetricLogger = 934 spy(mMediaSwitchingController.mMetricLogger); 935 mMediaSwitchingController.mMetricLogger = spyMediaOutputMetricLogger; 936 937 mMediaSwitchingController.logInteractionAdjustVolume(mMediaDevice1); 938 939 verify(spyMediaOutputMetricLogger).logInteractionAdjustVolume(mMediaDevice1); 940 } 941 942 @Test getSessionVolumeMax_triggersFromLocalMediaManager()943 public void getSessionVolumeMax_triggersFromLocalMediaManager() { 944 mMediaSwitchingController.getSessionVolumeMax(); 945 946 verify(mLocalMediaManager).getSessionVolumeMax(); 947 } 948 949 @Test getSessionVolume_triggersFromLocalMediaManager()950 public void getSessionVolume_triggersFromLocalMediaManager() { 951 mMediaSwitchingController.getSessionVolume(); 952 953 verify(mLocalMediaManager).getSessionVolume(); 954 } 955 956 @Test getSessionName_triggersFromLocalMediaManager()957 public void getSessionName_triggersFromLocalMediaManager() { 958 mMediaSwitchingController.getSessionName(); 959 960 verify(mLocalMediaManager).getSessionName(); 961 } 962 963 @Test releaseSession_triggersFromLocalMediaManager()964 public void releaseSession_triggersFromLocalMediaManager() { 965 mMediaSwitchingController.releaseSession(); 966 967 verify(mLocalMediaManager).releaseSession(); 968 } 969 970 @Test isAnyDeviceTransferring_noDevicesStateIsConnecting_returnsFalse()971 public void isAnyDeviceTransferring_noDevicesStateIsConnecting_returnsFalse() { 972 mMediaSwitchingController.start(mCb); 973 reset(mCb); 974 975 mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); 976 977 assertThat(mMediaSwitchingController.isAnyDeviceTransferring()).isFalse(); 978 } 979 980 @Test isAnyDeviceTransferring_deviceStateIsConnecting_returnsTrue()981 public void isAnyDeviceTransferring_deviceStateIsConnecting_returnsTrue() { 982 when(mMediaDevice1.getState()).thenReturn( 983 LocalMediaManager.MediaDeviceState.STATE_CONNECTING); 984 mMediaSwitchingController.start(mCb); 985 reset(mCb); 986 987 mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); 988 989 assertThat(mMediaSwitchingController.isAnyDeviceTransferring()).isTrue(); 990 } 991 992 @Test isAnyDeviceTransferring_advancedLayoutSupport()993 public void isAnyDeviceTransferring_advancedLayoutSupport() { 994 when(mMediaDevice1.getState()).thenReturn( 995 LocalMediaManager.MediaDeviceState.STATE_CONNECTING); 996 mMediaSwitchingController.start(mCb); 997 mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); 998 999 assertThat(mMediaSwitchingController.isAnyDeviceTransferring()).isTrue(); 1000 } 1001 1002 @Test isPlaying_stateIsNull()1003 public void isPlaying_stateIsNull() { 1004 when(mSessionMediaController.getPlaybackState()).thenReturn(null); 1005 1006 assertThat(mMediaSwitchingController.isPlaying()).isFalse(); 1007 } 1008 1009 @Test onSelectedDeviceStateChanged_verifyCallback()1010 public void onSelectedDeviceStateChanged_verifyCallback() { 1011 when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice2); 1012 mMediaSwitchingController.start(mCb); 1013 reset(mCb); 1014 mMediaSwitchingController.connectDevice(mMediaDevice1); 1015 1016 mMediaSwitchingController.onSelectedDeviceStateChanged( 1017 mMediaDevice1, LocalMediaManager.MediaDeviceState.STATE_CONNECTED); 1018 1019 verify(mCb).onRouteChanged(); 1020 } 1021 1022 @Test onDeviceAttributesChanged_verifyCallback()1023 public void onDeviceAttributesChanged_verifyCallback() { 1024 mMediaSwitchingController.start(mCb); 1025 reset(mCb); 1026 1027 mMediaSwitchingController.onDeviceAttributesChanged(); 1028 1029 verify(mCb).onRouteChanged(); 1030 } 1031 1032 @Test onRequestFailed_verifyCallback()1033 public void onRequestFailed_verifyCallback() { 1034 when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice1); 1035 mMediaSwitchingController.start(mCb); 1036 reset(mCb); 1037 mMediaSwitchingController.connectDevice(mMediaDevice2); 1038 1039 mMediaSwitchingController.onRequestFailed(0 /* reason */); 1040 1041 verify(mCb, atLeastOnce()).onRouteChanged(); 1042 } 1043 1044 @Test getHeaderTitle_withoutMetadata_returnDefaultString()1045 public void getHeaderTitle_withoutMetadata_returnDefaultString() { 1046 when(mSessionMediaController.getMetadata()).thenReturn(null); 1047 1048 mMediaSwitchingController.start(mCb); 1049 1050 assertThat( 1051 mMediaSwitchingController 1052 .getHeaderTitle() 1053 .equals(mContext.getText(R.string.controls_media_title))) 1054 .isTrue(); 1055 } 1056 1057 @Test getHeaderTitle_withMetadata_returnSongName()1058 public void getHeaderTitle_withMetadata_returnSongName() { 1059 when(mSessionMediaController.getMetadata()).thenReturn(mMediaMetadata); 1060 1061 mMediaSwitchingController.start(mCb); 1062 1063 assertThat(mMediaSwitchingController.getHeaderTitle().equals(TEST_SONG)).isTrue(); 1064 } 1065 1066 @Test getHeaderSubTitle_withoutMetadata_returnNull()1067 public void getHeaderSubTitle_withoutMetadata_returnNull() { 1068 when(mSessionMediaController.getMetadata()).thenReturn(null); 1069 1070 mMediaSwitchingController.start(mCb); 1071 1072 assertThat(mMediaSwitchingController.getHeaderSubTitle()).isNull(); 1073 } 1074 1075 @Test getHeaderSubTitle_withMetadata_returnArtistName()1076 public void getHeaderSubTitle_withMetadata_returnArtistName() { 1077 when(mSessionMediaController.getMetadata()).thenReturn(mMediaMetadata); 1078 1079 mMediaSwitchingController.start(mCb); 1080 1081 assertThat(mMediaSwitchingController.getHeaderSubTitle().equals(TEST_ARTIST)).isTrue(); 1082 } 1083 1084 @Test getActiveRemoteMediaDevices()1085 public void getActiveRemoteMediaDevices() { 1086 when(mRemoteSessionInfo.getId()).thenReturn(TEST_SESSION_ID); 1087 when(mRemoteSessionInfo.getName()).thenReturn(TEST_SESSION_NAME); 1088 when(mRemoteSessionInfo.getVolumeMax()).thenReturn(100); 1089 when(mRemoteSessionInfo.getVolume()).thenReturn(10); 1090 when(mRemoteSessionInfo.isSystemSession()).thenReturn(false); 1091 mRoutingSessionInfos.add(mRemoteSessionInfo); 1092 when(mLocalMediaManager.getRemoteRoutingSessions()).thenReturn(mRoutingSessionInfos); 1093 1094 assertThat(mMediaSwitchingController.getActiveRemoteMediaDevices()) 1095 .containsExactly(mRemoteSessionInfo); 1096 } 1097 1098 @Test getGroupMediaDevices_differentDeviceOrder_showingSameOrder()1099 public void getGroupMediaDevices_differentDeviceOrder_showingSameOrder() { 1100 final MediaDevice selectedMediaDevice1 = mock(MediaDevice.class); 1101 final MediaDevice selectedMediaDevice2 = mock(MediaDevice.class); 1102 final MediaDevice selectableMediaDevice1 = mock(MediaDevice.class); 1103 final MediaDevice selectableMediaDevice2 = mock(MediaDevice.class); 1104 final List<MediaDevice> selectedMediaDevices = new ArrayList<>(); 1105 final List<MediaDevice> selectableMediaDevices = new ArrayList<>(); 1106 when(selectedMediaDevice1.getId()).thenReturn(TEST_DEVICE_1_ID); 1107 when(selectedMediaDevice2.getId()).thenReturn(TEST_DEVICE_2_ID); 1108 when(selectableMediaDevice1.getId()).thenReturn(TEST_DEVICE_3_ID); 1109 when(selectableMediaDevice2.getId()).thenReturn(TEST_DEVICE_4_ID); 1110 selectedMediaDevices.add(selectedMediaDevice1); 1111 selectedMediaDevices.add(selectedMediaDevice2); 1112 selectableMediaDevices.add(selectableMediaDevice1); 1113 selectableMediaDevices.add(selectableMediaDevice2); 1114 doReturn(selectedMediaDevices).when(mLocalMediaManager).getSelectedMediaDevice(); 1115 doReturn(selectableMediaDevices).when(mLocalMediaManager).getSelectableMediaDevice(); 1116 final List<MediaDevice> groupMediaDevices = 1117 mMediaSwitchingController.getGroupMediaDevices(); 1118 // Reset order 1119 selectedMediaDevices.clear(); 1120 selectedMediaDevices.add(selectedMediaDevice2); 1121 selectedMediaDevices.add(selectedMediaDevice1); 1122 selectableMediaDevices.clear(); 1123 selectableMediaDevices.add(selectableMediaDevice2); 1124 selectableMediaDevices.add(selectableMediaDevice1); 1125 final List<MediaDevice> newDevices = mMediaSwitchingController.getGroupMediaDevices(); 1126 1127 assertThat(newDevices.size()).isEqualTo(groupMediaDevices.size()); 1128 for (int i = 0; i < groupMediaDevices.size(); i++) { 1129 assertThat(TextUtils.equals(groupMediaDevices.get(i).getId(), 1130 newDevices.get(i).getId())).isTrue(); 1131 } 1132 } 1133 1134 @Test getGroupMediaDevices_newDevice_verifyDeviceOrder()1135 public void getGroupMediaDevices_newDevice_verifyDeviceOrder() { 1136 final MediaDevice selectedMediaDevice1 = mock(MediaDevice.class); 1137 final MediaDevice selectedMediaDevice2 = mock(MediaDevice.class); 1138 final MediaDevice selectableMediaDevice1 = mock(MediaDevice.class); 1139 final MediaDevice selectableMediaDevice2 = mock(MediaDevice.class); 1140 final MediaDevice selectableMediaDevice3 = mock(MediaDevice.class); 1141 final List<MediaDevice> selectedMediaDevices = new ArrayList<>(); 1142 final List<MediaDevice> selectableMediaDevices = new ArrayList<>(); 1143 when(selectedMediaDevice1.getId()).thenReturn(TEST_DEVICE_1_ID); 1144 when(selectedMediaDevice2.getId()).thenReturn(TEST_DEVICE_2_ID); 1145 when(selectableMediaDevice1.getId()).thenReturn(TEST_DEVICE_3_ID); 1146 when(selectableMediaDevice2.getId()).thenReturn(TEST_DEVICE_4_ID); 1147 when(selectableMediaDevice3.getId()).thenReturn(TEST_DEVICE_5_ID); 1148 selectedMediaDevices.add(selectedMediaDevice1); 1149 selectedMediaDevices.add(selectedMediaDevice2); 1150 selectableMediaDevices.add(selectableMediaDevice1); 1151 selectableMediaDevices.add(selectableMediaDevice2); 1152 doReturn(selectedMediaDevices).when(mLocalMediaManager).getSelectedMediaDevice(); 1153 doReturn(selectableMediaDevices).when(mLocalMediaManager).getSelectableMediaDevice(); 1154 final List<MediaDevice> groupMediaDevices = 1155 mMediaSwitchingController.getGroupMediaDevices(); 1156 // Reset order 1157 selectedMediaDevices.clear(); 1158 selectedMediaDevices.add(selectedMediaDevice2); 1159 selectedMediaDevices.add(selectedMediaDevice1); 1160 selectableMediaDevices.clear(); 1161 selectableMediaDevices.add(selectableMediaDevice3); 1162 selectableMediaDevices.add(selectableMediaDevice2); 1163 selectableMediaDevices.add(selectableMediaDevice1); 1164 final List<MediaDevice> newDevices = mMediaSwitchingController.getGroupMediaDevices(); 1165 1166 assertThat(newDevices.size()).isEqualTo(5); 1167 for (int i = 0; i < groupMediaDevices.size(); i++) { 1168 assertThat(TextUtils.equals(groupMediaDevices.get(i).getId(), 1169 newDevices.get(i).getId())).isTrue(); 1170 } 1171 assertThat(newDevices.get(4).getId()).isEqualTo(TEST_DEVICE_5_ID); 1172 } 1173 1174 @Test getNotificationLargeIcon_withoutPackageName_returnsNull()1175 public void getNotificationLargeIcon_withoutPackageName_returnsNull() { 1176 mMediaSwitchingController = 1177 new MediaSwitchingController( 1178 mSpyContext, 1179 null, 1180 mSpyContext.getUser(), 1181 /* token */ null, 1182 mMediaSessionManager, 1183 mLocalBluetoothManager, 1184 mStarter, 1185 mNotifCollection, 1186 mDialogTransitionAnimator, 1187 mNearbyMediaDevicesManager, 1188 mAudioManager, 1189 mPowerExemptionManager, 1190 mKeyguardManager, 1191 mFlags, 1192 mVolumePanelGlobalStateInteractor, 1193 mUserTracker); 1194 1195 assertThat(mMediaSwitchingController.getNotificationIcon()).isNull(); 1196 } 1197 1198 @Test getNotificationLargeIcon_withoutLargeIcon_returnsNull()1199 public void getNotificationLargeIcon_withoutLargeIcon_returnsNull() { 1200 final List<NotificationEntry> entryList = new ArrayList<>(); 1201 final NotificationEntry entry = mock(NotificationEntry.class); 1202 final StatusBarNotification sbn = mock(StatusBarNotification.class); 1203 final Notification notification = mock(Notification.class); 1204 entryList.add(entry); 1205 1206 when(mNotifCollection.getAllNotifs()).thenReturn(entryList); 1207 when(entry.getSbn()).thenReturn(sbn); 1208 when(sbn.getNotification()).thenReturn(notification); 1209 when(sbn.getPackageName()).thenReturn(mPackageName); 1210 when(notification.isMediaNotification()).thenReturn(true); 1211 when(notification.getLargeIcon()).thenReturn(null); 1212 1213 assertThat(mMediaSwitchingController.getNotificationIcon()).isNull(); 1214 } 1215 1216 @Test getNotificationLargeIcon_withPackageNameAndMediaSession_returnsIconCompat()1217 public void getNotificationLargeIcon_withPackageNameAndMediaSession_returnsIconCompat() { 1218 final List<NotificationEntry> entryList = new ArrayList<>(); 1219 final NotificationEntry entry = mock(NotificationEntry.class); 1220 final StatusBarNotification sbn = mock(StatusBarNotification.class); 1221 final Notification notification = mock(Notification.class); 1222 final Icon icon = mock(Icon.class); 1223 entryList.add(entry); 1224 1225 when(mNotifCollection.getAllNotifs()).thenReturn(entryList); 1226 when(entry.getSbn()).thenReturn(sbn); 1227 when(sbn.getNotification()).thenReturn(notification); 1228 when(sbn.getPackageName()).thenReturn(mPackageName); 1229 when(notification.isMediaNotification()).thenReturn(true); 1230 when(notification.getLargeIcon()).thenReturn(icon); 1231 1232 assertThat(mMediaSwitchingController.getNotificationIcon()).isInstanceOf(IconCompat.class); 1233 } 1234 1235 @Test getNotificationLargeIcon_withPackageNameAndNoMediaSession_returnsNull()1236 public void getNotificationLargeIcon_withPackageNameAndNoMediaSession_returnsNull() { 1237 final List<NotificationEntry> entryList = new ArrayList<>(); 1238 final NotificationEntry entry = mock(NotificationEntry.class); 1239 final StatusBarNotification sbn = mock(StatusBarNotification.class); 1240 final Notification notification = mock(Notification.class); 1241 final Icon icon = mock(Icon.class); 1242 entryList.add(entry); 1243 1244 when(mNotifCollection.getAllNotifs()).thenReturn(entryList); 1245 when(entry.getSbn()).thenReturn(sbn); 1246 when(sbn.getNotification()).thenReturn(notification); 1247 when(sbn.getPackageName()).thenReturn(mPackageName); 1248 when(notification.isMediaNotification()).thenReturn(false); 1249 when(notification.getLargeIcon()).thenReturn(icon); 1250 1251 assertThat(mMediaSwitchingController.getNotificationIcon()).isNull(); 1252 } 1253 1254 @Test getNotificationSmallIcon_withoutSmallIcon_returnsNull()1255 public void getNotificationSmallIcon_withoutSmallIcon_returnsNull() { 1256 final List<NotificationEntry> entryList = new ArrayList<>(); 1257 final NotificationEntry entry = mock(NotificationEntry.class); 1258 final StatusBarNotification sbn = mock(StatusBarNotification.class); 1259 final Notification notification = mock(Notification.class); 1260 entryList.add(entry); 1261 1262 when(mNotifCollection.getAllNotifs()).thenReturn(entryList); 1263 when(entry.getSbn()).thenReturn(sbn); 1264 when(sbn.getNotification()).thenReturn(notification); 1265 when(sbn.getPackageName()).thenReturn(mPackageName); 1266 when(notification.isMediaNotification()).thenReturn(true); 1267 when(notification.getSmallIcon()).thenReturn(null); 1268 1269 assertThat(mMediaSwitchingController.getNotificationSmallIcon()).isNull(); 1270 } 1271 1272 @Test getNotificationSmallIcon_withPackageNameAndMediaSession_returnsIconCompat()1273 public void getNotificationSmallIcon_withPackageNameAndMediaSession_returnsIconCompat() { 1274 final List<NotificationEntry> entryList = new ArrayList<>(); 1275 final NotificationEntry entry = mock(NotificationEntry.class); 1276 final StatusBarNotification sbn = mock(StatusBarNotification.class); 1277 final Notification notification = mock(Notification.class); 1278 final Icon icon = mock(Icon.class); 1279 entryList.add(entry); 1280 1281 when(mNotifCollection.getAllNotifs()).thenReturn(entryList); 1282 when(entry.getSbn()).thenReturn(sbn); 1283 when(sbn.getNotification()).thenReturn(notification); 1284 when(sbn.getPackageName()).thenReturn(mPackageName); 1285 when(notification.isMediaNotification()).thenReturn(true); 1286 when(notification.getSmallIcon()).thenReturn(icon); 1287 1288 assertThat(mMediaSwitchingController.getNotificationSmallIcon()) 1289 .isInstanceOf(IconCompat.class); 1290 } 1291 1292 @Test getDeviceIconCompat_deviceIconIsNotNull_returnsIcon()1293 public void getDeviceIconCompat_deviceIconIsNotNull_returnsIcon() { 1294 when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice2); 1295 when(mMediaDevice1.getIcon()).thenReturn(mDrawable); 1296 1297 assertThat(mMediaSwitchingController.getDeviceIconCompat(mMediaDevice1)) 1298 .isInstanceOf(IconCompat.class); 1299 } 1300 1301 @Test getDeviceIconCompat_deviceIconIsNull_returnsIcon()1302 public void getDeviceIconCompat_deviceIconIsNull_returnsIcon() { 1303 when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice2); 1304 when(mMediaDevice1.getIcon()).thenReturn(null); 1305 1306 assertThat(mMediaSwitchingController.getDeviceIconCompat(mMediaDevice1)) 1307 .isInstanceOf(IconCompat.class); 1308 } 1309 1310 @Test resetGroupMediaDevices_clearGroupDevices()1311 public void resetGroupMediaDevices_clearGroupDevices() { 1312 final MediaDevice selectedMediaDevice1 = mock(MediaDevice.class); 1313 final MediaDevice selectedMediaDevice2 = mock(MediaDevice.class); 1314 final MediaDevice selectableMediaDevice1 = mock(MediaDevice.class); 1315 final MediaDevice selectableMediaDevice2 = mock(MediaDevice.class); 1316 final List<MediaDevice> selectedMediaDevices = new ArrayList<>(); 1317 final List<MediaDevice> selectableMediaDevices = new ArrayList<>(); 1318 when(selectedMediaDevice1.getId()).thenReturn(TEST_DEVICE_1_ID); 1319 when(selectedMediaDevice2.getId()).thenReturn(TEST_DEVICE_2_ID); 1320 when(selectableMediaDevice1.getId()).thenReturn(TEST_DEVICE_3_ID); 1321 when(selectableMediaDevice2.getId()).thenReturn(TEST_DEVICE_4_ID); 1322 selectedMediaDevices.add(selectedMediaDevice1); 1323 selectedMediaDevices.add(selectedMediaDevice2); 1324 selectableMediaDevices.add(selectableMediaDevice1); 1325 selectableMediaDevices.add(selectableMediaDevice2); 1326 doReturn(selectedMediaDevices).when(mLocalMediaManager).getSelectedMediaDevice(); 1327 doReturn(selectableMediaDevices).when(mLocalMediaManager).getSelectableMediaDevice(); 1328 assertThat(mMediaSwitchingController.getGroupMediaDevices().isEmpty()).isFalse(); 1329 1330 mMediaSwitchingController.resetGroupMediaDevices(); 1331 1332 assertThat(mMediaSwitchingController.mGroupMediaDevices.isEmpty()).isTrue(); 1333 } 1334 1335 @Test isVolumeControlEnabled_isCastWithVolumeFixed_returnsFalse()1336 public void isVolumeControlEnabled_isCastWithVolumeFixed_returnsFalse() { 1337 when(mMediaDevice1.getDeviceType()).thenReturn( 1338 MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE); 1339 1340 when(mMediaDevice1.isVolumeFixed()).thenReturn(true); 1341 1342 assertThat(mMediaSwitchingController.isVolumeControlEnabled(mMediaDevice1)).isFalse(); 1343 } 1344 1345 @Test isVolumeControlEnabled_isCastWithVolumeNotFixed_returnsTrue()1346 public void isVolumeControlEnabled_isCastWithVolumeNotFixed_returnsTrue() { 1347 when(mMediaDevice1.getDeviceType()).thenReturn( 1348 MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE); 1349 1350 when(mMediaDevice1.isVolumeFixed()).thenReturn(false); 1351 1352 assertThat(mMediaSwitchingController.isVolumeControlEnabled(mMediaDevice1)).isTrue(); 1353 } 1354 1355 @Test setTemporaryAllowListExceptionIfNeeded_fromRemoteToBluetooth_addsAllowList()1356 public void setTemporaryAllowListExceptionIfNeeded_fromRemoteToBluetooth_addsAllowList() { 1357 when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice1); 1358 when(mMediaDevice1.getDeviceType()).thenReturn( 1359 MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE); 1360 when(mMediaDevice1.getFeatures()).thenReturn( 1361 ImmutableList.of(MediaRoute2Info.FEATURE_REMOTE_AUDIO_PLAYBACK)); 1362 when(mMediaDevice2.getDeviceType()).thenReturn( 1363 MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE); 1364 1365 mMediaSwitchingController.setTemporaryAllowListExceptionIfNeeded(mMediaDevice2); 1366 1367 verify(mPowerExemptionManager).addToTemporaryAllowList(anyString(), anyInt(), anyString(), 1368 anyLong()); 1369 } 1370 1371 @Test setTemporaryAllowListExceptionIfNeeded_packageNameIsNull_NoAction()1372 public void setTemporaryAllowListExceptionIfNeeded_packageNameIsNull_NoAction() { 1373 MediaSwitchingController testMediaSwitchingController = 1374 new MediaSwitchingController( 1375 mSpyContext, 1376 null, 1377 mSpyContext.getUser(), 1378 /* token */ null, 1379 mMediaSessionManager, 1380 mLocalBluetoothManager, 1381 mStarter, 1382 mNotifCollection, 1383 mDialogTransitionAnimator, 1384 mNearbyMediaDevicesManager, 1385 mAudioManager, 1386 mPowerExemptionManager, 1387 mKeyguardManager, 1388 mFlags, 1389 mVolumePanelGlobalStateInteractor, 1390 mUserTracker); 1391 1392 testMediaSwitchingController.setTemporaryAllowListExceptionIfNeeded(mMediaDevice2); 1393 1394 verify(mPowerExemptionManager, never()).addToTemporaryAllowList(anyString(), anyInt(), 1395 anyString(), 1396 anyLong()); 1397 } 1398 1399 @Test onMetadataChanged_triggersOnMetadataChanged()1400 public void onMetadataChanged_triggersOnMetadataChanged() { 1401 mMediaSwitchingController.mCallback = this.mCallback; 1402 1403 mMediaSwitchingController.mCb.onMetadataChanged(mMediaMetadata); 1404 1405 verify(mMediaSwitchingController.mCallback).onMediaChanged(); 1406 } 1407 1408 @Test onPlaybackStateChanged_updateWithNullState_onMediaStoppedOrPaused()1409 public void onPlaybackStateChanged_updateWithNullState_onMediaStoppedOrPaused() { 1410 when(mPlaybackState.getState()).thenReturn(PlaybackState.STATE_PLAYING); 1411 mMediaSwitchingController.mCallback = this.mCallback; 1412 mMediaSwitchingController.start(mCb); 1413 1414 mMediaSwitchingController.mCb.onPlaybackStateChanged(null); 1415 1416 verify(mMediaSwitchingController.mCallback).onMediaStoppedOrPaused(); 1417 } 1418 1419 @Test launchBluetoothPairing_isKeyguardLocked_dismissDialog()1420 public void launchBluetoothPairing_isKeyguardLocked_dismissDialog() { 1421 when(mDialogTransitionAnimator.createActivityTransitionController(mDialogLaunchView)) 1422 .thenReturn(mActivityTransitionAnimatorController); 1423 when(mKeyguardManager.isKeyguardLocked()).thenReturn(true); 1424 mMediaSwitchingController.mCallback = this.mCallback; 1425 1426 mMediaSwitchingController.launchBluetoothPairing(mDialogLaunchView); 1427 1428 verify(mCallback).dismissDialog(); 1429 } 1430 1431 @EnableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) 1432 @Test getSelectedMediaDevice()1433 public void getSelectedMediaDevice() { 1434 // Mock MediaDevice since none of the output media device constructor is publicly available 1435 // outside of SettingsLib package. 1436 final MediaDevice selectedOutputMediaDevice = mock(MediaDevice.class); 1437 doReturn(Collections.singletonList(selectedOutputMediaDevice)) 1438 .when(mLocalMediaManager) 1439 .getSelectedMediaDevice(); 1440 1441 // Mock selected input media device. 1442 final MediaDevice selectedInputMediaDevice = mock(MediaDevice.class); 1443 doReturn(selectedInputMediaDevice).when(mInputRouteManager).getSelectedInputDevice(); 1444 1445 List<MediaDevice> selectedMediaDevices = mMediaSwitchingController.getSelectedMediaDevice(); 1446 assertThat(selectedMediaDevices) 1447 .containsExactly(selectedOutputMediaDevice, selectedInputMediaDevice); 1448 } 1449 1450 @EnableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) 1451 @Test selectInputDevice()1452 public void selectInputDevice() throws InterruptedException { 1453 final MediaDevice inputMediaDevice = 1454 InputMediaDevice.create( 1455 mContext, 1456 TEST_DEVICE_1_ID, 1457 AudioDeviceInfo.TYPE_BUILTIN_MIC, 1458 MAX_VOLUME, 1459 CURRENT_VOLUME, 1460 VOLUME_FIXED_TRUE, 1461 PRODUCT_NAME_BUILTIN_MIC); 1462 mMediaSwitchingController.connectDevice(inputMediaDevice); 1463 1464 CountDownLatch latch = new CountDownLatch(LATCH_COUNT_DOWN_TIME_IN_SECOND); 1465 var unused = ThreadUtils.postOnBackgroundThread(latch::countDown); 1466 latch.await(LATCH_TIME_OUT_TIME_IN_SECOND, TimeUnit.SECONDS); 1467 1468 verify(mInputRouteManager, atLeastOnce()).selectDevice(inputMediaDevice); 1469 verify(mLocalMediaManager, never()).connectDevice(inputMediaDevice); 1470 } 1471 1472 @EnableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) 1473 @Test selectOutputDevice()1474 public void selectOutputDevice() throws InterruptedException { 1475 final MediaDevice outputMediaDevice = mock(MediaDevice.class); 1476 mMediaSwitchingController.connectDevice(outputMediaDevice); 1477 1478 CountDownLatch latch = new CountDownLatch(LATCH_COUNT_DOWN_TIME_IN_SECOND); 1479 var unused = ThreadUtils.postOnBackgroundThread(latch::countDown); 1480 latch.await(LATCH_TIME_OUT_TIME_IN_SECOND, TimeUnit.SECONDS); 1481 1482 verify(mInputRouteManager, never()).selectDevice(outputMediaDevice); 1483 verify(mLocalMediaManager, atLeastOnce()).connectDevice(outputMediaDevice); 1484 } 1485 1486 @Test connectDeviceButton_remoteDevice_noButton()1487 public void connectDeviceButton_remoteDevice_noButton() { 1488 when(mMediaDevice1.getFeatures()).thenReturn( 1489 ImmutableList.of(MediaRoute2Info.FEATURE_REMOTE_PLAYBACK)); 1490 when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice1); 1491 mMediaSwitchingController.start(mCb); 1492 mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); 1493 1494 List<MediaItem> resultList = mMediaSwitchingController.getMediaItemList(); 1495 1496 assertThat(getNumberOfConnectDeviceButtons(resultList)).isEqualTo(0); 1497 } 1498 1499 @Test connectDeviceButton_localDevice_hasButton()1500 public void connectDeviceButton_localDevice_hasButton() { 1501 when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice1); 1502 mMediaSwitchingController.start(mCb); 1503 mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); 1504 1505 List<MediaItem> resultList = mMediaSwitchingController.getMediaItemList(); 1506 1507 assertThat(getNumberOfConnectDeviceButtons(resultList)).isEqualTo(1); 1508 assertThat(resultList.get(resultList.size() - 1).getMediaItemType()).isEqualTo( 1509 MediaItem.MediaItemType.TYPE_PAIR_NEW_DEVICE); 1510 } 1511 1512 @Test connectDeviceButton_localDeviceButtonDisabledByParam_noButton()1513 public void connectDeviceButton_localDeviceButtonDisabledByParam_noButton() { 1514 when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice1); 1515 mMediaSwitchingController.start(mCb); 1516 mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); 1517 1518 List<MediaItem> resultList = mMediaSwitchingController.getMediaItemList( 1519 false /* addConnectDeviceButton */); 1520 1521 assertThat(getNumberOfConnectDeviceButtons(resultList)).isEqualTo(0); 1522 } 1523 1524 @DisableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) 1525 @Test connectDeviceButton_presentAtAllTimesForNonGroupOutputs()1526 public void connectDeviceButton_presentAtAllTimesForNonGroupOutputs() { 1527 mMediaSwitchingController.start(mCb); 1528 reset(mCb); 1529 1530 // Mock the selected output device. 1531 doReturn(Collections.singletonList(mMediaDevice1)) 1532 .when(mLocalMediaManager) 1533 .getSelectedMediaDevice(); 1534 1535 // Verify that there is initially one "Connect a device" button present. 1536 assertThat(getNumberOfConnectDeviceButtons( 1537 mMediaSwitchingController.getMediaItemList())).isEqualTo(1); 1538 1539 // Change the selected device, and verify that there is still one "Connect a device" button 1540 // present. 1541 doReturn(Collections.singletonList(mMediaDevice2)) 1542 .when(mLocalMediaManager) 1543 .getSelectedMediaDevice(); 1544 mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); 1545 1546 assertThat(getNumberOfConnectDeviceButtons( 1547 mMediaSwitchingController.getMediaItemList())).isEqualTo(1); 1548 } 1549 1550 @EnableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) 1551 @Test connectDeviceButton_presentAtAllTimesForNonGroupOutputs_inputRoutingEnabled()1552 public void connectDeviceButton_presentAtAllTimesForNonGroupOutputs_inputRoutingEnabled() { 1553 mMediaSwitchingController.start(mCb); 1554 reset(mCb); 1555 1556 // Mock the selected output device. 1557 doReturn(Collections.singletonList(mMediaDevice1)) 1558 .when(mLocalMediaManager) 1559 .getSelectedMediaDevice(); 1560 1561 // Mock the selected input media device. 1562 final MediaDevice selectedInputMediaDevice = mock(MediaDevice.class); 1563 doReturn(selectedInputMediaDevice).when(mInputRouteManager).getSelectedInputDevice(); 1564 1565 // Verify that there is initially one "Connect a device" button present. 1566 assertThat(getNumberOfConnectDeviceButtons( 1567 mMediaSwitchingController.getMediaItemList())).isEqualTo(1); 1568 1569 // Change the selected device, and verify that there is still one "Connect a device" button 1570 // present. 1571 doReturn(Collections.singletonList(mMediaDevice2)) 1572 .when(mLocalMediaManager) 1573 .getSelectedMediaDevice(); 1574 mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); 1575 1576 assertThat(getNumberOfConnectDeviceButtons( 1577 mMediaSwitchingController.getMediaItemList())).isEqualTo(1); 1578 } 1579 1580 @EnableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_DEVICE_GROUPING) 1581 @Test selectedDevicesAddedInSameOrder()1582 public void selectedDevicesAddedInSameOrder() { 1583 when(mLocalMediaManager.isPreferenceRouteListingExist()).thenReturn(true); 1584 doReturn(mMediaDevices) 1585 .when(mLocalMediaManager) 1586 .getSelectedMediaDevice(); 1587 mMediaSwitchingController.start(mCb); 1588 reset(mCb); 1589 mMediaSwitchingController.clearMediaItemList(); 1590 1591 mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); 1592 1593 List<MediaItem> items = mMediaSwitchingController.getMediaItemList(); 1594 assertThat(items.get(0).getMediaDevice().get()).isEqualTo(mMediaDevice1); 1595 assertThat(items.get(1).getMediaDevice().get()).isEqualTo(mMediaDevice2); 1596 } 1597 1598 @EnableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_DEVICE_GROUPING) 1599 @Test selectedDevicesAddedInSameOrderWhenRlpDoesNotExist()1600 public void selectedDevicesAddedInSameOrderWhenRlpDoesNotExist() { 1601 setUpSelectedDevicesAndOrdering(); 1602 1603 mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); 1604 1605 List<MediaDevice> devices = 1606 mMediaSwitchingController.getMediaItemList().stream() 1607 .filter(item -> item.getMediaDevice().isPresent()) 1608 .map(item -> item.getMediaDevice().orElse(null)) 1609 .collect(Collectors.toList()); 1610 assertThat(devices) 1611 .containsExactly( 1612 mMediaDevice4, 1613 mMediaDevice3, 1614 mMediaDevice5, 1615 mMediaDevice1, 1616 mMediaDevice2) 1617 .inOrder(); 1618 } 1619 1620 @DisableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_DEVICE_GROUPING) 1621 @Test selectedDevicesAddedInSortedOrderWhenRlpDoesNotExist()1622 public void selectedDevicesAddedInSortedOrderWhenRlpDoesNotExist() { 1623 setUpSelectedDevicesAndOrdering(); 1624 1625 mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); 1626 1627 List<MediaDevice> devices = 1628 mMediaSwitchingController.getMediaItemList().stream() 1629 .filter(item -> item.getMediaDevice().isPresent()) 1630 .map(item -> item.getMediaDevice().orElse(null)) 1631 .collect(Collectors.toList()); 1632 1633 assertThat(devices) 1634 .containsExactly( 1635 mMediaDevice5, 1636 mMediaDevice4, 1637 mMediaDevice3, 1638 mMediaDevice1, 1639 mMediaDevice2) 1640 .inOrder(); 1641 } 1642 setUpSelectedDevicesAndOrdering()1643 private void setUpSelectedDevicesAndOrdering() { 1644 when(mMediaDevice1.getId()).thenReturn(TEST_DEVICE_1_ID); 1645 when(mMediaDevice2.getId()).thenReturn(TEST_DEVICE_2_ID); 1646 when(mMediaDevice3.getId()).thenReturn(TEST_DEVICE_3_ID); 1647 when(mMediaDevice4.getId()).thenReturn(TEST_DEVICE_4_ID); 1648 when(mMediaDevice5.getId()).thenReturn(TEST_DEVICE_5_ID); 1649 mMediaDevices.clear(); 1650 Collections.addAll( 1651 mMediaDevices, 1652 mMediaDevice2, 1653 mMediaDevice1, 1654 mMediaDevice4, 1655 mMediaDevice3, 1656 mMediaDevice5); 1657 List<MediaDevice> selectedMediaDevices = new ArrayList<>(); 1658 Collections.addAll(selectedMediaDevices, mMediaDevice3, mMediaDevice4, mMediaDevice5); 1659 doReturn(selectedMediaDevices).when(mLocalMediaManager).getSelectedMediaDevice(); 1660 // Sort the media devices in the order they appear in the deviceOrder list 1661 List<MediaDevice> deviceOrder = new ArrayList<>(); 1662 Collections.addAll( 1663 deviceOrder, 1664 mMediaDevice1, 1665 mMediaDevice2, 1666 mMediaDevice3, 1667 mMediaDevice4, 1668 mMediaDevice5); 1669 for (int i = 0; i < deviceOrder.size(); i++) { 1670 for (int j = i + 1; j < deviceOrder.size(); j++) { 1671 when(deviceOrder.get(i).compareTo(deviceOrder.get(j))).thenReturn(-1); 1672 when(deviceOrder.get(j).compareTo(deviceOrder.get(i))).thenReturn(1); 1673 } 1674 } 1675 when(mLocalMediaManager.isPreferenceRouteListingExist()).thenReturn(false); 1676 mMediaSwitchingController.start(mCb); 1677 reset(mCb); 1678 mMediaSwitchingController.clearMediaItemList(); 1679 } 1680 1681 @DisableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_DEVICE_GROUPING) 1682 @Test selectedDevicesAddedInReverseOrder()1683 public void selectedDevicesAddedInReverseOrder() { 1684 when(mLocalMediaManager.isPreferenceRouteListingExist()).thenReturn(true); 1685 doReturn(mMediaDevices) 1686 .when(mLocalMediaManager) 1687 .getSelectedMediaDevice(); 1688 mMediaSwitchingController.start(mCb); 1689 reset(mCb); 1690 mMediaSwitchingController.clearMediaItemList(); 1691 1692 mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); 1693 1694 List<MediaItem> items = mMediaSwitchingController.getMediaItemList(); 1695 assertThat(items.get(0).getMediaDevice().get()).isEqualTo(mMediaDevice2); 1696 assertThat(items.get(1).getMediaDevice().get()).isEqualTo(mMediaDevice1); 1697 } 1698 1699 @EnableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_DEVICE_GROUPING) 1700 @Test firstSelectedDeviceIsFirstDeviceInGroupIsTrue()1701 public void firstSelectedDeviceIsFirstDeviceInGroupIsTrue() { 1702 when(mLocalMediaManager.isPreferenceRouteListingExist()).thenReturn(true); 1703 doReturn(mMediaDevices) 1704 .when(mLocalMediaManager) 1705 .getSelectedMediaDevice(); 1706 mMediaSwitchingController.start(mCb); 1707 reset(mCb); 1708 mMediaSwitchingController.clearMediaItemList(); 1709 1710 mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); 1711 1712 List<MediaItem> items = mMediaSwitchingController.getMediaItemList(); 1713 assertThat(items.get(0).isFirstDeviceInGroup()).isTrue(); 1714 assertThat(items.get(1).isFirstDeviceInGroup()).isFalse(); 1715 } 1716 1717 @EnableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_DEVICE_GROUPING) 1718 @Test deviceListUpdateWithDifferentDevices_firstSelectedDeviceIsFirstDeviceInGroup()1719 public void deviceListUpdateWithDifferentDevices_firstSelectedDeviceIsFirstDeviceInGroup() { 1720 when(mLocalMediaManager.isPreferenceRouteListingExist()).thenReturn(true); 1721 doReturn(mMediaDevices) 1722 .when(mLocalMediaManager) 1723 .getSelectedMediaDevice(); 1724 mMediaSwitchingController.start(mCb); 1725 reset(mCb); 1726 mMediaSwitchingController.clearMediaItemList(); 1727 mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); 1728 mMediaDevices.clear(); 1729 mMediaDevices.add(mMediaDevice2); 1730 mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); 1731 1732 List<MediaItem> items = mMediaSwitchingController.getMediaItemList(); 1733 assertThat(items.get(0).isFirstDeviceInGroup()).isTrue(); 1734 } 1735 getNumberOfConnectDeviceButtons(List<MediaItem> itemList)1736 private int getNumberOfConnectDeviceButtons(List<MediaItem> itemList) { 1737 int numberOfConnectDeviceButtons = 0; 1738 for (MediaItem item : itemList) { 1739 if (item.getMediaItemType() == MediaItem.MediaItemType.TYPE_PAIR_NEW_DEVICE) { 1740 numberOfConnectDeviceButtons++; 1741 } 1742 } 1743 return numberOfConnectDeviceButtons; 1744 } 1745 } 1746