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.Mockito.any; 22 import static org.mockito.Mockito.doNothing; 23 import static org.mockito.Mockito.mock; 24 import static org.mockito.Mockito.never; 25 import static org.mockito.Mockito.verify; 26 import static org.mockito.Mockito.when; 27 28 import android.app.KeyguardManager; 29 import android.content.Context; 30 import android.graphics.Bitmap; 31 import android.media.AudioManager; 32 import android.media.session.MediaController; 33 import android.media.session.MediaSessionManager; 34 import android.media.session.PlaybackState; 35 import android.os.Bundle; 36 import android.os.PowerExemptionManager; 37 import android.testing.TestableLooper; 38 import android.view.View; 39 import android.widget.Button; 40 import android.widget.ImageView; 41 import android.widget.TextView; 42 43 import androidx.core.graphics.drawable.IconCompat; 44 import androidx.test.ext.junit.runners.AndroidJUnit4; 45 import androidx.test.filters.SmallTest; 46 47 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; 48 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast; 49 import com.android.settingslib.bluetooth.LocalBluetoothManager; 50 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; 51 import com.android.settingslib.media.LocalMediaManager; 52 import com.android.systemui.SysuiTestCase; 53 import com.android.systemui.animation.DialogTransitionAnimator; 54 import com.android.systemui.broadcast.BroadcastSender; 55 import com.android.systemui.flags.FeatureFlags; 56 import com.android.systemui.media.nearby.NearbyMediaDevicesManager; 57 import com.android.systemui.plugins.ActivityStarter; 58 import com.android.systemui.res.R; 59 import com.android.systemui.settings.UserTracker; 60 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; 61 62 import org.junit.Before; 63 import org.junit.Test; 64 import org.junit.runner.RunWith; 65 66 import java.util.ArrayList; 67 import java.util.List; 68 69 @SmallTest 70 @RunWith(AndroidJUnit4.class) 71 @TestableLooper.RunWithLooper(setAsMainLooper = true) 72 public class MediaOutputBaseDialogTest extends SysuiTestCase { 73 74 private static final String TEST_PACKAGE = "test_package"; 75 76 // Mock 77 private MediaOutputBaseAdapter mMediaOutputBaseAdapter = mock(MediaOutputBaseAdapter.class); 78 private MediaController mMediaController = mock(MediaController.class); 79 private PlaybackState mPlaybackState = mock(PlaybackState.class); 80 private MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class); 81 private LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class); 82 private final LocalBluetoothProfileManager mLocalBluetoothProfileManager = mock( 83 LocalBluetoothProfileManager.class); 84 private final LocalBluetoothLeBroadcast mLocalBluetoothLeBroadcast = mock( 85 LocalBluetoothLeBroadcast.class); 86 private ActivityStarter mStarter = mock(ActivityStarter.class); 87 private BroadcastSender mBroadcastSender = mock(BroadcastSender.class); 88 private final CommonNotifCollection mNotifCollection = mock(CommonNotifCollection.class); 89 private NearbyMediaDevicesManager mNearbyMediaDevicesManager = mock( 90 NearbyMediaDevicesManager.class); 91 private final DialogTransitionAnimator mDialogTransitionAnimator = mock( 92 DialogTransitionAnimator.class); 93 private final AudioManager mAudioManager = mock(AudioManager.class); 94 private PowerExemptionManager mPowerExemptionManager = mock(PowerExemptionManager.class); 95 private KeyguardManager mKeyguardManager = mock(KeyguardManager.class); 96 private FeatureFlags mFlags = mock(FeatureFlags.class); 97 private UserTracker mUserTracker = mock(UserTracker.class); 98 99 private List<MediaController> mMediaControllers = new ArrayList<>(); 100 private MediaOutputBaseDialogImpl mMediaOutputBaseDialogImpl; 101 private MediaOutputController mMediaOutputController; 102 private int mHeaderIconRes; 103 private IconCompat mIconCompat; 104 private CharSequence mHeaderTitle; 105 private CharSequence mHeaderSubtitle; 106 private String mStopText; 107 private boolean mIsBroadcasting; 108 private boolean mIsBroadcastIconVisibility; 109 110 111 @Before setUp()112 public void setUp() { 113 when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager); 114 final CachedBluetoothDeviceManager cachedBluetoothDeviceManager = mock( 115 CachedBluetoothDeviceManager.class); 116 when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn( 117 cachedBluetoothDeviceManager); 118 when(cachedBluetoothDeviceManager.findDevice(any())).thenReturn(null); 119 when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(null); 120 when(mMediaController.getPlaybackState()).thenReturn(mPlaybackState); 121 when(mPlaybackState.getState()).thenReturn(PlaybackState.STATE_NONE); 122 when(mMediaController.getPackageName()).thenReturn(TEST_PACKAGE); 123 mMediaControllers.add(mMediaController); 124 when(mMediaSessionManager.getActiveSessions(any())).thenReturn(mMediaControllers); 125 126 mMediaOutputController = 127 new MediaOutputController( 128 mContext, 129 TEST_PACKAGE, 130 mContext.getUser(), 131 /* token */ null, 132 mMediaSessionManager, 133 mLocalBluetoothManager, 134 mStarter, 135 mNotifCollection, 136 mDialogTransitionAnimator, 137 mNearbyMediaDevicesManager, 138 mAudioManager, 139 mPowerExemptionManager, 140 mKeyguardManager, 141 mFlags, 142 mUserTracker); 143 144 // Using a fake package will cause routing operations to fail, so we intercept 145 // scanning-related operations. 146 mMediaOutputController.mLocalMediaManager = mock(LocalMediaManager.class); 147 doNothing().when(mMediaOutputController.mLocalMediaManager).startScan(); 148 doNothing().when(mMediaOutputController.mLocalMediaManager).stopScan(); 149 150 mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext, mBroadcastSender, 151 mMediaOutputController); 152 mMediaOutputBaseDialogImpl.onCreate(new Bundle()); 153 } 154 155 @Test refresh_withIconRes_iconIsVisible()156 public void refresh_withIconRes_iconIsVisible() { 157 mHeaderIconRes = 1; 158 mMediaOutputBaseDialogImpl.refresh(); 159 final ImageView view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById( 160 R.id.header_icon); 161 162 assertThat(view.getVisibility()).isEqualTo(View.VISIBLE); 163 } 164 165 @Test refresh_withIconCompat_iconIsVisible()166 public void refresh_withIconCompat_iconIsVisible() { 167 mIconCompat = IconCompat.createWithBitmap( 168 Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)); 169 when(mMediaOutputBaseAdapter.getController()).thenReturn(mMediaOutputController); 170 171 mMediaOutputBaseDialogImpl.refresh(); 172 final ImageView view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById( 173 R.id.header_icon); 174 175 assertThat(view.getVisibility()).isEqualTo(View.VISIBLE); 176 } 177 178 @Test refresh_noIcon_iconLayoutNotVisible()179 public void refresh_noIcon_iconLayoutNotVisible() { 180 mHeaderIconRes = 0; 181 mIconCompat = null; 182 mMediaOutputBaseDialogImpl.refresh(); 183 final ImageView view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById( 184 R.id.header_icon); 185 186 assertThat(view.getVisibility()).isEqualTo(View.GONE); 187 } 188 189 @Test refresh_broadcastIconVisibilityOff_broadcastIconLayoutNotVisible()190 public void refresh_broadcastIconVisibilityOff_broadcastIconLayoutNotVisible() { 191 mIsBroadcastIconVisibility = false; 192 193 mMediaOutputBaseDialogImpl.refresh(); 194 final ImageView view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById( 195 R.id.broadcast_icon); 196 197 assertThat(view.getVisibility()).isEqualTo(View.GONE); 198 } 199 @Test refresh_broadcastIconVisibilityOn_broadcastIconLayoutVisible()200 public void refresh_broadcastIconVisibilityOn_broadcastIconLayoutVisible() { 201 mIsBroadcastIconVisibility = true; 202 203 mMediaOutputBaseDialogImpl.refresh(); 204 final ImageView view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById( 205 R.id.broadcast_icon); 206 207 assertThat(view.getVisibility()).isEqualTo(View.VISIBLE); 208 } 209 210 @Test refresh_checkTitle()211 public void refresh_checkTitle() { 212 mHeaderTitle = "test_string"; 213 214 mMediaOutputBaseDialogImpl.refresh(); 215 final TextView titleView = mMediaOutputBaseDialogImpl.mDialogView.requireViewById( 216 R.id.header_title); 217 218 assertThat(titleView.getVisibility()).isEqualTo(View.VISIBLE); 219 assertThat(titleView.getText()).isEqualTo(mHeaderTitle); 220 } 221 222 @Test refresh_withSubtitle_checkSubtitle()223 public void refresh_withSubtitle_checkSubtitle() { 224 mHeaderSubtitle = "test_string"; 225 226 mMediaOutputBaseDialogImpl.refresh(); 227 final TextView subtitleView = mMediaOutputBaseDialogImpl.mDialogView.requireViewById( 228 R.id.header_subtitle); 229 230 assertThat(subtitleView.getVisibility()).isEqualTo(View.VISIBLE); 231 assertThat(subtitleView.getText()).isEqualTo(mHeaderSubtitle); 232 } 233 234 @Test refresh_noSubtitle_checkSubtitle()235 public void refresh_noSubtitle_checkSubtitle() { 236 mMediaOutputBaseDialogImpl.refresh(); 237 final TextView subtitleView = mMediaOutputBaseDialogImpl.mDialogView.requireViewById( 238 R.id.header_subtitle); 239 240 assertThat(subtitleView.getVisibility()).isEqualTo(View.GONE); 241 } 242 243 @Test refresh_inDragging_notUpdateAdapter()244 public void refresh_inDragging_notUpdateAdapter() { 245 when(mMediaOutputBaseAdapter.isDragging()).thenReturn(true); 246 mMediaOutputBaseDialogImpl.refresh(); 247 248 verify(mMediaOutputBaseAdapter, never()).notifyDataSetChanged(); 249 } 250 251 @Test refresh_inDragging_directSetRefreshingToFalse()252 public void refresh_inDragging_directSetRefreshingToFalse() { 253 when(mMediaOutputBaseAdapter.isDragging()).thenReturn(true); 254 mMediaOutputBaseDialogImpl.refresh(); 255 256 assertThat(mMediaOutputController.isRefreshing()).isFalse(); 257 } 258 259 @Test refresh_notInDragging_verifyUpdateAdapter()260 public void refresh_notInDragging_verifyUpdateAdapter() { 261 when(mMediaOutputBaseAdapter.getCurrentActivePosition()).thenReturn(-1); 262 when(mMediaOutputBaseAdapter.isDragging()).thenReturn(false); 263 mMediaOutputBaseDialogImpl.refresh(); 264 265 verify(mMediaOutputBaseAdapter).updateItems(); 266 } 267 268 @Test dismissDialog_closesDialogByBroadcastSender()269 public void dismissDialog_closesDialogByBroadcastSender() { 270 mMediaOutputBaseDialogImpl.dismissDialog(); 271 272 verify(mBroadcastSender).closeSystemDialogs(); 273 } 274 275 @Test whenBroadcasting_verifyLeBroadcastServiceCallBackIsRegisteredAndUnregistered()276 public void whenBroadcasting_verifyLeBroadcastServiceCallBackIsRegisteredAndUnregistered() { 277 when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( 278 mLocalBluetoothLeBroadcast); 279 mIsBroadcasting = true; 280 281 mMediaOutputBaseDialogImpl.start(); 282 verify(mLocalBluetoothLeBroadcast).registerServiceCallBack(any(), any()); 283 284 mMediaOutputBaseDialogImpl.stop(); 285 verify(mLocalBluetoothLeBroadcast).unregisterServiceCallBack(any()); 286 } 287 288 @Test 289 public void whenNotBroadcasting_verifyLeBroadcastServiceCallBackIsNotRegisteredOrUnregistered()290 whenNotBroadcasting_verifyLeBroadcastServiceCallBackIsNotRegisteredOrUnregistered() { 291 when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( 292 mLocalBluetoothLeBroadcast); 293 mIsBroadcasting = false; 294 295 mMediaOutputBaseDialogImpl.start(); 296 mMediaOutputBaseDialogImpl.stop(); 297 298 verify(mLocalBluetoothLeBroadcast, never()).registerServiceCallBack(any(), any()); 299 verify(mLocalBluetoothLeBroadcast, never()).unregisterServiceCallBack(any()); 300 } 301 302 @Test 303 public void whenNotBroadcasting_verifyLeBroadcastServiceCallBackIsUnregisteredIfProfileEnabled()304 whenNotBroadcasting_verifyLeBroadcastServiceCallBackIsUnregisteredIfProfileEnabled() { 305 when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( 306 mLocalBluetoothLeBroadcast); 307 mIsBroadcasting = true; 308 309 mMediaOutputBaseDialogImpl.start(); 310 verify(mLocalBluetoothLeBroadcast).registerServiceCallBack(any(), any()); 311 312 mIsBroadcasting = false; 313 mMediaOutputBaseDialogImpl.stop(); 314 verify(mLocalBluetoothLeBroadcast).unregisterServiceCallBack(any()); 315 } 316 317 @Test refresh_checkStopText()318 public void refresh_checkStopText() { 319 mStopText = "test_string"; 320 mMediaOutputBaseDialogImpl.refresh(); 321 final Button stop = mMediaOutputBaseDialogImpl.mDialogView.requireViewById(R.id.stop); 322 323 assertThat(stop.getText().toString()).isEqualTo(mStopText); 324 } 325 326 class MediaOutputBaseDialogImpl extends MediaOutputBaseDialog { 327 MediaOutputBaseDialogImpl(Context context, BroadcastSender broadcastSender, MediaOutputController mediaOutputController)328 MediaOutputBaseDialogImpl(Context context, BroadcastSender broadcastSender, 329 MediaOutputController mediaOutputController) { 330 super( 331 context, 332 broadcastSender, 333 mediaOutputController, /* includePlaybackAndAppMetadata */ 334 true); 335 336 mAdapter = mMediaOutputBaseAdapter; 337 } 338 339 @Override getAppSourceIcon()340 IconCompat getAppSourceIcon() { 341 return null; 342 } 343 344 @Override getHeaderIconRes()345 int getHeaderIconRes() { 346 return mHeaderIconRes; 347 } 348 349 @Override getHeaderIcon()350 IconCompat getHeaderIcon() { 351 return mIconCompat; 352 } 353 354 @Override getHeaderIconSize()355 int getHeaderIconSize() { 356 return 10; 357 } 358 359 @Override getHeaderText()360 CharSequence getHeaderText() { 361 return mHeaderTitle; 362 } 363 364 @Override getHeaderSubtitle()365 CharSequence getHeaderSubtitle() { 366 return mHeaderSubtitle; 367 } 368 369 @Override getStopButtonVisibility()370 int getStopButtonVisibility() { 371 return 0; 372 } 373 374 @Override isBroadcastSupported()375 public boolean isBroadcastSupported() { 376 return mIsBroadcasting; 377 } 378 379 @Override getStopButtonText()380 public CharSequence getStopButtonText() { 381 return mStopText; 382 } 383 384 @Override getBroadcastIconVisibility()385 public int getBroadcastIconVisibility() { 386 return mIsBroadcastIconVisibility ? View.VISIBLE : View.GONE; 387 } 388 } 389 } 390