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