• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package com.android.launcher3.taskbar;
17 
18 import static com.android.launcher3.anim.AnimatedFloat.VALUE;
19 import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
20 
21 import android.animation.Animator;
22 import android.animation.AnimatorListenerAdapter;
23 import android.animation.ObjectAnimator;
24 import android.animation.ValueAnimator;
25 
26 import androidx.annotation.Nullable;
27 import androidx.dynamicanimation.animation.SpringForce;
28 
29 import com.android.launcher3.anim.AnimatedFloat;
30 import com.android.launcher3.anim.Interpolators;
31 import com.android.launcher3.anim.SpringAnimationBuilder;
32 import com.android.launcher3.util.DisplayController;
33 
34 import java.io.PrintWriter;
35 
36 /**
37  * Class responsible for translating the transient taskbar UI during a swipe gesture.
38  *
39  * The translation is controlled, in priority order:
40  * - animation to home
41  * - a spring animation
42  * - controlled by user
43  *
44  * The spring animation will play start once the user lets go or when user pauses to go to overview.
45  * When the user goes home, the stash animation will play.
46  */
47 public class TaskbarTranslationController implements TaskbarControllers.LoggableTaskbarController {
48 
49     private final TaskbarActivityContext mContext;
50     private TaskbarControllers mControllers;
51     private final AnimatedFloat mTranslationYForSwipe = new AnimatedFloat(
52             this::updateTranslationYForSwipe);
53 
54     private boolean mHasSprungOnceThisGesture;
55     private @Nullable ValueAnimator mSpringBounce;
56     private boolean mGestureEnded;
57     private boolean mAnimationToHomeRunning;
58 
59     private final boolean mIsTransientTaskbar;
60 
61     private final TransitionCallback mCallback;
62 
TaskbarTranslationController(TaskbarActivityContext context)63     public TaskbarTranslationController(TaskbarActivityContext context) {
64         mContext = context;
65         mIsTransientTaskbar = DisplayController.isTransientTaskbar(mContext);
66         mCallback = new TransitionCallback();
67     }
68 
69     /**
70      * Initialization method.
71      */
init(TaskbarControllers controllers)72     public void init(TaskbarControllers controllers) {
73         mControllers = controllers;
74     }
75 
76     /**
77      * Called to cancel any existing animations.
78      */
cancelSpringIfExists()79     public void cancelSpringIfExists() {
80         if (mSpringBounce != null) {
81             mSpringBounce.cancel();
82             mSpringBounce = null;
83         }
84     }
85 
updateTranslationYForSwipe()86     private void updateTranslationYForSwipe() {
87         if (!mIsTransientTaskbar) {
88             return;
89         }
90 
91         float transY = mTranslationYForSwipe.value;
92         mControllers.stashedHandleViewController.setTranslationYForSwipe(transY);
93         mControllers.taskbarViewController.setTranslationYForSwipe(transY);
94         mControllers.taskbarDragLayerController.setTranslationYForSwipe(transY);
95     }
96 
97     /**
98      * Starts a spring aniamtion to set the views back to the resting state.
99      */
startSpring()100     public void startSpring() {
101         if (mHasSprungOnceThisGesture || mAnimationToHomeRunning) {
102             return;
103         }
104         mSpringBounce = new SpringAnimationBuilder(mContext)
105                 .setStartValue(mTranslationYForSwipe.value)
106                 .setEndValue(0)
107                 .setDampingRatio(SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY)
108                 .setStiffness(SpringForce.STIFFNESS_LOW)
109                 .build(mTranslationYForSwipe, VALUE);
110         mSpringBounce.addListener(forEndCallback(() -> {
111             if (!mGestureEnded) {
112                 return;
113             }
114             reset();
115             if (mControllers.taskbarStashController.isInApp()
116                     && mControllers.taskbarStashController.isTaskbarVisibleAndNotStashing()) {
117                 mControllers.taskbarEduTooltipController.maybeShowFeaturesEdu();
118             }
119         }));
120         mSpringBounce.start();
121         mHasSprungOnceThisGesture = true;
122     }
123 
reset()124     private void reset() {
125         mGestureEnded = false;
126         mHasSprungOnceThisGesture = false;
127     }
128 
129     /**
130      * Returns a callback to help monitor the swipe gesture.
131      */
getTransitionCallback()132     public TransitionCallback getTransitionCallback() {
133         return mCallback;
134     }
135 
136     /**
137      * Returns true if we will animate to zero before the input duration.
138      */
willAnimateToZeroBefore(long duration)139     public boolean willAnimateToZeroBefore(long duration) {
140         if (mSpringBounce != null && mSpringBounce.isRunning()) {
141             long springDuration = mSpringBounce.getDuration();
142             long current = mSpringBounce.getCurrentPlayTime();
143             return (springDuration - current < duration);
144         }
145         if (mTranslationYForSwipe.isAnimatingToValue(0)) {
146             return mTranslationYForSwipe.getRemainingTime() < duration;
147         }
148         return false;
149     }
150 
151     /**
152      * Returns an animation to reset the taskbar translation to {@code 0}.
153      */
createAnimToResetTranslation(long duration)154     public ObjectAnimator createAnimToResetTranslation(long duration) {
155         ObjectAnimator animator = mTranslationYForSwipe.animateToValue(0);
156         animator.setInterpolator(Interpolators.LINEAR);
157         animator.setDuration(duration);
158         animator.addListener(new AnimatorListenerAdapter() {
159             @Override
160             public void onAnimationStart(Animator animation) {
161                 cancelSpringIfExists();
162                 reset();
163                 mAnimationToHomeRunning = true;
164             }
165 
166             @Override
167             public void onAnimationEnd(Animator animation) {
168                 mAnimationToHomeRunning = false;
169                 reset();
170             }
171         });
172         return animator;
173     }
174 
175     /**
176      * Helper class to communicate to/from  the input consumer.
177      */
178     public class TransitionCallback {
179 
180         /**
181          * Clears any existing animations so that user
182          * can take control over the movement of the taskbaer.
183          */
onActionDown()184         public void onActionDown() {
185             if (mAnimationToHomeRunning) {
186                 mTranslationYForSwipe.cancelAnimation();
187             }
188             mAnimationToHomeRunning = false;
189             cancelSpringIfExists();
190             reset();
191         }
192         /**
193          * Called when there is movement to move the taskbar.
194          */
onActionMove(float dY)195         public void onActionMove(float dY) {
196             if (mAnimationToHomeRunning
197                     || (mHasSprungOnceThisGesture && !mGestureEnded)) {
198                 return;
199             }
200 
201             mTranslationYForSwipe.updateValue(dY);
202         }
203 
204         /**
205          * Called when swipe gesture has ended.
206          */
onActionEnd()207         public void onActionEnd() {
208             if (mHasSprungOnceThisGesture) {
209                 reset();
210             } else {
211                 mGestureEnded = true;
212                 startSpring();
213             }
214         }
215     }
216 
217     @Override
dumpLogs(String prefix, PrintWriter pw)218     public void dumpLogs(String prefix, PrintWriter pw) {
219         pw.println(prefix + "TaskbarTranslationController:");
220 
221         pw.println(prefix + "\tmTranslationYForSwipe=" + mTranslationYForSwipe.value);
222         pw.println(prefix + "\tmHasSprungOnceThisGesture=" + mHasSprungOnceThisGesture);
223         pw.println(prefix + "\tmAnimationToHomeRunning=" + mAnimationToHomeRunning);
224         pw.println(prefix + "\tmGestureEnded=" + mGestureEnded);
225         pw.println(prefix + "\tmSpringBounce is running=" + (mSpringBounce != null
226                 && mSpringBounce.isRunning()));
227     }
228 }
229 
230