1 /* 2 * Copyright (C) 2019 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 package com.android.launcher3.uioverrides.touchcontrollers; 17 18 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; 19 import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; 20 import static com.android.launcher3.LauncherState.NORMAL; 21 import static com.android.launcher3.LauncherState.OVERVIEW; 22 import static com.android.launcher3.LauncherState.OVERVIEW_BUTTONS; 23 import static com.android.launcher3.LauncherState.QUICK_SWITCH; 24 import static com.android.launcher3.anim.AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD; 25 import static com.android.launcher3.anim.Interpolators.ACCEL_0_75; 26 import static com.android.launcher3.anim.Interpolators.DEACCEL; 27 import static com.android.launcher3.anim.Interpolators.DEACCEL_5; 28 import static com.android.launcher3.anim.Interpolators.LINEAR; 29 import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; 30 import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER; 31 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME; 32 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEDOWN; 33 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEUP; 34 import static com.android.launcher3.logging.StatsLogManager.getLauncherAtomEvent; 35 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE; 36 import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS; 37 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE; 38 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE; 39 import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW; 40 import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_RIGHT; 41 import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_UP; 42 import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_PAUSE_TO_OVERVIEW_ANIM; 43 import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs; 44 import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC; 45 import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.CANCEL; 46 import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.HIDE; 47 import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.PEEK; 48 import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET; 49 import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS; 50 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; 51 52 import android.animation.Animator; 53 import android.animation.AnimatorListenerAdapter; 54 import android.animation.ValueAnimator; 55 import android.graphics.PointF; 56 import android.view.MotionEvent; 57 import android.view.animation.Interpolator; 58 59 import com.android.launcher3.BaseQuickstepLauncher; 60 import com.android.launcher3.LauncherState; 61 import com.android.launcher3.R; 62 import com.android.launcher3.Utilities; 63 import com.android.launcher3.allapps.AllAppsTransitionController; 64 import com.android.launcher3.anim.AnimatorPlaybackController; 65 import com.android.launcher3.anim.PendingAnimation; 66 import com.android.launcher3.config.FeatureFlags; 67 import com.android.launcher3.graphics.OverviewScrim; 68 import com.android.launcher3.logging.StatsLogManager; 69 import com.android.launcher3.states.StateAnimationConfig; 70 import com.android.launcher3.touch.BaseSwipeDetector; 71 import com.android.launcher3.touch.BothAxesSwipeDetector; 72 import com.android.launcher3.userevent.nano.LauncherLogProto; 73 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; 74 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; 75 import com.android.launcher3.util.TouchController; 76 import com.android.launcher3.util.VibratorWrapper; 77 import com.android.quickstep.SystemUiProxy; 78 import com.android.quickstep.util.LayoutUtils; 79 import com.android.quickstep.util.MotionPauseDetector; 80 import com.android.quickstep.util.ShelfPeekAnim; 81 import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState; 82 import com.android.quickstep.util.StaggeredWorkspaceAnim; 83 import com.android.quickstep.views.LauncherRecentsView; 84 85 /** 86 * Handles quick switching to a recent task from the home screen. To give as much flexibility to 87 * the user as possible, also handles swipe up and hold to go to overview and swiping back home. 88 */ 89 public class NoButtonQuickSwitchTouchController implements TouchController, 90 BothAxesSwipeDetector.Listener, MotionPauseDetector.OnMotionPauseListener { 91 92 /** The minimum progress of the scale/translationY animation until drag end. */ 93 private static final float Y_ANIM_MIN_PROGRESS = 0.15f; 94 private static final Interpolator FADE_OUT_INTERPOLATOR = DEACCEL_5; 95 private static final Interpolator TRANSLATE_OUT_INTERPOLATOR = ACCEL_0_75; 96 private static final Interpolator SCALE_DOWN_INTERPOLATOR = DEACCEL; 97 98 private final BaseQuickstepLauncher mLauncher; 99 private final BothAxesSwipeDetector mSwipeDetector; 100 private final ShelfPeekAnim mShelfPeekAnim; 101 private final float mXRange; 102 private final float mYRange; 103 private final MotionPauseDetector mMotionPauseDetector; 104 private final float mMotionPauseMinDisplacement; 105 private final LauncherRecentsView mRecentsView; 106 107 private boolean mNoIntercept; 108 private LauncherState mStartState; 109 110 private boolean mIsHomeScreenVisible = true; 111 112 // As we drag, we control 3 animations: one to get non-overview components out of the way, 113 // and the other two to set overview properties based on x and y progress. 114 private AnimatorPlaybackController mNonOverviewAnim; 115 private AnimatorPlaybackController mXOverviewAnim; 116 private AnimatorPlaybackController mYOverviewAnim; 117 NoButtonQuickSwitchTouchController(BaseQuickstepLauncher launcher)118 public NoButtonQuickSwitchTouchController(BaseQuickstepLauncher launcher) { 119 mLauncher = launcher; 120 mSwipeDetector = new BothAxesSwipeDetector(mLauncher, this); 121 mShelfPeekAnim = mLauncher.getShelfPeekAnim(); 122 mRecentsView = mLauncher.getOverviewPanel(); 123 mXRange = mLauncher.getDeviceProfile().widthPx / 2f; 124 mYRange = LayoutUtils.getShelfTrackingDistance( 125 mLauncher, mLauncher.getDeviceProfile(), mRecentsView.getPagedOrientationHandler()); 126 mMotionPauseDetector = new MotionPauseDetector(mLauncher); 127 mMotionPauseMinDisplacement = mLauncher.getResources().getDimension( 128 R.dimen.motion_pause_detector_min_displacement_from_app); 129 } 130 131 @Override onControllerInterceptTouchEvent(MotionEvent ev)132 public boolean onControllerInterceptTouchEvent(MotionEvent ev) { 133 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 134 mNoIntercept = !canInterceptTouch(ev); 135 if (mNoIntercept) { 136 return false; 137 } 138 139 // Only detect horizontal swipe for intercept, then we will allow swipe up as well. 140 mSwipeDetector.setDetectableScrollConditions(DIRECTION_RIGHT, 141 false /* ignoreSlopWhenSettling */); 142 } 143 144 if (mNoIntercept) { 145 return false; 146 } 147 148 onControllerTouchEvent(ev); 149 return mSwipeDetector.isDraggingOrSettling(); 150 } 151 152 @Override onControllerTouchEvent(MotionEvent ev)153 public boolean onControllerTouchEvent(MotionEvent ev) { 154 return mSwipeDetector.onTouchEvent(ev); 155 } 156 canInterceptTouch(MotionEvent ev)157 private boolean canInterceptTouch(MotionEvent ev) { 158 if (!mLauncher.isInState(LauncherState.NORMAL)) { 159 return false; 160 } 161 if ((ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) == 0) { 162 return false; 163 } 164 int stateFlags = SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags(); 165 if ((stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0) { 166 return false; 167 } 168 return true; 169 } 170 171 @Override onDragStart(boolean start)172 public void onDragStart(boolean start) { 173 mMotionPauseDetector.clear(); 174 if (start) { 175 mStartState = mLauncher.getStateManager().getState(); 176 177 mMotionPauseDetector.setOnMotionPauseListener(this); 178 179 // We have detected horizontal drag start, now allow swipe up as well. 180 mSwipeDetector.setDetectableScrollConditions(DIRECTION_RIGHT | DIRECTION_UP, 181 false /* ignoreSlopWhenSettling */); 182 183 setupAnimators(); 184 } 185 } 186 187 @Override onMotionPauseChanged(boolean isPaused)188 public void onMotionPauseChanged(boolean isPaused) { 189 VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC); 190 191 if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) { 192 return; 193 } 194 195 ShelfAnimState shelfState = isPaused ? PEEK : HIDE; 196 if (shelfState == PEEK) { 197 // Some shelf elements (e.g. qsb) were hidden, but we need them visible when peeking. 198 AllAppsTransitionController allAppsController = mLauncher.getAllAppsController(); 199 allAppsController.setAlphas( 200 NORMAL, new StateAnimationConfig(), NO_ANIM_PROPERTY_SETTER); 201 202 if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) { 203 // Hotseat was hidden, but we need it visible when peeking. 204 mLauncher.getHotseat().setAlpha(1); 205 } 206 } 207 mShelfPeekAnim.setShelfState(shelfState, ShelfPeekAnim.INTERPOLATOR, 208 ShelfPeekAnim.DURATION); 209 } 210 setupAnimators()211 private void setupAnimators() { 212 // Animate the non-overview components (e.g. workspace, shelf) out of the way. 213 StateAnimationConfig nonOverviewBuilder = new StateAnimationConfig(); 214 nonOverviewBuilder.setInterpolator(ANIM_WORKSPACE_FADE, FADE_OUT_INTERPOLATOR); 215 nonOverviewBuilder.setInterpolator(ANIM_ALL_APPS_FADE, FADE_OUT_INTERPOLATOR); 216 nonOverviewBuilder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, TRANSLATE_OUT_INTERPOLATOR); 217 nonOverviewBuilder.setInterpolator(ANIM_VERTICAL_PROGRESS, TRANSLATE_OUT_INTERPOLATOR); 218 updateNonOverviewAnim(QUICK_SWITCH, nonOverviewBuilder); 219 mNonOverviewAnim.dispatchOnStart(); 220 221 if (mRecentsView.getTaskViewCount() == 0) { 222 mRecentsView.setOnEmptyMessageUpdatedListener(isEmpty -> { 223 if (!isEmpty && mSwipeDetector.isDraggingState()) { 224 // We have loaded tasks, update the animators to start at the correct scale etc. 225 setupOverviewAnimators(); 226 } 227 }); 228 } 229 230 setupOverviewAnimators(); 231 } 232 233 /** Create state animation to control non-overview components. */ updateNonOverviewAnim(LauncherState toState, StateAnimationConfig config)234 private void updateNonOverviewAnim(LauncherState toState, StateAnimationConfig config) { 235 config.duration = (long) (Math.max(mXRange, mYRange) * 2); 236 config.animFlags = config.animFlags | SKIP_OVERVIEW; 237 mNonOverviewAnim = mLauncher.getStateManager() 238 .createAnimationToNewWorkspace(toState, config) 239 .setOnCancelRunnable(this::clearState); 240 } 241 setupOverviewAnimators()242 private void setupOverviewAnimators() { 243 final LauncherState fromState = QUICK_SWITCH; 244 final LauncherState toState = OVERVIEW; 245 246 // Set RecentView's initial properties. 247 SCALE_PROPERTY.set(mRecentsView, fromState.getOverviewScaleAndOffset(mLauncher)[0]); 248 ADJACENT_PAGE_OFFSET.set(mRecentsView, 1f); 249 mRecentsView.setContentAlpha(1); 250 mRecentsView.setFullscreenProgress(fromState.getOverviewFullscreenProgress()); 251 mLauncher.getActionsView().getVisibilityAlpha().setValue( 252 (fromState.getVisibleElements(mLauncher) & OVERVIEW_BUTTONS) != 0 ? 1f : 0f); 253 254 float[] scaleAndOffset = toState.getOverviewScaleAndOffset(mLauncher); 255 // As we drag right, animate the following properties: 256 // - RecentsView translationX 257 // - OverviewScrim 258 PendingAnimation xAnim = new PendingAnimation((long) (mXRange * 2)); 259 xAnim.setFloat(mRecentsView, ADJACENT_PAGE_OFFSET, scaleAndOffset[1], LINEAR); 260 xAnim.setFloat(mLauncher.getDragLayer().getOverviewScrim(), OverviewScrim.SCRIM_PROGRESS, 261 toState.getOverviewScrimAlpha(mLauncher), LINEAR); 262 mXOverviewAnim = xAnim.createPlaybackController(); 263 mXOverviewAnim.dispatchOnStart(); 264 265 // As we drag up, animate the following properties: 266 // - RecentsView scale 267 // - RecentsView fullscreenProgress 268 PendingAnimation yAnim = new PendingAnimation((long) (mYRange * 2)); 269 yAnim.setFloat(mRecentsView, SCALE_PROPERTY, scaleAndOffset[0], SCALE_DOWN_INTERPOLATOR); 270 yAnim.setFloat(mRecentsView, FULLSCREEN_PROGRESS, 271 toState.getOverviewFullscreenProgress(), SCALE_DOWN_INTERPOLATOR); 272 mYOverviewAnim = yAnim.createPlaybackController(); 273 mYOverviewAnim.dispatchOnStart(); 274 } 275 276 @Override onDrag(PointF displacement, MotionEvent ev)277 public boolean onDrag(PointF displacement, MotionEvent ev) { 278 float xProgress = Math.max(0, displacement.x) / mXRange; 279 float yProgress = Math.max(0, -displacement.y) / mYRange; 280 yProgress = Utilities.mapRange(yProgress, Y_ANIM_MIN_PROGRESS, 1f); 281 282 boolean wasHomeScreenVisible = mIsHomeScreenVisible; 283 if (wasHomeScreenVisible && mNonOverviewAnim != null) { 284 mNonOverviewAnim.setPlayFraction(xProgress); 285 } 286 mIsHomeScreenVisible = FADE_OUT_INTERPOLATOR.getInterpolation(xProgress) 287 <= 1 - ALPHA_CUTOFF_THRESHOLD; 288 289 if (wasHomeScreenVisible && !mIsHomeScreenVisible) { 290 // Get the shelf all the way offscreen so it pops up when we decide to peek it. 291 mShelfPeekAnim.setShelfState(HIDE, LINEAR, 0); 292 } 293 294 // Only allow motion pause if the home screen is invisible, since some 295 // home screen elements will appear in the shelf on motion pause. 296 mMotionPauseDetector.setDisallowPause(mIsHomeScreenVisible 297 || -displacement.y < mMotionPauseMinDisplacement); 298 mMotionPauseDetector.addPosition(ev); 299 300 if (mIsHomeScreenVisible) { 301 // Cancel the shelf anim so it doesn't clobber mNonOverviewAnim. 302 mShelfPeekAnim.setShelfState(CANCEL, LINEAR, 0); 303 } 304 305 if (mXOverviewAnim != null) { 306 mXOverviewAnim.setPlayFraction(xProgress); 307 } 308 if (mYOverviewAnim != null) { 309 mYOverviewAnim.setPlayFraction(yProgress); 310 } 311 return true; 312 } 313 314 @Override onDragEnd(PointF velocity)315 public void onDragEnd(PointF velocity) { 316 boolean horizontalFling = mSwipeDetector.isFling(velocity.x); 317 boolean verticalFling = mSwipeDetector.isFling(velocity.y); 318 boolean noFling = !horizontalFling && !verticalFling; 319 int logAction = noFling ? Touch.SWIPE : Touch.FLING; 320 if (mMotionPauseDetector.isPaused() && noFling) { 321 cancelAnimations(); 322 323 Animator overviewAnim = mLauncher.createAtomicAnimationFactory() 324 .createStateElementAnimation(INDEX_PAUSE_TO_OVERVIEW_ANIM); 325 overviewAnim.addListener(new AnimatorListenerAdapter() { 326 @Override 327 public void onAnimationEnd(Animator animation) { 328 onAnimationToStateCompleted(OVERVIEW, logAction); 329 } 330 }); 331 overviewAnim.start(); 332 return; 333 } 334 335 final LauncherState targetState; 336 if (horizontalFling && verticalFling) { 337 if (velocity.x < 0) { 338 // Flinging left and up or down both go back home. 339 targetState = NORMAL; 340 } else { 341 if (velocity.y > 0) { 342 // Flinging right and down goes to quick switch. 343 targetState = QUICK_SWITCH; 344 } else { 345 // Flinging up and right could go either home or to quick switch. 346 // Determine the target based on the higher velocity. 347 targetState = Math.abs(velocity.x) > Math.abs(velocity.y) 348 ? QUICK_SWITCH : NORMAL; 349 } 350 } 351 } else if (horizontalFling) { 352 targetState = velocity.x > 0 ? QUICK_SWITCH : NORMAL; 353 } else if (verticalFling) { 354 targetState = velocity.y > 0 ? QUICK_SWITCH : NORMAL; 355 } else { 356 // If user isn't flinging, just snap to the closest state based on x progress. 357 boolean passedHorizontalThreshold = mXOverviewAnim.getInterpolatedProgress() > 0.5f; 358 targetState = passedHorizontalThreshold ? QUICK_SWITCH : NORMAL; 359 } 360 361 // Animate the various components to the target state. 362 363 float xProgress = mXOverviewAnim.getProgressFraction(); 364 float startXProgress = Utilities.boundToRange(xProgress 365 + velocity.x * getSingleFrameMs(mLauncher) / mXRange, 0f, 1f); 366 final float endXProgress = targetState == NORMAL ? 0 : 1; 367 long xDuration = BaseSwipeDetector.calculateDuration(velocity.x, 368 Math.abs(endXProgress - startXProgress)); 369 ValueAnimator xOverviewAnim = mXOverviewAnim.getAnimationPlayer(); 370 xOverviewAnim.setFloatValues(startXProgress, endXProgress); 371 xOverviewAnim.setDuration(xDuration) 372 .setInterpolator(scrollInterpolatorForVelocity(velocity.x)); 373 mXOverviewAnim.dispatchOnStart(); 374 375 boolean flingUpToNormal = verticalFling && velocity.y < 0 && targetState == NORMAL; 376 377 float yProgress = mYOverviewAnim.getProgressFraction(); 378 float startYProgress = Utilities.boundToRange(yProgress 379 - velocity.y * getSingleFrameMs(mLauncher) / mYRange, 0f, 1f); 380 final float endYProgress; 381 if (flingUpToNormal) { 382 endYProgress = 1; 383 } else if (targetState == NORMAL) { 384 // Keep overview at its current scale/translationY as it slides off the screen. 385 endYProgress = startYProgress; 386 } else { 387 endYProgress = 0; 388 } 389 long yDuration = BaseSwipeDetector.calculateDuration(velocity.y, 390 Math.abs(endYProgress - startYProgress)); 391 ValueAnimator yOverviewAnim = mYOverviewAnim.getAnimationPlayer(); 392 yOverviewAnim.setFloatValues(startYProgress, endYProgress); 393 yOverviewAnim.setDuration(yDuration); 394 mYOverviewAnim.dispatchOnStart(); 395 396 ValueAnimator nonOverviewAnim = mNonOverviewAnim.getAnimationPlayer(); 397 if (flingUpToNormal && !mIsHomeScreenVisible) { 398 // We are flinging to home while workspace is invisible, run the same staggered 399 // animation as from an app. 400 StateAnimationConfig config = new StateAnimationConfig(); 401 // Update mNonOverviewAnim to do nothing so it doesn't interfere. 402 config.animFlags = 0; 403 updateNonOverviewAnim(targetState, config); 404 nonOverviewAnim = mNonOverviewAnim.getAnimationPlayer(); 405 406 new StaggeredWorkspaceAnim(mLauncher, velocity.y, false /* animateOverviewScrim */) 407 .start(); 408 } else { 409 boolean canceled = targetState == NORMAL; 410 if (canceled) { 411 // Let the state manager know that the animation didn't go to the target state, 412 // but don't clean up yet (we already clean up when the animation completes). 413 mNonOverviewAnim.dispatchOnCancelWithoutCancelRunnable(); 414 } 415 float startProgress = mNonOverviewAnim.getProgressFraction(); 416 float endProgress = canceled ? 0 : 1; 417 nonOverviewAnim.setFloatValues(startProgress, endProgress); 418 mNonOverviewAnim.dispatchOnStart(); 419 } 420 421 nonOverviewAnim.setDuration(Math.max(xDuration, yDuration)); 422 mNonOverviewAnim.setEndAction(() -> onAnimationToStateCompleted(targetState, logAction)); 423 424 cancelAnimations(); 425 xOverviewAnim.start(); 426 yOverviewAnim.start(); 427 nonOverviewAnim.start(); 428 } 429 onAnimationToStateCompleted(LauncherState targetState, int logAction)430 private void onAnimationToStateCompleted(LauncherState targetState, int logAction) { 431 mLauncher.getUserEventDispatcher().logStateChangeAction(logAction, 432 getDirectionForLog(), mSwipeDetector.getDownX(), mSwipeDetector.getDownY(), 433 LauncherLogProto.ContainerType.NAVBAR, 434 mStartState.containerType, 435 targetState.containerType, 436 mLauncher.getWorkspace().getCurrentPage()); 437 mLauncher.getStatsLogManager().logger() 438 .withSrcState(LAUNCHER_STATE_HOME) 439 .withDstState(StatsLogManager.containerTypeToAtomState(targetState.containerType)) 440 .log(getLauncherAtomEvent(mStartState.containerType, targetState.containerType, 441 targetState.ordinal > mStartState.ordinal 442 ? LAUNCHER_UNKNOWN_SWIPEUP 443 : LAUNCHER_UNKNOWN_SWIPEDOWN)); 444 mLauncher.getStateManager().goToState(targetState, false, this::clearState); 445 } 446 getDirectionForLog()447 private int getDirectionForLog() { 448 return Utilities.isRtl(mLauncher.getResources()) ? Direction.LEFT : Direction.RIGHT; 449 } 450 cancelAnimations()451 private void cancelAnimations() { 452 if (mNonOverviewAnim != null) { 453 mNonOverviewAnim.getAnimationPlayer().cancel(); 454 } 455 if (mXOverviewAnim != null) { 456 mXOverviewAnim.getAnimationPlayer().cancel(); 457 } 458 if (mYOverviewAnim != null) { 459 mYOverviewAnim.getAnimationPlayer().cancel(); 460 } 461 mShelfPeekAnim.setShelfState(ShelfAnimState.CANCEL, LINEAR, 0); 462 mMotionPauseDetector.clear(); 463 } 464 clearState()465 private void clearState() { 466 cancelAnimations(); 467 mNonOverviewAnim = null; 468 mXOverviewAnim = null; 469 mYOverviewAnim = null; 470 mIsHomeScreenVisible = true; 471 mSwipeDetector.finishedScrolling(); 472 mRecentsView.setOnEmptyMessageUpdatedListener(null); 473 } 474 } 475