1 /* 2 * Copyright (C) 2022 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.dreams.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.when; 28 29 import android.animation.AnimatorListenerAdapter; 30 import android.animation.ValueAnimator; 31 import android.content.pm.UserInfo; 32 import android.graphics.Rect; 33 import android.graphics.Region; 34 import android.testing.AndroidTestingRunner; 35 import android.view.GestureDetector; 36 import android.view.GestureDetector.OnGestureListener; 37 import android.view.MotionEvent; 38 import android.view.VelocityTracker; 39 40 import androidx.test.filters.SmallTest; 41 42 import com.android.internal.logging.UiEventLogger; 43 import com.android.internal.widget.LockPatternUtils; 44 import com.android.systemui.SysuiTestCase; 45 import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants; 46 import com.android.systemui.dreams.touch.scrim.ScrimController; 47 import com.android.systemui.dreams.touch.scrim.ScrimManager; 48 import com.android.systemui.settings.FakeUserTracker; 49 import com.android.systemui.shade.ShadeExpansionChangeEvent; 50 import com.android.systemui.shared.system.InputChannelCompat; 51 import com.android.systemui.statusbar.NotificationShadeWindowController; 52 import com.android.systemui.statusbar.phone.CentralSurfaces; 53 import com.android.wm.shell.animation.FlingAnimationUtils; 54 55 import org.junit.Before; 56 import org.junit.Test; 57 import org.junit.runner.RunWith; 58 import org.mockito.ArgumentCaptor; 59 import org.mockito.Mock; 60 import org.mockito.Mockito; 61 import org.mockito.MockitoAnnotations; 62 63 import java.util.Collections; 64 import java.util.Optional; 65 66 @SmallTest 67 @RunWith(AndroidTestingRunner.class) 68 public class BouncerSwipeTouchHandlerTest extends SysuiTestCase { 69 @Mock 70 CentralSurfaces mCentralSurfaces; 71 72 @Mock 73 ScrimManager mScrimManager; 74 75 @Mock 76 ScrimController mScrimController; 77 78 @Mock 79 NotificationShadeWindowController mNotificationShadeWindowController; 80 81 @Mock 82 FlingAnimationUtils mFlingAnimationUtils; 83 84 @Mock 85 FlingAnimationUtils mFlingAnimationUtilsClosing; 86 87 @Mock 88 DreamTouchHandler.TouchSession mTouchSession; 89 90 BouncerSwipeTouchHandler mTouchHandler; 91 92 @Mock 93 BouncerSwipeTouchHandler.ValueAnimatorCreator mValueAnimatorCreator; 94 95 @Mock 96 ValueAnimator mValueAnimator; 97 98 @Mock 99 BouncerSwipeTouchHandler.VelocityTrackerFactory mVelocityTrackerFactory; 100 101 @Mock 102 VelocityTracker mVelocityTracker; 103 104 @Mock 105 UiEventLogger mUiEventLogger; 106 107 @Mock 108 LockPatternUtils mLockPatternUtils; 109 110 FakeUserTracker mUserTracker; 111 112 private static final float TOUCH_REGION = .3f; 113 private static final int SCREEN_WIDTH_PX = 1024; 114 private static final int SCREEN_HEIGHT_PX = 100; 115 116 private static final Rect SCREEN_BOUNDS = new Rect(0, 0, 1024, 100); 117 private static final UserInfo CURRENT_USER_INFO = new UserInfo( 118 10, 119 /* name= */ "user10", 120 /* flags= */ 0 121 ); 122 123 @Before setup()124 public void setup() { 125 MockitoAnnotations.initMocks(this); 126 mUserTracker = new FakeUserTracker(); 127 mTouchHandler = new BouncerSwipeTouchHandler( 128 mScrimManager, 129 Optional.of(mCentralSurfaces), 130 mNotificationShadeWindowController, 131 mValueAnimatorCreator, 132 mVelocityTrackerFactory, 133 mLockPatternUtils, 134 mUserTracker, 135 mFlingAnimationUtils, 136 mFlingAnimationUtilsClosing, 137 TOUCH_REGION, 138 mUiEventLogger); 139 140 when(mScrimManager.getCurrentController()).thenReturn(mScrimController); 141 when(mCentralSurfaces.isBouncerShowing()).thenReturn(false); 142 when(mValueAnimatorCreator.create(anyFloat(), anyFloat())).thenReturn(mValueAnimator); 143 when(mVelocityTrackerFactory.obtain()).thenReturn(mVelocityTracker); 144 when(mFlingAnimationUtils.getMinVelocityPxPerSecond()).thenReturn(Float.MAX_VALUE); 145 when(mTouchSession.getBounds()).thenReturn(SCREEN_BOUNDS); 146 when(mLockPatternUtils.isSecure(CURRENT_USER_INFO.id)).thenReturn(true); 147 148 mUserTracker.set(Collections.singletonList(CURRENT_USER_INFO), 0); 149 } 150 151 /** 152 * Ensures expansion only happens when touch down happens in valid part of the screen. 153 */ 154 @Test testSessionStart()155 public void testSessionStart() { 156 final Region region = Region.obtain(); 157 mTouchHandler.getTouchInitiationRegion(SCREEN_BOUNDS, region); 158 159 final Rect bounds = region.getBounds(); 160 161 final Rect expected = new Rect(); 162 163 expected.set(0, Math.round(SCREEN_HEIGHT_PX * (1 - TOUCH_REGION)), SCREEN_WIDTH_PX, 164 SCREEN_HEIGHT_PX); 165 166 assertThat(bounds).isEqualTo(expected); 167 168 mTouchHandler.onSessionStart(mTouchSession); 169 verify(mNotificationShadeWindowController).setForcePluginOpen(eq(true), any()); 170 ArgumentCaptor<InputChannelCompat.InputEventListener> eventListenerCaptor = 171 ArgumentCaptor.forClass(InputChannelCompat.InputEventListener.class); 172 ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor = 173 ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class); 174 verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture()); 175 verify(mTouchSession).registerInputListener(eventListenerCaptor.capture()); 176 177 // A touch within range at the bottom of the screen should trigger listening 178 assertThat(gestureListenerCaptor.getValue() 179 .onScroll(Mockito.mock(MotionEvent.class), 180 Mockito.mock(MotionEvent.class), 181 1, 182 2)).isTrue(); 183 } 184 185 private enum Direction { 186 DOWN, 187 UP, 188 } 189 190 /** 191 * Makes sure swiping up when bouncer initially showing doesn't change the expansion amount. 192 */ 193 @Test testSwipeUp_whenBouncerInitiallyShowing_doesNotSetExpansion()194 public void testSwipeUp_whenBouncerInitiallyShowing_doesNotSetExpansion() { 195 when(mCentralSurfaces.isBouncerShowing()).thenReturn(true); 196 197 mTouchHandler.onSessionStart(mTouchSession); 198 ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor = 199 ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class); 200 verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture()); 201 202 final OnGestureListener gestureListener = gestureListenerCaptor.getValue(); 203 204 final float percent = .3f; 205 final float distanceY = SCREEN_HEIGHT_PX * percent; 206 207 // Swiping up near the top of the screen where the touch initiation region is. 208 final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 209 0, distanceY, 0); 210 final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 211 0, 0, 0); 212 213 assertThat(gestureListener.onScroll(event1, event2, 0, distanceY)) 214 .isTrue(); 215 216 verify(mScrimController, never()).expand(any()); 217 } 218 219 /** 220 * Makes sure swiping down when bouncer initially hidden doesn't change the expansion amount. 221 */ 222 @Test testSwipeDown_whenBouncerInitiallyHidden_doesNotSetExpansion()223 public void testSwipeDown_whenBouncerInitiallyHidden_doesNotSetExpansion() { 224 mTouchHandler.onSessionStart(mTouchSession); 225 ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor = 226 ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class); 227 verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture()); 228 229 final OnGestureListener gestureListener = gestureListenerCaptor.getValue(); 230 231 final float percent = .15f; 232 final float distanceY = SCREEN_HEIGHT_PX * percent; 233 234 // Swiping down near the bottom of the screen where the touch initiation region is. 235 final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 236 0, SCREEN_HEIGHT_PX - distanceY, 0); 237 final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 238 0, SCREEN_HEIGHT_PX, 0); 239 240 assertThat(gestureListener.onScroll(event1, event2, 0, distanceY)) 241 .isTrue(); 242 243 verify(mScrimController, never()).expand(any()); 244 } 245 246 /** 247 * Makes sure the expansion amount is proportional to (1 - scroll). 248 */ 249 @Test testSwipeUp_setsCorrectExpansionAmount()250 public void testSwipeUp_setsCorrectExpansionAmount() { 251 mTouchHandler.onSessionStart(mTouchSession); 252 ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor = 253 ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class); 254 verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture()); 255 256 final OnGestureListener gestureListener = gestureListenerCaptor.getValue(); 257 258 verifyScroll(.3f, Direction.UP, false, gestureListener); 259 260 // Ensure that subsequent gestures are treated as expanding even if the bouncer state 261 // changes. 262 when(mCentralSurfaces.isBouncerShowing()).thenReturn(true); 263 verifyScroll(.7f, Direction.UP, false, gestureListener); 264 } 265 266 /** 267 * Makes sure the expansion amount is proportional to scroll. 268 */ 269 @Test testSwipeDown_setsCorrectExpansionAmount()270 public void testSwipeDown_setsCorrectExpansionAmount() { 271 when(mCentralSurfaces.isBouncerShowing()).thenReturn(true); 272 273 mTouchHandler.onSessionStart(mTouchSession); 274 ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor = 275 ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class); 276 verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture()); 277 278 final OnGestureListener gestureListener = gestureListenerCaptor.getValue(); 279 280 verifyScroll(.3f, Direction.DOWN, true, gestureListener); 281 282 // Ensure that subsequent gestures are treated as collapsing even if the bouncer state 283 // changes. 284 when(mCentralSurfaces.isBouncerShowing()).thenReturn(false); 285 verifyScroll(.7f, Direction.DOWN, true, gestureListener); 286 } 287 288 /** 289 * Makes sure the expansion amount is proportional to (1 - scroll). 290 */ 291 @Test testSwipeUp_keyguardNotSecure_doesNotExpand()292 public void testSwipeUp_keyguardNotSecure_doesNotExpand() { 293 when(mLockPatternUtils.isSecure(CURRENT_USER_INFO.id)).thenReturn(false); 294 mTouchHandler.onSessionStart(mTouchSession); 295 ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor = 296 ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class); 297 verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture()); 298 299 final OnGestureListener gestureListener = gestureListenerCaptor.getValue(); 300 301 final float distanceY = SCREEN_HEIGHT_PX * 0.3f; 302 final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 303 0, SCREEN_HEIGHT_PX, 0); 304 final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 305 0, SCREEN_HEIGHT_PX - distanceY, 0); 306 307 reset(mScrimController); 308 assertThat(gestureListener.onScroll(event1, event2, 0, distanceY)) 309 .isTrue(); 310 // We should not expand since the keyguard is not secure 311 verify(mScrimController, never()).expand(any()); 312 } 313 verifyScroll(float percent, Direction direction, boolean isBouncerInitiallyShowing, GestureDetector.OnGestureListener gestureListener)314 private void verifyScroll(float percent, Direction direction, 315 boolean isBouncerInitiallyShowing, GestureDetector.OnGestureListener gestureListener) { 316 final float distanceY = SCREEN_HEIGHT_PX * percent; 317 318 final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 319 0, direction == Direction.UP ? SCREEN_HEIGHT_PX : 0, 0); 320 final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 321 0, direction == Direction.UP ? SCREEN_HEIGHT_PX - distanceY : distanceY, 0); 322 323 reset(mScrimController); 324 assertThat(gestureListener.onScroll(event1, event2, 0, distanceY)) 325 .isTrue(); 326 327 // Ensure only called once 328 verify(mScrimController).expand(any()); 329 330 final float expansion = isBouncerInitiallyShowing ? percent : 1 - percent; 331 final float dragDownAmount = event2.getY() - event1.getY(); 332 333 // Ensure correct expansion passed in. 334 ShadeExpansionChangeEvent event = 335 new ShadeExpansionChangeEvent( 336 expansion, /* expanded= */ false, /* tracking= */ true, dragDownAmount); 337 verify(mScrimController).expand(event); 338 } 339 340 /** 341 * Tests that ending an upward swipe before the set threshold leads to bouncer collapsing down. 342 */ 343 @Test testSwipeUpPositionBelowThreshold_collapsesBouncer()344 public void testSwipeUpPositionBelowThreshold_collapsesBouncer() { 345 final float swipeUpPercentage = .3f; 346 final float expansion = 1 - swipeUpPercentage; 347 // The upward velocity is ignored. 348 final float velocityY = -1; 349 swipeToPosition(swipeUpPercentage, Direction.UP, velocityY); 350 351 verify(mValueAnimatorCreator).create(eq(expansion), 352 eq(KeyguardBouncerConstants.EXPANSION_HIDDEN)); 353 verify(mValueAnimator, never()).addListener(any()); 354 355 verify(mFlingAnimationUtilsClosing).apply(eq(mValueAnimator), 356 eq(SCREEN_HEIGHT_PX * expansion), 357 eq(SCREEN_HEIGHT_PX * KeyguardBouncerConstants.EXPANSION_HIDDEN), 358 eq(velocityY), eq((float) SCREEN_HEIGHT_PX)); 359 verify(mValueAnimator).start(); 360 verify(mUiEventLogger, never()).log(any()); 361 } 362 363 /** 364 * Tests that ending an upward swipe above the set threshold will continue the expansion. 365 */ 366 @Test testSwipeUpPositionAboveThreshold_expandsBouncer()367 public void testSwipeUpPositionAboveThreshold_expandsBouncer() { 368 final float swipeUpPercentage = .7f; 369 final float expansion = 1 - swipeUpPercentage; 370 // The downward velocity is ignored. 371 final float velocityY = 1; 372 swipeToPosition(swipeUpPercentage, Direction.UP, velocityY); 373 374 verify(mValueAnimatorCreator).create(eq(expansion), 375 eq(KeyguardBouncerConstants.EXPANSION_VISIBLE)); 376 377 ArgumentCaptor<AnimatorListenerAdapter> endAnimationListenerCaptor = 378 ArgumentCaptor.forClass(AnimatorListenerAdapter.class); 379 verify(mValueAnimator).addListener(endAnimationListenerCaptor.capture()); 380 AnimatorListenerAdapter endAnimationListener = endAnimationListenerCaptor.getValue(); 381 382 verify(mFlingAnimationUtils).apply(eq(mValueAnimator), eq(SCREEN_HEIGHT_PX * expansion), 383 eq(SCREEN_HEIGHT_PX * KeyguardBouncerConstants.EXPANSION_VISIBLE), 384 eq(velocityY), eq((float) SCREEN_HEIGHT_PX)); 385 verify(mValueAnimator).start(); 386 verify(mUiEventLogger).log(BouncerSwipeTouchHandler.DreamEvent.DREAM_SWIPED); 387 388 endAnimationListener.onAnimationEnd(mValueAnimator); 389 verify(mUiEventLogger).log(BouncerSwipeTouchHandler.DreamEvent.DREAM_BOUNCER_FULLY_VISIBLE); 390 } 391 392 /** 393 * Tests that ending a downward swipe above the set threshold will continue the expansion, 394 * but will not trigger logging of the DREAM_SWIPED event. 395 */ 396 @Test testSwipeDownPositionAboveThreshold_expandsBouncer_doesNotLog()397 public void testSwipeDownPositionAboveThreshold_expandsBouncer_doesNotLog() { 398 when(mCentralSurfaces.isBouncerShowing()).thenReturn(true); 399 400 final float swipeDownPercentage = .3f; 401 // The downward velocity is ignored. 402 final float velocityY = 1; 403 swipeToPosition(swipeDownPercentage, Direction.DOWN, velocityY); 404 405 verify(mValueAnimatorCreator).create(eq(swipeDownPercentage), 406 eq(KeyguardBouncerConstants.EXPANSION_VISIBLE)); 407 verify(mValueAnimator, never()).addListener(any()); 408 409 verify(mFlingAnimationUtils).apply(eq(mValueAnimator), 410 eq(SCREEN_HEIGHT_PX * swipeDownPercentage), 411 eq(SCREEN_HEIGHT_PX * KeyguardBouncerConstants.EXPANSION_VISIBLE), 412 eq(velocityY), eq((float) SCREEN_HEIGHT_PX)); 413 verify(mValueAnimator).start(); 414 verify(mUiEventLogger, never()).log(any()); 415 } 416 417 /** 418 * Tests that swiping down with a speed above the set threshold leads to bouncer collapsing 419 * down. 420 */ 421 @Test testSwipeDownVelocityAboveMin_collapsesBouncer()422 public void testSwipeDownVelocityAboveMin_collapsesBouncer() { 423 when(mCentralSurfaces.isBouncerShowing()).thenReturn(true); 424 when(mFlingAnimationUtils.getMinVelocityPxPerSecond()).thenReturn((float) 0); 425 426 // The ending position above the set threshold is ignored. 427 final float swipeDownPercentage = .3f; 428 final float velocityY = 1; 429 swipeToPosition(swipeDownPercentage, Direction.DOWN, velocityY); 430 431 verify(mValueAnimatorCreator).create(eq(swipeDownPercentage), 432 eq(KeyguardBouncerConstants.EXPANSION_HIDDEN)); 433 verify(mValueAnimator, never()).addListener(any()); 434 435 verify(mFlingAnimationUtilsClosing).apply(eq(mValueAnimator), 436 eq(SCREEN_HEIGHT_PX * swipeDownPercentage), 437 eq(SCREEN_HEIGHT_PX * KeyguardBouncerConstants.EXPANSION_HIDDEN), 438 eq(velocityY), eq((float) SCREEN_HEIGHT_PX)); 439 verify(mValueAnimator).start(); 440 verify(mUiEventLogger, never()).log(any()); 441 } 442 443 /** 444 * Tests that swiping up with a speed above the set threshold will continue the expansion. 445 */ 446 @Test testSwipeUpVelocityAboveMin_expandsBouncer()447 public void testSwipeUpVelocityAboveMin_expandsBouncer() { 448 when(mFlingAnimationUtils.getMinVelocityPxPerSecond()).thenReturn((float) 0); 449 450 // The ending position below the set threshold is ignored. 451 final float swipeUpPercentage = .3f; 452 final float expansion = 1 - swipeUpPercentage; 453 final float velocityY = -1; 454 swipeToPosition(swipeUpPercentage, Direction.UP, velocityY); 455 456 verify(mValueAnimatorCreator).create(eq(expansion), 457 eq(KeyguardBouncerConstants.EXPANSION_VISIBLE)); 458 459 ArgumentCaptor<AnimatorListenerAdapter> endAnimationListenerCaptor = 460 ArgumentCaptor.forClass(AnimatorListenerAdapter.class); 461 verify(mValueAnimator).addListener(endAnimationListenerCaptor.capture()); 462 AnimatorListenerAdapter endAnimationListener = endAnimationListenerCaptor.getValue(); 463 464 verify(mFlingAnimationUtils).apply(eq(mValueAnimator), eq(SCREEN_HEIGHT_PX * expansion), 465 eq(SCREEN_HEIGHT_PX * KeyguardBouncerConstants.EXPANSION_VISIBLE), 466 eq(velocityY), eq((float) SCREEN_HEIGHT_PX)); 467 verify(mValueAnimator).start(); 468 verify(mUiEventLogger).log(BouncerSwipeTouchHandler.DreamEvent.DREAM_SWIPED); 469 470 endAnimationListener.onAnimationEnd(mValueAnimator); 471 verify(mUiEventLogger).log(BouncerSwipeTouchHandler.DreamEvent.DREAM_BOUNCER_FULLY_VISIBLE); 472 } 473 474 /** 475 * Ensures {@link CentralSurfaces} 476 */ 477 @Test testInformBouncerShowingOnExpand()478 public void testInformBouncerShowingOnExpand() { 479 swipeToPosition(1f, Direction.UP, 0); 480 } 481 482 /** 483 * Ensures {@link CentralSurfaces} 484 */ 485 @Test testInformBouncerHidingOnCollapse()486 public void testInformBouncerHidingOnCollapse() { 487 // Must swipe up to set initial state. 488 swipeToPosition(1f, Direction.UP, 0); 489 Mockito.clearInvocations(mCentralSurfaces); 490 491 swipeToPosition(0f, Direction.DOWN, 0); 492 } 493 494 @Test testTouchSessionOnRemovedCalledTwice()495 public void testTouchSessionOnRemovedCalledTwice() { 496 mTouchHandler.onSessionStart(mTouchSession); 497 ArgumentCaptor<DreamTouchHandler.TouchSession.Callback> onRemovedCallbackCaptor = 498 ArgumentCaptor.forClass(DreamTouchHandler.TouchSession.Callback.class); 499 verify(mTouchSession).registerCallback(onRemovedCallbackCaptor.capture()); 500 onRemovedCallbackCaptor.getValue().onRemoved(); 501 onRemovedCallbackCaptor.getValue().onRemoved(); 502 } 503 swipeToPosition(float percent, Direction direction, float velocityY)504 private void swipeToPosition(float percent, Direction direction, float velocityY) { 505 Mockito.clearInvocations(mTouchSession); 506 mTouchHandler.onSessionStart(mTouchSession); 507 ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor = 508 ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class); 509 verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture()); 510 ArgumentCaptor<InputChannelCompat.InputEventListener> inputEventListenerCaptor = 511 ArgumentCaptor.forClass(InputChannelCompat.InputEventListener.class); 512 verify(mTouchSession).registerInputListener(inputEventListenerCaptor.capture()); 513 514 when(mVelocityTracker.getYVelocity()).thenReturn(velocityY); 515 516 final float distanceY = SCREEN_HEIGHT_PX * percent; 517 518 final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 519 0, direction == Direction.UP ? SCREEN_HEIGHT_PX : 0, 0); 520 final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 521 0, direction == Direction.UP ? SCREEN_HEIGHT_PX - distanceY : distanceY, 0); 522 523 assertThat(gestureListenerCaptor.getValue().onScroll(event1, event2, 0, distanceY)) 524 .isTrue(); 525 526 final MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 527 0, 0, 0); 528 529 inputEventListenerCaptor.getValue().onInputEvent(upEvent); 530 } 531 } 532