1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.ambient.touch; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.ArgumentMatchers.any; 22 import static org.mockito.ArgumentMatchers.anyFloat; 23 import static org.mockito.ArgumentMatchers.eq; 24 import static org.mockito.Mockito.never; 25 import static org.mockito.Mockito.reset; 26 import static org.mockito.Mockito.verify; 27 import static org.mockito.Mockito.verifyNoMoreInteractions; 28 import static org.mockito.Mockito.when; 29 30 import android.animation.AnimatorListenerAdapter; 31 import android.animation.ValueAnimator; 32 import android.content.pm.UserInfo; 33 import android.graphics.Rect; 34 import android.graphics.Region; 35 import android.platform.test.annotations.DisableFlags; 36 import android.platform.test.annotations.EnableFlags; 37 import android.view.GestureDetector; 38 import android.view.GestureDetector.OnGestureListener; 39 import android.view.MotionEvent; 40 import android.view.VelocityTracker; 41 42 import androidx.test.ext.junit.runners.AndroidJUnit4; 43 import androidx.test.filters.SmallTest; 44 45 import com.android.internal.logging.UiEventLogger; 46 import com.android.internal.widget.LockPatternUtils; 47 import com.android.systemui.Flags; 48 import com.android.systemui.SysuiTestCase; 49 import com.android.systemui.ambient.touch.scrim.ScrimController; 50 import com.android.systemui.ambient.touch.scrim.ScrimManager; 51 import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants; 52 import com.android.systemui.settings.FakeUserTracker; 53 import com.android.systemui.shade.ShadeExpansionChangeEvent; 54 import com.android.systemui.shared.system.InputChannelCompat; 55 import com.android.systemui.statusbar.NotificationShadeWindowController; 56 import com.android.systemui.statusbar.phone.CentralSurfaces; 57 import com.android.wm.shell.animation.FlingAnimationUtils; 58 59 import org.junit.Before; 60 import org.junit.Test; 61 import org.junit.runner.RunWith; 62 import org.mockito.ArgumentCaptor; 63 import org.mockito.Captor; 64 import org.mockito.Mock; 65 import org.mockito.Mockito; 66 import org.mockito.MockitoAnnotations; 67 68 import java.util.Collections; 69 import java.util.Optional; 70 71 @SmallTest 72 @RunWith(AndroidJUnit4.class) 73 public class BouncerSwipeTouchHandlerTest extends SysuiTestCase { 74 @Mock 75 CentralSurfaces mCentralSurfaces; 76 77 @Mock 78 ScrimManager mScrimManager; 79 80 @Mock 81 ScrimController mScrimController; 82 83 @Mock 84 NotificationShadeWindowController mNotificationShadeWindowController; 85 86 @Mock 87 FlingAnimationUtils mFlingAnimationUtils; 88 89 @Mock 90 FlingAnimationUtils mFlingAnimationUtilsClosing; 91 92 @Mock 93 TouchHandler.TouchSession mTouchSession; 94 95 BouncerSwipeTouchHandler mTouchHandler; 96 97 @Mock 98 BouncerSwipeTouchHandler.ValueAnimatorCreator mValueAnimatorCreator; 99 100 @Mock 101 ValueAnimator mValueAnimator; 102 103 @Mock 104 BouncerSwipeTouchHandler.VelocityTrackerFactory mVelocityTrackerFactory; 105 106 @Mock 107 VelocityTracker mVelocityTracker; 108 109 @Mock 110 UiEventLogger mUiEventLogger; 111 112 @Mock 113 LockPatternUtils mLockPatternUtils; 114 115 @Mock 116 Region mRegion; 117 118 @Captor 119 ArgumentCaptor<Rect> mRectCaptor; 120 121 FakeUserTracker mUserTracker; 122 123 private static final float TOUCH_REGION = .3f; 124 private static final int SCREEN_WIDTH_PX = 1024; 125 private static final int SCREEN_HEIGHT_PX = 100; 126 private static final float MIN_BOUNCER_HEIGHT = .05f; 127 128 private static final Rect SCREEN_BOUNDS = new Rect(0, 0, 1024, 100); 129 private static final UserInfo CURRENT_USER_INFO = new UserInfo( 130 10, 131 /* name= */ "user10", 132 /* flags= */ 0 133 ); 134 135 @Before setup()136 public void setup() { 137 MockitoAnnotations.initMocks(this); 138 mUserTracker = new FakeUserTracker(); 139 mTouchHandler = new BouncerSwipeTouchHandler( 140 mScrimManager, 141 Optional.of(mCentralSurfaces), 142 mNotificationShadeWindowController, 143 mValueAnimatorCreator, 144 mVelocityTrackerFactory, 145 mLockPatternUtils, 146 mUserTracker, 147 mFlingAnimationUtils, 148 mFlingAnimationUtilsClosing, 149 TOUCH_REGION, 150 MIN_BOUNCER_HEIGHT, 151 mUiEventLogger); 152 153 when(mScrimManager.getCurrentController()).thenReturn(mScrimController); 154 when(mValueAnimatorCreator.create(anyFloat(), anyFloat())).thenReturn(mValueAnimator); 155 when(mVelocityTrackerFactory.obtain()).thenReturn(mVelocityTracker); 156 when(mFlingAnimationUtils.getMinVelocityPxPerSecond()).thenReturn(Float.MAX_VALUE); 157 when(mTouchSession.getBounds()).thenReturn(SCREEN_BOUNDS); 158 when(mLockPatternUtils.isSecure(CURRENT_USER_INFO.id)).thenReturn(true); 159 160 mUserTracker.set(Collections.singletonList(CURRENT_USER_INFO), 0); 161 } 162 163 /** 164 * Ensures expansion only happens when touch down happens in valid part of the screen. 165 */ 166 @Test 167 @DisableFlags(Flags.FLAG_COMMUNAL_BOUNCER_DO_NOT_MODIFY_PLUGIN_OPEN) testSessionStart()168 public void testSessionStart() { 169 mTouchHandler.getTouchInitiationRegion(SCREEN_BOUNDS, mRegion, null); 170 171 verify(mRegion).union(mRectCaptor.capture()); 172 final Rect bounds = mRectCaptor.getValue(); 173 174 final Rect expected = new Rect(); 175 176 expected.set(0, Math.round(SCREEN_HEIGHT_PX * (1 - TOUCH_REGION)), SCREEN_WIDTH_PX, 177 SCREEN_HEIGHT_PX); 178 179 assertThat(bounds).isEqualTo(expected); 180 181 mTouchHandler.onSessionStart(mTouchSession); 182 verify(mNotificationShadeWindowController).setForcePluginOpen(eq(true), any()); 183 ArgumentCaptor<InputChannelCompat.InputEventListener> eventListenerCaptor = 184 ArgumentCaptor.forClass(InputChannelCompat.InputEventListener.class); 185 ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor = 186 ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class); 187 verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture()); 188 verify(mTouchSession).registerInputListener(eventListenerCaptor.capture()); 189 190 // A touch within range at the bottom of the screen should trigger listening 191 assertThat(gestureListenerCaptor.getValue() 192 .onScroll(Mockito.mock(MotionEvent.class), 193 Mockito.mock(MotionEvent.class), 194 1, 195 2)).isTrue(); 196 } 197 198 199 /** 200 * Ensures expansion only happens when touch down happens in valid part of the screen. 201 */ 202 @Test 203 @EnableFlags(Flags.FLAG_COMMUNAL_BOUNCER_DO_NOT_MODIFY_PLUGIN_OPEN) testSessionStart_doesNotModifyNotificationShadeWindow()204 public void testSessionStart_doesNotModifyNotificationShadeWindow() { 205 mTouchHandler.getTouchInitiationRegion(SCREEN_BOUNDS, mRegion, null); 206 207 verify(mRegion).union(mRectCaptor.capture()); 208 final Rect bounds = mRectCaptor.getValue(); 209 210 final Rect expected = new Rect(); 211 212 expected.set(0, Math.round(SCREEN_HEIGHT_PX * (1 - TOUCH_REGION)), SCREEN_WIDTH_PX, 213 SCREEN_HEIGHT_PX); 214 215 assertThat(bounds).isEqualTo(expected); 216 217 mTouchHandler.onSessionStart(mTouchSession); 218 verifyNoMoreInteractions(mNotificationShadeWindowController); 219 } 220 221 @Test 222 @DisableFlags(Flags.FLAG_COMMUNAL_BOUNCER_DO_NOT_MODIFY_PLUGIN_OPEN) testSwipeUp_whenBouncerInitiallyShowing_reduceHeightWithExclusionRects()223 public void testSwipeUp_whenBouncerInitiallyShowing_reduceHeightWithExclusionRects() { 224 mTouchHandler.getTouchInitiationRegion(SCREEN_BOUNDS, mRegion, 225 new Rect(0, 0, SCREEN_WIDTH_PX, SCREEN_HEIGHT_PX)); 226 verify(mRegion).union(mRectCaptor.capture()); 227 final Rect bounds = mRectCaptor.getValue(); 228 229 final Rect expected = new Rect(); 230 final float minBouncerHeight = 231 SCREEN_HEIGHT_PX * MIN_BOUNCER_HEIGHT; 232 final int minAllowableBottom = SCREEN_HEIGHT_PX - Math.round(minBouncerHeight); 233 234 expected.set(0, minAllowableBottom, SCREEN_WIDTH_PX, SCREEN_HEIGHT_PX); 235 236 assertThat(bounds).isEqualTo(expected); 237 238 onSessionStartHelper(mTouchHandler, mTouchSession, mNotificationShadeWindowController); 239 } 240 241 @Test 242 @DisableFlags(Flags.FLAG_COMMUNAL_BOUNCER_DO_NOT_MODIFY_PLUGIN_OPEN) testSwipeUp_exclusionRectAtTop_doesNotIntersectGestureArea()243 public void testSwipeUp_exclusionRectAtTop_doesNotIntersectGestureArea() { 244 mTouchHandler.getTouchInitiationRegion(SCREEN_BOUNDS, mRegion, 245 new Rect(0, 0, SCREEN_WIDTH_PX, SCREEN_HEIGHT_PX / 4)); 246 verify(mRegion).union(mRectCaptor.capture()); 247 final Rect bounds = mRectCaptor.getValue(); 248 249 final Rect expected = new Rect(); 250 final int gestureAreaTop = SCREEN_HEIGHT_PX - Math.round(SCREEN_HEIGHT_PX * TOUCH_REGION); 251 expected.set(0, gestureAreaTop, SCREEN_WIDTH_PX, SCREEN_HEIGHT_PX); 252 253 assertThat(bounds).isEqualTo(expected); 254 onSessionStartHelper(mTouchHandler, mTouchSession, mNotificationShadeWindowController); 255 } 256 257 @Test 258 @DisableFlags(Flags.FLAG_COMMUNAL_BOUNCER_DO_NOT_MODIFY_PLUGIN_OPEN) testSwipeUp_exclusionRectBetweenNormalAndMinimumSwipeArea()259 public void testSwipeUp_exclusionRectBetweenNormalAndMinimumSwipeArea() { 260 final int normalSwipeAreaTop = SCREEN_HEIGHT_PX 261 - Math.round(SCREEN_HEIGHT_PX * TOUCH_REGION); 262 final int minimumSwipeAreaTop = SCREEN_HEIGHT_PX 263 - Math.round(SCREEN_HEIGHT_PX * MIN_BOUNCER_HEIGHT); 264 265 Rect exclusionRect = new Rect(0, 0, SCREEN_WIDTH_PX, 266 (normalSwipeAreaTop + minimumSwipeAreaTop) / 2); 267 268 mTouchHandler.getTouchInitiationRegion(SCREEN_BOUNDS, mRegion, exclusionRect); 269 270 verify(mRegion).union(mRectCaptor.capture()); 271 272 final Rect bounds = mRectCaptor.getValue(); 273 final Rect expected = new Rect(); 274 275 final int expectedSwipeAreaBottom = exclusionRect.bottom; 276 expected.set(0, expectedSwipeAreaBottom, SCREEN_WIDTH_PX, SCREEN_HEIGHT_PX); 277 278 assertThat(bounds).isEqualTo(expected); 279 280 onSessionStartHelper(mTouchHandler, mTouchSession, mNotificationShadeWindowController); 281 } 282 onSessionStartHelper(BouncerSwipeTouchHandler touchHandler, TouchHandler.TouchSession touchSession, NotificationShadeWindowController notificationShadeWindowController)283 private static void onSessionStartHelper(BouncerSwipeTouchHandler touchHandler, 284 TouchHandler.TouchSession touchSession, 285 NotificationShadeWindowController notificationShadeWindowController) { 286 touchHandler.onSessionStart(touchSession); 287 verify(notificationShadeWindowController).setForcePluginOpen(eq(true), any()); 288 ArgumentCaptor<InputChannelCompat.InputEventListener> eventListenerCaptor = 289 ArgumentCaptor.forClass(InputChannelCompat.InputEventListener.class); 290 ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor = 291 ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class); 292 verify(touchSession).registerGestureListener(gestureListenerCaptor.capture()); 293 verify(touchSession).registerInputListener(eventListenerCaptor.capture()); 294 295 // A touch within range at the bottom of the screen should trigger listening 296 assertThat(gestureListenerCaptor.getValue() 297 .onScroll(Mockito.mock(MotionEvent.class), 298 Mockito.mock(MotionEvent.class), 299 1, 300 2)).isTrue(); 301 } 302 303 /** 304 * Makes sure swiping down doesn't change the expansion amount. 305 */ 306 @Test 307 @DisableFlags(Flags.FLAG_DREAM_OVERLAY_BOUNCER_SWIPE_DIRECTION_FILTERING) testSwipeDown_doesNotSetExpansion()308 public void testSwipeDown_doesNotSetExpansion() { 309 mTouchHandler.onSessionStart(mTouchSession); 310 ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor = 311 ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class); 312 verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture()); 313 314 final OnGestureListener gestureListener = gestureListenerCaptor.getValue(); 315 316 final float percent = .15f; 317 final float distanceY = SCREEN_HEIGHT_PX * percent; 318 319 // Swiping down near the bottom of the screen where the touch initiation region is. 320 final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 321 0, SCREEN_HEIGHT_PX - distanceY, 0); 322 final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 323 0, SCREEN_HEIGHT_PX, 0); 324 325 assertThat(gestureListener.onScroll(event1, event2, 0, -distanceY)).isTrue(); 326 327 verify(mScrimController, never()).expand(any()); 328 } 329 330 /** 331 * Makes sure swiping down when bouncer initially hidden doesn't change the expansion amount. 332 */ 333 @Test 334 @EnableFlags(Flags.FLAG_DREAM_OVERLAY_BOUNCER_SWIPE_DIRECTION_FILTERING) testSwipeDown_whenBouncerInitiallyHidden_doesNotSetExpansion_directionFiltering()335 public void testSwipeDown_whenBouncerInitiallyHidden_doesNotSetExpansion_directionFiltering() { 336 mTouchHandler.onSessionStart(mTouchSession); 337 ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor = 338 ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class); 339 verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture()); 340 341 final OnGestureListener gestureListener = gestureListenerCaptor.getValue(); 342 343 final float percent = .15f; 344 final float distanceY = SCREEN_HEIGHT_PX * percent; 345 346 // Swiping down near the bottom of the screen where the touch initiation region is. 347 final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 348 0, SCREEN_HEIGHT_PX - distanceY, 0); 349 final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 350 0, SCREEN_HEIGHT_PX, 0); 351 352 assertThat(gestureListener.onScroll(event1, event2, 0, -distanceY)).isFalse(); 353 354 verify(mScrimController, never()).expand(any()); 355 } 356 357 /** 358 * Makes sure the expansion amount is proportional to (1 - scroll). 359 */ 360 @Test testSwipeUp_setsCorrectExpansionAmount()361 public void testSwipeUp_setsCorrectExpansionAmount() { 362 mTouchHandler.onSessionStart(mTouchSession); 363 ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor = 364 ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class); 365 verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture()); 366 367 final OnGestureListener gestureListener = gestureListenerCaptor.getValue(); 368 369 verifyScroll(.3f, gestureListener); 370 verifyScroll(.7f, gestureListener); 371 } 372 373 /** 374 * Verifies that swiping up when the lock pattern is not secure dismissed dream and consumes 375 * the gesture. 376 */ 377 @Test testSwipeUp_keyguardNotSecure_doesNotExpand()378 public void testSwipeUp_keyguardNotSecure_doesNotExpand() { 379 when(mLockPatternUtils.isSecure(CURRENT_USER_INFO.id)).thenReturn(false); 380 mTouchHandler.onSessionStart(mTouchSession); 381 ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor = 382 ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class); 383 verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture()); 384 385 final OnGestureListener gestureListener = gestureListenerCaptor.getValue(); 386 387 final float distanceY = SCREEN_HEIGHT_PX * 0.3f; 388 final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 389 0, SCREEN_HEIGHT_PX, 0); 390 final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 391 0, SCREEN_HEIGHT_PX - distanceY, 0); 392 393 reset(mScrimController); 394 395 // Scroll gesture is consumed. 396 assertThat(gestureListener.onScroll(event1, event2, 0, distanceY)) 397 .isTrue(); 398 // We should not expand since the keyguard is not secure 399 verify(mScrimController, never()).expand(any()); 400 // Since we are swiping up, we should wake from dreams. 401 verify(mCentralSurfaces).awakenDreams(); 402 } 403 404 /** 405 * Verifies that swiping down when the lock pattern is not secure does not dismiss the dream. 406 */ 407 @Test testSwipeDown_keyguardNotSecure_doesNotExpand()408 public void testSwipeDown_keyguardNotSecure_doesNotExpand() { 409 when(mLockPatternUtils.isSecure(CURRENT_USER_INFO.id)).thenReturn(false); 410 mTouchHandler.onSessionStart(mTouchSession); 411 ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor = 412 ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class); 413 verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture()); 414 415 final OnGestureListener gestureListener = gestureListenerCaptor.getValue(); 416 417 final float distanceY = SCREEN_HEIGHT_PX * 0.3f; 418 // Swiping down near the bottom of the screen where the touch initiation region is. 419 final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 420 0, SCREEN_HEIGHT_PX - distanceY, 0); 421 final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 422 0, SCREEN_HEIGHT_PX, 0); 423 424 reset(mScrimController); 425 426 // Scroll gesture is not consumed. 427 assertThat(gestureListener.onScroll(event1, event2, 0, distanceY)) 428 .isTrue(); 429 // We should not expand since the keyguard is not secure 430 verify(mScrimController, never()).expand(any()); 431 // Since we are swiping down, we should not dismiss the dream. 432 verify(mCentralSurfaces, never()).awakenDreams(); 433 } 434 verifyScroll(float percent, OnGestureListener gestureListener)435 private void verifyScroll(float percent, 436 OnGestureListener gestureListener) { 437 final float distanceY = SCREEN_HEIGHT_PX * percent; 438 439 final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 440 0, SCREEN_HEIGHT_PX, 0); 441 final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 442 0, SCREEN_HEIGHT_PX - distanceY, 0); 443 444 reset(mScrimController); 445 assertThat(gestureListener.onScroll(event1, event2, 0, 446 distanceY)) 447 .isTrue(); 448 449 // Ensure only called once 450 verify(mScrimController).expand(any()); 451 452 final float expansion = 1 - percent; 453 454 // Ensure correct expansion passed in. 455 ShadeExpansionChangeEvent event = 456 new ShadeExpansionChangeEvent( 457 expansion, /* expanded= */ false, /* tracking= */ true); 458 verify(mScrimController).expand(event); 459 } 460 461 /** 462 * Tests that ending an upward swipe before the set threshold leads to bouncer collapsing down. 463 */ 464 @Test testSwipeUpPositionBelowThreshold_collapsesBouncer()465 public void testSwipeUpPositionBelowThreshold_collapsesBouncer() { 466 final float swipeUpPercentage = .3f; 467 final float expansion = 1 - swipeUpPercentage; 468 // The upward velocity is ignored. 469 final float velocityY = -1; 470 swipeToPosition(swipeUpPercentage, velocityY); 471 472 verify(mValueAnimatorCreator).create(eq(expansion), 473 eq(KeyguardBouncerConstants.EXPANSION_HIDDEN)); 474 verify(mValueAnimator, never()).addListener(any()); 475 476 verify(mFlingAnimationUtilsClosing).apply(eq(mValueAnimator), 477 eq(SCREEN_HEIGHT_PX * expansion), 478 eq(SCREEN_HEIGHT_PX * KeyguardBouncerConstants.EXPANSION_HIDDEN), 479 eq(velocityY), eq((float) SCREEN_HEIGHT_PX)); 480 verify(mValueAnimator).start(); 481 verify(mUiEventLogger, never()).log(any()); 482 } 483 484 /** 485 * Tests that ending an upward swipe above the set threshold will continue the expansion. 486 */ 487 @Test testSwipeUpPositionAboveThreshold_expandsBouncer()488 public void testSwipeUpPositionAboveThreshold_expandsBouncer() { 489 final float swipeUpPercentage = .7f; 490 final float expansion = 1 - swipeUpPercentage; 491 // The downward velocity is ignored. 492 final float velocityY = 1; 493 swipeToPosition(swipeUpPercentage, velocityY); 494 495 verify(mValueAnimatorCreator).create(eq(expansion), 496 eq(KeyguardBouncerConstants.EXPANSION_VISIBLE)); 497 498 ArgumentCaptor<AnimatorListenerAdapter> endAnimationListenerCaptor = 499 ArgumentCaptor.forClass(AnimatorListenerAdapter.class); 500 verify(mValueAnimator).addListener(endAnimationListenerCaptor.capture()); 501 AnimatorListenerAdapter endAnimationListener = endAnimationListenerCaptor.getValue(); 502 503 verify(mFlingAnimationUtils).apply(eq(mValueAnimator), eq(SCREEN_HEIGHT_PX * expansion), 504 eq(SCREEN_HEIGHT_PX * KeyguardBouncerConstants.EXPANSION_VISIBLE), 505 eq(velocityY), eq((float) SCREEN_HEIGHT_PX)); 506 verify(mValueAnimator).start(); 507 verify(mUiEventLogger).log(BouncerSwipeTouchHandler.DreamEvent.DREAM_SWIPED); 508 509 endAnimationListener.onAnimationEnd(mValueAnimator); 510 verify(mUiEventLogger).log(BouncerSwipeTouchHandler.DreamEvent.DREAM_BOUNCER_FULLY_VISIBLE); 511 } 512 513 /** 514 * Tests that swiping up with a speed above the set threshold will continue the expansion. 515 */ 516 @Test testSwipeUpVelocityAboveMin_expandsBouncer()517 public void testSwipeUpVelocityAboveMin_expandsBouncer() { 518 when(mFlingAnimationUtils.getMinVelocityPxPerSecond()).thenReturn((float) 0); 519 520 // The ending position below the set threshold is ignored. 521 final float swipeUpPercentage = .3f; 522 final float expansion = 1 - swipeUpPercentage; 523 final float velocityY = -1; 524 swipeToPosition(swipeUpPercentage, velocityY); 525 526 verify(mValueAnimatorCreator).create(eq(expansion), 527 eq(KeyguardBouncerConstants.EXPANSION_VISIBLE)); 528 529 ArgumentCaptor<AnimatorListenerAdapter> endAnimationListenerCaptor = 530 ArgumentCaptor.forClass(AnimatorListenerAdapter.class); 531 verify(mValueAnimator).addListener(endAnimationListenerCaptor.capture()); 532 AnimatorListenerAdapter endAnimationListener = endAnimationListenerCaptor.getValue(); 533 534 verify(mFlingAnimationUtils).apply(eq(mValueAnimator), eq(SCREEN_HEIGHT_PX * expansion), 535 eq(SCREEN_HEIGHT_PX * KeyguardBouncerConstants.EXPANSION_VISIBLE), 536 eq(velocityY), eq((float) SCREEN_HEIGHT_PX)); 537 verify(mValueAnimator).start(); 538 verify(mUiEventLogger).log(BouncerSwipeTouchHandler.DreamEvent.DREAM_SWIPED); 539 540 endAnimationListener.onAnimationEnd(mValueAnimator); 541 verify(mUiEventLogger).log(BouncerSwipeTouchHandler.DreamEvent.DREAM_BOUNCER_FULLY_VISIBLE); 542 } 543 544 @Test testTouchSessionOnRemovedCalledTwice()545 public void testTouchSessionOnRemovedCalledTwice() { 546 mTouchHandler.onSessionStart(mTouchSession); 547 ArgumentCaptor<TouchHandler.TouchSession.Callback> onRemovedCallbackCaptor = 548 ArgumentCaptor.forClass(TouchHandler.TouchSession.Callback.class); 549 verify(mTouchSession).registerCallback(onRemovedCallbackCaptor.capture()); 550 onRemovedCallbackCaptor.getValue().onRemoved(); 551 onRemovedCallbackCaptor.getValue().onRemoved(); 552 } 553 swipeToPosition(float percent, float velocityY)554 private void swipeToPosition(float percent, float velocityY) { 555 Mockito.clearInvocations(mTouchSession); 556 mTouchHandler.onSessionStart(mTouchSession); 557 ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor = 558 ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class); 559 verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture()); 560 ArgumentCaptor<InputChannelCompat.InputEventListener> inputEventListenerCaptor = 561 ArgumentCaptor.forClass(InputChannelCompat.InputEventListener.class); 562 verify(mTouchSession).registerInputListener(inputEventListenerCaptor.capture()); 563 564 when(mVelocityTracker.getYVelocity()).thenReturn(velocityY); 565 566 final float distanceY = SCREEN_HEIGHT_PX * percent; 567 568 final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 569 0, SCREEN_HEIGHT_PX, 0); 570 final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 571 0, SCREEN_HEIGHT_PX - distanceY, 0); 572 573 assertThat(gestureListenerCaptor.getValue().onScroll(event1, event2, 0, 574 distanceY)) 575 .isTrue(); 576 577 final MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 578 0, 0, 0); 579 580 inputEventListenerCaptor.getValue().onInputEvent(upEvent); 581 } 582 } 583