1 /* 2 * Copyright (C) 2018 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.volume; 18 19 import static android.media.AudioManager.RINGER_MODE_NORMAL; 20 import static android.media.AudioManager.RINGER_MODE_SILENT; 21 import static android.media.AudioManager.RINGER_MODE_VIBRATE; 22 23 import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION; 24 import static com.android.systemui.volume.Events.DISMISS_REASON_UNKNOWN; 25 import static com.android.systemui.volume.Events.SHOW_REASON_UNKNOWN; 26 import static com.android.systemui.volume.VolumeDialogControllerImpl.STREAMS; 27 28 import static junit.framework.Assert.assertEquals; 29 import static junit.framework.Assert.assertNotSame; 30 import static junit.framework.Assert.assertTrue; 31 32 import static org.junit.Assume.assumeNotNull; 33 import static org.mockito.ArgumentMatchers.any; 34 import static org.mockito.ArgumentMatchers.eq; 35 import static org.mockito.Mockito.never; 36 import static org.mockito.Mockito.reset; 37 import static org.mockito.Mockito.times; 38 import static org.mockito.Mockito.verify; 39 import static org.mockito.Mockito.when; 40 41 import android.app.KeyguardManager; 42 import android.content.res.Configuration; 43 import android.media.AudioManager; 44 import android.os.SystemClock; 45 import android.testing.AndroidTestingRunner; 46 import android.testing.TestableLooper; 47 import android.util.Log; 48 import android.view.Gravity; 49 import android.view.InputDevice; 50 import android.view.MotionEvent; 51 import android.view.View; 52 import android.view.ViewGroup; 53 import android.view.accessibility.AccessibilityManager; 54 55 import androidx.test.filters.SmallTest; 56 57 import com.android.internal.jank.InteractionJankMonitor; 58 import com.android.systemui.Prefs; 59 import com.android.systemui.R; 60 import com.android.systemui.SysuiTestCase; 61 import com.android.systemui.animation.AnimatorTestRule; 62 import com.android.systemui.dump.DumpManager; 63 import com.android.systemui.flags.FakeFeatureFlags; 64 import com.android.systemui.media.dialog.MediaOutputDialogFactory; 65 import com.android.systemui.plugins.ActivityStarter; 66 import com.android.systemui.plugins.VolumeDialogController; 67 import com.android.systemui.plugins.VolumeDialogController.State; 68 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; 69 import com.android.systemui.statusbar.policy.ConfigurationController; 70 import com.android.systemui.statusbar.policy.DevicePostureController; 71 import com.android.systemui.statusbar.policy.DeviceProvisionedController; 72 import com.android.systemui.statusbar.policy.FakeConfigurationController; 73 74 import org.junit.After; 75 import org.junit.Before; 76 import org.junit.Rule; 77 import org.junit.Test; 78 import org.junit.runner.RunWith; 79 import org.mockito.ArgumentCaptor; 80 import org.mockito.Mock; 81 import org.mockito.Mockito; 82 import org.mockito.MockitoAnnotations; 83 84 import java.util.function.Predicate; 85 86 @SmallTest 87 @RunWith(AndroidTestingRunner.class) 88 @TestableLooper.RunWithLooper(setAsMainLooper = true) 89 public class VolumeDialogImplTest extends SysuiTestCase { 90 VolumeDialogImpl mDialog; 91 View mActiveRinger; 92 View mDrawerContainer; 93 View mDrawerVibrate; 94 View mDrawerMute; 95 View mDrawerNormal; 96 CaptionsToggleImageButton mODICaptionsIcon; 97 98 private TestableLooper mTestableLooper; 99 private ConfigurationController mConfigurationController; 100 private int mOriginalOrientation; 101 102 private static final String TAG = "VolumeDialogImplTest"; 103 104 @Mock 105 VolumeDialogController mVolumeDialogController; 106 @Mock 107 KeyguardManager mKeyguard; 108 @Mock 109 AccessibilityManagerWrapper mAccessibilityMgr; 110 @Mock 111 DeviceProvisionedController mDeviceProvisionedController; 112 @Mock 113 MediaOutputDialogFactory mMediaOutputDialogFactory; 114 @Mock 115 VolumePanelFactory mVolumePanelFactory; 116 @Mock 117 ActivityStarter mActivityStarter; 118 @Mock 119 InteractionJankMonitor mInteractionJankMonitor; 120 @Mock 121 private DumpManager mDumpManager; 122 @Mock CsdWarningDialog mCsdWarningDialog; 123 @Mock 124 DevicePostureController mPostureController; 125 126 private final CsdWarningDialog.Factory mCsdWarningDialogFactory = 127 new CsdWarningDialog.Factory() { 128 @Override 129 public CsdWarningDialog create(int warningType, Runnable onCleanup) { 130 return mCsdWarningDialog; 131 } 132 }; 133 134 private FakeFeatureFlags mFeatureFlags; 135 private int mLongestHideShowAnimationDuration = 250; 136 137 @Rule 138 public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(); 139 140 @Before setup()141 public void setup() throws Exception { 142 MockitoAnnotations.initMocks(this); 143 144 getContext().addMockSystemService(KeyguardManager.class, mKeyguard); 145 146 mTestableLooper = TestableLooper.get(this); 147 allowTestableLooperAsMainThread(); 148 149 when(mPostureController.getDevicePosture()) 150 .thenReturn(DevicePostureController.DEVICE_POSTURE_CLOSED); 151 152 int hideDialogDuration = mContext.getResources() 153 .getInteger(R.integer.config_dialogHideAnimationDurationMs); 154 int showDialogDuration = mContext.getResources() 155 .getInteger(R.integer.config_dialogShowAnimationDurationMs); 156 157 mLongestHideShowAnimationDuration = Math.max(hideDialogDuration, showDialogDuration); 158 159 mOriginalOrientation = mContext.getResources().getConfiguration().orientation; 160 161 mConfigurationController = new FakeConfigurationController(); 162 163 mFeatureFlags = new FakeFeatureFlags(); 164 165 mDialog = new VolumeDialogImpl( 166 getContext(), 167 mVolumeDialogController, 168 mAccessibilityMgr, 169 mDeviceProvisionedController, 170 mConfigurationController, 171 mMediaOutputDialogFactory, 172 mVolumePanelFactory, 173 mActivityStarter, 174 mInteractionJankMonitor, 175 false, 176 mCsdWarningDialogFactory, 177 mPostureController, 178 mTestableLooper.getLooper(), 179 mDumpManager, 180 mFeatureFlags); 181 mDialog.init(0, null); 182 State state = createShellState(); 183 mDialog.onStateChangedH(state); 184 185 mActiveRinger = mDialog.getDialogView().findViewById( 186 R.id.volume_new_ringer_active_icon_container); 187 mDrawerContainer = mDialog.getDialogView().findViewById(R.id.volume_drawer_container); 188 mDrawerVibrate = mDrawerContainer.findViewById(R.id.volume_drawer_vibrate); 189 mDrawerMute = mDrawerContainer.findViewById(R.id.volume_drawer_mute); 190 mDrawerNormal = mDrawerContainer.findViewById(R.id.volume_drawer_normal); 191 mODICaptionsIcon = mDialog.getDialogView().findViewById(R.id.odi_captions_icon); 192 193 Prefs.putInt(mContext, 194 Prefs.Key.SEEN_RINGER_GUIDANCE_COUNT, 195 VolumePrefs.SHOW_RINGER_TOAST_COUNT + 1); 196 197 Prefs.putBoolean(mContext, Prefs.Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP, false); 198 } 199 createShellState()200 private State createShellState() { 201 State state = new VolumeDialogController.State(); 202 for (int i = AudioManager.STREAM_VOICE_CALL; i <= AudioManager.STREAM_ACCESSIBILITY; i++) { 203 VolumeDialogController.StreamState ss = new VolumeDialogController.StreamState(); 204 ss.name = STREAMS.get(i); 205 ss.level = 1; 206 state.states.append(i, ss); 207 } 208 return state; 209 } 210 navigateViews(View view, Predicate<View> condition)211 private void navigateViews(View view, Predicate<View> condition) { 212 if (view instanceof ViewGroup) { 213 ViewGroup viewGroup = (ViewGroup) view; 214 for (int i = 0; i < viewGroup.getChildCount(); i++) { 215 navigateViews(viewGroup.getChildAt(i), condition); 216 } 217 } else { 218 String resourceName = null; 219 try { 220 resourceName = getContext().getResources().getResourceName(view.getId()); 221 } catch (Exception e) {} 222 assertTrue("View " + resourceName != null ? resourceName : view.getId() 223 + " failed test", condition.test(view)); 224 } 225 } 226 227 @Test testComputeTimeout()228 public void testComputeTimeout() { 229 Mockito.reset(mAccessibilityMgr); 230 mDialog.rescheduleTimeoutH(); 231 verify(mAccessibilityMgr).getRecommendedTimeoutMillis( 232 VolumeDialogImpl.DIALOG_TIMEOUT_MILLIS, 233 AccessibilityManager.FLAG_CONTENT_CONTROLS); 234 } 235 236 @Test testComputeTimeout_tooltip()237 public void testComputeTimeout_tooltip() { 238 Mockito.reset(mAccessibilityMgr); 239 mDialog.showCaptionsTooltip(); 240 verify(mAccessibilityMgr).getRecommendedTimeoutMillis( 241 VolumeDialogImpl.DIALOG_ODI_CAPTIONS_TOOLTIP_TIMEOUT_MILLIS, 242 AccessibilityManager.FLAG_CONTENT_CONTROLS 243 | AccessibilityManager.FLAG_CONTENT_TEXT); 244 } 245 246 @Test testComputeTimeout_withHovering()247 public void testComputeTimeout_withHovering() { 248 Mockito.reset(mAccessibilityMgr); 249 View dialog = mDialog.getDialogView(); 250 long uptimeMillis = SystemClock.uptimeMillis(); 251 MotionEvent event = MotionEvent.obtain(uptimeMillis, uptimeMillis, 252 MotionEvent.ACTION_HOVER_ENTER, 0, 0, 0); 253 event.setSource(InputDevice.SOURCE_TOUCHSCREEN); 254 dialog.dispatchGenericMotionEvent(event); 255 event.recycle(); 256 verify(mAccessibilityMgr).getRecommendedTimeoutMillis( 257 VolumeDialogImpl.DIALOG_HOVERING_TIMEOUT_MILLIS, 258 AccessibilityManager.FLAG_CONTENT_CONTROLS); 259 } 260 261 @Test testComputeTimeout_withSafetyWarningOn()262 public void testComputeTimeout_withSafetyWarningOn() { 263 Mockito.reset(mAccessibilityMgr); 264 ArgumentCaptor<VolumeDialogController.Callbacks> controllerCallbackCapture = 265 ArgumentCaptor.forClass(VolumeDialogController.Callbacks.class); 266 verify(mVolumeDialogController).addCallback(controllerCallbackCapture.capture(), any()); 267 VolumeDialogController.Callbacks callbacks = controllerCallbackCapture.getValue(); 268 269 callbacks.onShowSafetyWarning(AudioManager.FLAG_SHOW_UI); 270 verify(mAccessibilityMgr).getRecommendedTimeoutMillis( 271 VolumeDialogImpl.DIALOG_SAFETYWARNING_TIMEOUT_MILLIS, 272 AccessibilityManager.FLAG_CONTENT_TEXT 273 | AccessibilityManager.FLAG_CONTENT_CONTROLS); 274 } 275 276 @Test testComputeTimeout_standard()277 public void testComputeTimeout_standard() { 278 Mockito.reset(mAccessibilityMgr); 279 mDialog.tryToRemoveCaptionsTooltip(); 280 mDialog.rescheduleTimeoutH(); 281 verify(mAccessibilityMgr).getRecommendedTimeoutMillis( 282 VolumeDialogImpl.DIALOG_TIMEOUT_MILLIS, 283 AccessibilityManager.FLAG_CONTENT_CONTROLS); 284 } 285 286 @Test testVibrateOnRingerChangedToVibrate()287 public void testVibrateOnRingerChangedToVibrate() { 288 mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false); 289 final State initialSilentState = new State(); 290 initialSilentState.ringerModeInternal = AudioManager.RINGER_MODE_SILENT; 291 292 final State vibrateState = new State(); 293 vibrateState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE; 294 295 // change ringer to silent 296 mDialog.onStateChangedH(initialSilentState); 297 298 // expected: shouldn't call vibrate yet 299 verify(mVolumeDialogController, never()).vibrate(any()); 300 301 // changed ringer to vibrate 302 mDialog.onStateChangedH(vibrateState); 303 304 // expected: vibrate device 305 verify(mVolumeDialogController).vibrate(any()); 306 } 307 308 @Test testControllerDoesNotVibrateOnRingerChangedToVibrate_OnewayAPI_On()309 public void testControllerDoesNotVibrateOnRingerChangedToVibrate_OnewayAPI_On() { 310 mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true); 311 final State initialSilentState = new State(); 312 initialSilentState.ringerModeInternal = AudioManager.RINGER_MODE_SILENT; 313 314 final State vibrateState = new State(); 315 vibrateState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE; 316 317 // change ringer to silent 318 mDialog.onStateChangedH(initialSilentState); 319 320 // expected: shouldn't call vibrate yet 321 verify(mVolumeDialogController, never()).vibrate(any()); 322 323 // changed ringer to vibrate 324 mDialog.onStateChangedH(vibrateState); 325 326 // expected: vibrate method of controller is not used 327 verify(mVolumeDialogController, never()).vibrate(any()); 328 } 329 330 @Test testNoVibrateOnRingerInitialization()331 public void testNoVibrateOnRingerInitialization() { 332 mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false); 333 final State initialUnsetState = new State(); 334 initialUnsetState.ringerModeInternal = -1; 335 336 // ringer not initialized yet: 337 mDialog.onStateChangedH(initialUnsetState); 338 339 final State vibrateState = new State(); 340 vibrateState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE; 341 342 // changed ringer to vibrate 343 mDialog.onStateChangedH(vibrateState); 344 345 // shouldn't call vibrate 346 verify(mVolumeDialogController, never()).vibrate(any()); 347 } 348 349 @Test testControllerDoesNotVibrateOnRingerInitialization_OnewayAPI_On()350 public void testControllerDoesNotVibrateOnRingerInitialization_OnewayAPI_On() { 351 mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true); 352 final State initialUnsetState = new State(); 353 initialUnsetState.ringerModeInternal = -1; 354 355 // ringer not initialized yet: 356 mDialog.onStateChangedH(initialUnsetState); 357 358 final State vibrateState = new State(); 359 vibrateState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE; 360 361 // changed ringer to vibrate 362 mDialog.onStateChangedH(vibrateState); 363 364 // shouldn't call vibrate on the controller either 365 verify(mVolumeDialogController, never()).vibrate(any()); 366 } 367 368 @Test testSelectVibrateFromDrawer()369 public void testSelectVibrateFromDrawer() { 370 mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false); 371 final State initialUnsetState = new State(); 372 initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL; 373 mDialog.onStateChangedH(initialUnsetState); 374 375 mActiveRinger.performClick(); 376 mDrawerVibrate.performClick(); 377 378 // Make sure we've actually changed the ringer mode. 379 verify(mVolumeDialogController, times(1)).setRingerMode( 380 AudioManager.RINGER_MODE_VIBRATE, false); 381 } 382 383 @Test testSelectVibrateFromDrawer_OnewayAPI_On()384 public void testSelectVibrateFromDrawer_OnewayAPI_On() { 385 mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true); 386 final State initialUnsetState = new State(); 387 initialUnsetState.ringerModeInternal = RINGER_MODE_NORMAL; 388 mDialog.onStateChangedH(initialUnsetState); 389 390 mActiveRinger.performClick(); 391 mDrawerVibrate.performClick(); 392 393 // Make sure we've actually changed the ringer mode. 394 verify(mVolumeDialogController, times(1)).setRingerMode( 395 AudioManager.RINGER_MODE_VIBRATE, false); 396 } 397 398 @Test testSelectMuteFromDrawer()399 public void testSelectMuteFromDrawer() { 400 mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false); 401 final State initialUnsetState = new State(); 402 initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL; 403 mDialog.onStateChangedH(initialUnsetState); 404 405 mActiveRinger.performClick(); 406 mDrawerMute.performClick(); 407 408 // Make sure we've actually changed the ringer mode. 409 verify(mVolumeDialogController, times(1)).setRingerMode( 410 AudioManager.RINGER_MODE_SILENT, false); 411 } 412 413 @Test testSelectMuteFromDrawer_OnewayAPI_On()414 public void testSelectMuteFromDrawer_OnewayAPI_On() { 415 mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true); 416 final State initialUnsetState = new State(); 417 initialUnsetState.ringerModeInternal = RINGER_MODE_NORMAL; 418 mDialog.onStateChangedH(initialUnsetState); 419 420 mActiveRinger.performClick(); 421 mDrawerMute.performClick(); 422 423 // Make sure we've actually changed the ringer mode. 424 verify(mVolumeDialogController, times(1)).setRingerMode( 425 AudioManager.RINGER_MODE_SILENT, false); 426 } 427 428 @Test testSelectNormalFromDrawer()429 public void testSelectNormalFromDrawer() { 430 mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false); 431 final State initialUnsetState = new State(); 432 initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE; 433 mDialog.onStateChangedH(initialUnsetState); 434 435 mActiveRinger.performClick(); 436 mDrawerNormal.performClick(); 437 438 // Make sure we've actually changed the ringer mode. 439 verify(mVolumeDialogController, times(1)).setRingerMode( 440 AudioManager.RINGER_MODE_NORMAL, false); 441 } 442 443 @Test testSelectNormalFromDrawer_OnewayAPI_On()444 public void testSelectNormalFromDrawer_OnewayAPI_On() { 445 mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true); 446 final State initialUnsetState = new State(); 447 initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE; 448 mDialog.onStateChangedH(initialUnsetState); 449 450 mActiveRinger.performClick(); 451 mDrawerNormal.performClick(); 452 453 // Make sure we've actually changed the ringer mode. 454 verify(mVolumeDialogController, times(1)).setRingerMode( 455 RINGER_MODE_NORMAL, false); 456 } 457 458 /** 459 * Ideally we would look at the ringer ImageView and check its assigned drawable id, but that 460 * API does not exist. So we do the next best thing; we check the cached icon id. 461 */ 462 @Test notificationVolumeSeparated_theRingerIconChangesToSpeakerIcon()463 public void notificationVolumeSeparated_theRingerIconChangesToSpeakerIcon() { 464 // already separated. assert icon is new based on res id 465 assertEquals(mDialog.mVolumeRingerIconDrawableId, 466 R.drawable.ic_speaker_on); 467 assertEquals(mDialog.mVolumeRingerMuteIconDrawableId, 468 R.drawable.ic_speaker_mute); 469 } 470 471 @Test testDialogDismissAnimation_notifyVisibleIsNotCalledBeforeAnimation()472 public void testDialogDismissAnimation_notifyVisibleIsNotCalledBeforeAnimation() { 473 mDialog.dismissH(DISMISS_REASON_UNKNOWN); 474 // notifyVisible(false) should not be called immediately but only after the dismiss 475 // animation has ended. 476 verify(mVolumeDialogController, times(0)).notifyVisible(false); 477 mDialog.getDialogView().animate().cancel(); 478 } 479 480 @Test showCsdWarning_dialogShown()481 public void showCsdWarning_dialogShown() { 482 mDialog.showCsdWarningH(AudioManager.CSD_WARNING_DOSE_REACHED_1X, 483 CsdWarningDialog.NO_ACTION_TIMEOUT_MS); 484 485 verify(mCsdWarningDialog).show(); 486 } 487 488 @Test ifPortraitHalfOpen_drawVerticallyTop()489 public void ifPortraitHalfOpen_drawVerticallyTop() { 490 mDialog.onPostureChanged(DevicePostureController.DEVICE_POSTURE_HALF_OPENED); 491 mTestableLooper.processAllMessages(); // let dismiss() finish 492 493 setOrientation(Configuration.ORIENTATION_PORTRAIT); 494 495 // Call show() to trigger layout updates before verifying position 496 mDialog.show(SHOW_REASON_UNKNOWN); 497 mTestableLooper.processAllMessages(); // let show() finish before assessing its side-effect 498 499 int gravity = mDialog.getWindowGravity(); 500 assertEquals(Gravity.TOP, gravity & Gravity.VERTICAL_GRAVITY_MASK); 501 } 502 503 @Test ifPortraitAndOpen_drawCenterVertically()504 public void ifPortraitAndOpen_drawCenterVertically() { 505 mDialog.onPostureChanged(DevicePostureController.DEVICE_POSTURE_OPENED); 506 mTestableLooper.processAllMessages(); // let dismiss() finish 507 508 setOrientation(Configuration.ORIENTATION_PORTRAIT); 509 510 mDialog.show(SHOW_REASON_UNKNOWN); 511 mTestableLooper.processAllMessages(); // let show() finish before assessing its side-effect 512 513 int gravity = mDialog.getWindowGravity(); 514 assertEquals(Gravity.CENTER_VERTICAL, gravity & Gravity.VERTICAL_GRAVITY_MASK); 515 } 516 517 @Test ifLandscapeAndHalfOpen_drawCenterVertically()518 public void ifLandscapeAndHalfOpen_drawCenterVertically() { 519 mDialog.onPostureChanged(DevicePostureController.DEVICE_POSTURE_HALF_OPENED); 520 mTestableLooper.processAllMessages(); // let dismiss() finish 521 522 setOrientation(Configuration.ORIENTATION_LANDSCAPE); 523 524 mDialog.show(SHOW_REASON_UNKNOWN); 525 mTestableLooper.processAllMessages(); // let show() finish before assessing its side-effect 526 527 int gravity = mDialog.getWindowGravity(); 528 assertEquals(Gravity.CENTER_VERTICAL, gravity & Gravity.VERTICAL_GRAVITY_MASK); 529 } 530 531 @Test dialogInit_addsPostureControllerCallback()532 public void dialogInit_addsPostureControllerCallback() { 533 // init is already called in setup 534 verify(mPostureController).addCallback(any()); 535 } 536 537 @Test dialogDestroy_removesPostureControllerCallback()538 public void dialogDestroy_removesPostureControllerCallback() { 539 verify(mPostureController, never()).removeCallback(any()); 540 mDialog.destroy(); 541 verify(mPostureController).removeCallback(any()); 542 } 543 setOrientation(int orientation)544 private void setOrientation(int orientation) { 545 Configuration config = new Configuration(); 546 config.orientation = orientation; 547 if (mConfigurationController != null) { 548 mConfigurationController.onConfigurationChanged(config); 549 } 550 } 551 552 private enum RingerDrawerState {INIT, OPEN, CLOSE} 553 554 @Test ringerModeNormal_ringerContainerDescribesItsState()555 public void ringerModeNormal_ringerContainerDescribesItsState() { 556 assertRingerContainerDescribesItsState(RINGER_MODE_NORMAL, RingerDrawerState.INIT); 557 } 558 559 @Test ringerModeSilent_ringerContainerDescribesItsState()560 public void ringerModeSilent_ringerContainerDescribesItsState() { 561 assertRingerContainerDescribesItsState(RINGER_MODE_SILENT, RingerDrawerState.INIT); 562 } 563 564 @Test ringerModeVibrate_ringerContainerDescribesItsState()565 public void ringerModeVibrate_ringerContainerDescribesItsState() { 566 assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.INIT); 567 } 568 569 @Test ringerModeNormal_openDrawer_ringerContainerDescribesItsState()570 public void ringerModeNormal_openDrawer_ringerContainerDescribesItsState() { 571 assertRingerContainerDescribesItsState(RINGER_MODE_NORMAL, RingerDrawerState.OPEN); 572 } 573 574 @Test ringerModeSilent_openDrawer_ringerContainerDescribesItsState()575 public void ringerModeSilent_openDrawer_ringerContainerDescribesItsState() { 576 assertRingerContainerDescribesItsState(RINGER_MODE_SILENT, RingerDrawerState.OPEN); 577 } 578 579 @Test ringerModeVibrate_openDrawer_ringerContainerDescribesItsState()580 public void ringerModeVibrate_openDrawer_ringerContainerDescribesItsState() { 581 assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.OPEN); 582 } 583 584 @Test ringerModeNormal_closeDrawer_ringerContainerDescribesItsState()585 public void ringerModeNormal_closeDrawer_ringerContainerDescribesItsState() { 586 assertRingerContainerDescribesItsState(RINGER_MODE_NORMAL, RingerDrawerState.CLOSE); 587 } 588 589 @Test ringerModeSilent_closeDrawer_ringerContainerDescribesItsState()590 public void ringerModeSilent_closeDrawer_ringerContainerDescribesItsState() { 591 assertRingerContainerDescribesItsState(RINGER_MODE_SILENT, RingerDrawerState.CLOSE); 592 } 593 594 @Test ringerModeVibrate_closeDrawer_ringerContainerDescribesItsState()595 public void ringerModeVibrate_closeDrawer_ringerContainerDescribesItsState() { 596 assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.CLOSE); 597 } 598 599 @Test testOnCaptionEnabledStateChanged_checkBeforeSwitchTrue_setCaptionsEnabledState()600 public void testOnCaptionEnabledStateChanged_checkBeforeSwitchTrue_setCaptionsEnabledState() { 601 ArgumentCaptor<VolumeDialogController.Callbacks> controllerCallbackCapture = 602 ArgumentCaptor.forClass(VolumeDialogController.Callbacks.class); 603 verify(mVolumeDialogController).addCallback(controllerCallbackCapture.capture(), any()); 604 VolumeDialogController.Callbacks callbacks = controllerCallbackCapture.getValue(); 605 606 callbacks.onCaptionEnabledStateChanged(true, true); 607 verify(mVolumeDialogController).setCaptionsEnabledState(eq(false)); 608 } 609 610 @Test testOnCaptionEnabledStateChanged_checkBeforeSwitchFalse_getCaptionsEnabledTrue()611 public void testOnCaptionEnabledStateChanged_checkBeforeSwitchFalse_getCaptionsEnabledTrue() { 612 ArgumentCaptor<VolumeDialogController.Callbacks> controllerCallbackCapture = 613 ArgumentCaptor.forClass(VolumeDialogController.Callbacks.class); 614 verify(mVolumeDialogController).addCallback(controllerCallbackCapture.capture(), any()); 615 VolumeDialogController.Callbacks callbacks = controllerCallbackCapture.getValue(); 616 617 callbacks.onCaptionEnabledStateChanged(true, false); 618 assertTrue(mODICaptionsIcon.getCaptionsEnabled()); 619 } 620 621 /** 622 * The content description should include ringer state, and the correct one. 623 */ assertRingerContainerDescribesItsState(int ringerMode, RingerDrawerState drawerState)624 private void assertRingerContainerDescribesItsState(int ringerMode, 625 RingerDrawerState drawerState) { 626 State state = createShellState(); 627 state.ringerModeInternal = ringerMode; 628 mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true); 629 mDialog.onStateChangedH(state); 630 631 mDialog.show(SHOW_REASON_UNKNOWN); 632 633 if (drawerState != RingerDrawerState.INIT) { 634 // in both cases we first open the drawer 635 mDialog.toggleRingerDrawer(true); 636 637 if (drawerState == RingerDrawerState.CLOSE) { 638 mDialog.toggleRingerDrawer(false); 639 } 640 } 641 642 String ringerContainerDescription = mDialog.getSelectedRingerContainerDescription(); 643 assumeNotNull(ringerContainerDescription); 644 645 String ringerDescription = mContext.getString( 646 mDialog.getStringDescriptionResourceForRingerMode(ringerMode)); 647 648 if (drawerState == RingerDrawerState.OPEN) { 649 assertEquals(ringerDescription, ringerContainerDescription); 650 } else { 651 assertNotSame(ringerDescription, ringerContainerDescription); 652 assertTrue(ringerContainerDescription.startsWith(ringerDescription)); 653 } 654 } 655 656 @After teardown()657 public void teardown() { 658 // Detailed logs to track down timeout issues in b/299491332 659 Log.d(TAG, "teardown: entered"); 660 setOrientation(mOriginalOrientation); 661 Log.d(TAG, "teardown: after setOrientation"); 662 mAnimatorTestRule.advanceTimeBy(mLongestHideShowAnimationDuration); 663 Log.d(TAG, "teardown: after advanceTimeBy"); 664 mTestableLooper.moveTimeForward(mLongestHideShowAnimationDuration); 665 Log.d(TAG, "teardown: after moveTimeForward"); 666 mTestableLooper.processAllMessages(); 667 Log.d(TAG, "teardown: after processAllMessages"); 668 reset(mPostureController); 669 Log.d(TAG, "teardown: after reset"); 670 cleanUp(mDialog); 671 Log.d(TAG, "teardown: after cleanUp"); 672 } 673 cleanUp(VolumeDialogImpl dialog)674 private void cleanUp(VolumeDialogImpl dialog) { 675 if (dialog != null) { 676 dialog.clearInternalHandlerAfterTest(); 677 } 678 } 679 680 /* 681 @Test 682 public void testContentDescriptions() { 683 mDialog.show(SHOW_REASON_UNKNOWN); 684 ViewGroup dialog = mDialog.getDialogView(); 685 686 navigateViews(dialog, view -> { 687 if (view instanceof ImageView) { 688 return !TextUtils.isEmpty(view.getContentDescription()); 689 } else { 690 return true; 691 } 692 }); 693 694 mDialog.dismiss(DISMISS_REASON_UNKNOWN); 695 } 696 697 @Test 698 public void testNoDuplicationOfParentState() { 699 mDialog.show(SHOW_REASON_UNKNOWN); 700 ViewGroup dialog = mDialog.getDialogView(); 701 702 navigateViews(dialog, view -> !view.isDuplicateParentStateEnabled()); 703 704 mDialog.dismiss(DISMISS_REASON_UNKNOWN); 705 } 706 707 @Test 708 public void testNoClickableViewGroups() { 709 mDialog.show(SHOW_REASON_UNKNOWN); 710 ViewGroup dialog = mDialog.getDialogView(); 711 712 navigateViews(dialog, view -> { 713 if (view instanceof ViewGroup) { 714 return !view.isClickable(); 715 } else { 716 return true; 717 } 718 }); 719 720 mDialog.dismiss(DISMISS_REASON_UNKNOWN); 721 } 722 723 @Test 724 public void testTristateToggle_withVibrator() { 725 when(mController.hasVibrator()).thenReturn(true); 726 727 State state = createShellState(); 728 state.ringerModeInternal = RINGER_MODE_NORMAL; 729 mDialog.onStateChangedH(state); 730 731 mDialog.show(SHOW_REASON_UNKNOWN); 732 ViewGroup dialog = mDialog.getDialogView(); 733 734 // click once, verify updates to vibrate 735 dialog.findViewById(R.id.ringer_icon).performClick(); 736 verify(mController, times(1)).setRingerMode(RINGER_MODE_VIBRATE, false); 737 738 // fake the update back to the dialog with the new ringer mode 739 state = createShellState(); 740 state.ringerModeInternal = RINGER_MODE_VIBRATE; 741 mDialog.onStateChangedH(state); 742 743 // click once, verify updates to silent 744 dialog.findViewById(R.id.ringer_icon).performClick(); 745 verify(mController, times(1)).setRingerMode(RINGER_MODE_SILENT, false); 746 verify(mController, times(1)).setStreamVolume(STREAM_RING, 0); 747 748 // fake the update back to the dialog with the new ringer mode 749 state = createShellState(); 750 state.states.get(STREAM_RING).level = 0; 751 state.ringerModeInternal = RINGER_MODE_SILENT; 752 mDialog.onStateChangedH(state); 753 754 // click once, verify updates to normal 755 dialog.findViewById(R.id.ringer_icon).performClick(); 756 verify(mController, times(1)).setRingerMode(RINGER_MODE_NORMAL, false); 757 verify(mController, times(1)).setStreamVolume(STREAM_RING, 0); 758 } 759 760 @Test 761 public void testTristateToggle_withoutVibrator() { 762 when(mController.hasVibrator()).thenReturn(false); 763 764 State state = createShellState(); 765 state.ringerModeInternal = RINGER_MODE_NORMAL; 766 mDialog.onStateChangedH(state); 767 768 mDialog.show(SHOW_REASON_UNKNOWN); 769 ViewGroup dialog = mDialog.getDialogView(); 770 771 // click once, verify updates to silent 772 dialog.findViewById(R.id.ringer_icon).performClick(); 773 verify(mController, times(1)).setRingerMode(RINGER_MODE_SILENT, false); 774 verify(mController, times(1)).setStreamVolume(STREAM_RING, 0); 775 776 // fake the update back to the dialog with the new ringer mode 777 state = createShellState(); 778 state.states.get(STREAM_RING).level = 0; 779 state.ringerModeInternal = RINGER_MODE_SILENT; 780 mDialog.onStateChangedH(state); 781 782 // click once, verify updates to normal 783 dialog.findViewById(R.id.ringer_icon).performClick(); 784 verify(mController, times(1)).setRingerMode(RINGER_MODE_NORMAL, false); 785 verify(mController, times(1)).setStreamVolume(STREAM_RING, 0); 786 } 787 */ 788 } 789