• 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.app.animation.Interpolators;
30 import com.android.launcher3.anim.AnimatedFloat;
31 import com.android.launcher3.anim.SpringAnimationBuilder;
32 
33 import java.io.PrintWriter;
34 
35 /**
36  * Class responsible for translating the transient taskbar UI during a swipe gesture.
37  *
38  * The translation is controlled, in priority order:
39  * - animation to home
40  * - a spring animation
41  * - controlled by user
42  *
43  * The spring animation will play start once the user lets go or when user pauses to go to overview.
44  * When the user goes home, the stash animation will play.
45  */
46 public class TaskbarTranslationController implements TaskbarControllers.LoggableTaskbarController {
47 
48     private final TaskbarActivityContext mContext;
49     private TaskbarControllers mControllers;
50     private final AnimatedFloat mTranslationYForSwipe = new AnimatedFloat(
51             this::updateTranslationYForSwipe);
52 
53     private boolean mHasSprungOnceThisGesture;
54     private @Nullable ValueAnimator mSpringBounce;
55     private boolean mGestureInProgress;
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 = mContext.isTransientTaskbar();
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         mControllers.bubbleControllers.ifPresent(controllers -> {
96             controllers.bubbleBarViewController.setTranslationYForSwipe(transY);
97             controllers.bubbleStashedHandleViewController.ifPresent(
98                     controller -> controller.setTranslationYForSwipe(transY));
99         });
100     }
101 
102     /**
103      * Starts a spring aniamtion to set the views back to the resting state.
104      */
startSpring()105     public void startSpring() {
106         if (mHasSprungOnceThisGesture || mAnimationToHomeRunning) {
107             return;
108         }
109         mSpringBounce = new SpringAnimationBuilder(mContext)
110                 .setStartValue(mTranslationYForSwipe.value)
111                 .setEndValue(0)
112                 .setDampingRatio(SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY)
113                 .setStiffness(SpringForce.STIFFNESS_LOW)
114                 .build(mTranslationYForSwipe, VALUE);
115         mSpringBounce.addListener(forEndCallback(() -> {
116             if (!mGestureEnded) {
117                 return;
118             }
119             reset();
120             if (mControllers.taskbarStashController.isInApp()
121                     && mControllers.taskbarStashController.isTaskbarVisibleAndNotStashing()) {
122                 mControllers.taskbarEduTooltipController.maybeShowFeaturesEdu();
123             }
124         }));
125         mSpringBounce.start();
126         mHasSprungOnceThisGesture = true;
127     }
128 
reset()129     private void reset() {
130         mGestureEnded = false;
131         mHasSprungOnceThisGesture = false;
132     }
133 
134     /**
135      * Returns a callback to help monitor the swipe gesture.
136      */
getTransitionCallback()137     public TransitionCallback getTransitionCallback() {
138         return mCallback;
139     }
140 
141     /**
142      * Returns true if we will animate to zero before the input duration.
143      */
willAnimateToZeroBefore(long duration)144     public boolean willAnimateToZeroBefore(long duration) {
145         if (mSpringBounce != null && mSpringBounce.isRunning()) {
146             long springDuration = mSpringBounce.getDuration();
147             long current = mSpringBounce.getCurrentPlayTime();
148             return (springDuration - current < duration);
149         }
150         if (mTranslationYForSwipe.isAnimatingToValue(0)) {
151             return mTranslationYForSwipe.getRemainingTime() < duration;
152         }
153         return false;
154     }
155 
156     /**
157      * Returns an animation to reset the taskbar translation to {@code 0}.
158      */
createAnimToResetTranslation(long duration)159     public ValueAnimator createAnimToResetTranslation(long duration) {
160         if (mGestureInProgress) {
161             // Return an empty animator as the translation will reset itself after gesture ends.
162             return ValueAnimator.ofFloat(0).setDuration(duration);
163         }
164 
165         ObjectAnimator animator = mTranslationYForSwipe.animateToValue(0);
166         animator.setInterpolator(Interpolators.LINEAR);
167         animator.setDuration(duration);
168         animator.addListener(new AnimatorListenerAdapter() {
169             @Override
170             public void onAnimationStart(Animator animation) {
171                 cancelSpringIfExists();
172                 reset();
173                 mAnimationToHomeRunning = true;
174             }
175 
176             @Override
177             public void onAnimationEnd(Animator animation) {
178                 mAnimationToHomeRunning = false;
179                 reset();
180             }
181         });
182         return animator;
183     }
184 
185     /**
186      * Helper class to communicate to/from  the input consumer.
187      */
188     public class TransitionCallback {
189 
190         /**
191          * Clears any existing animations so that user
192          * can take control over the movement of the taskbaer.
193          */
onActionDown()194         public void onActionDown() {
195             if (mAnimationToHomeRunning) {
196                 mTranslationYForSwipe.cancelAnimation();
197             }
198             mAnimationToHomeRunning = false;
199             cancelSpringIfExists();
200             reset();
201             mGestureInProgress = true;
202         }
203         /**
204          * Called when there is movement to move the taskbar.
205          */
onActionMove(float dY)206         public void onActionMove(float dY) {
207             if (mAnimationToHomeRunning
208                     || (mHasSprungOnceThisGesture && !mGestureEnded)) {
209                 return;
210             }
211 
212             mTranslationYForSwipe.updateValue(dY);
213         }
214 
215         /**
216          * Called when swipe gesture has ended.
217          */
onActionEnd()218         public void onActionEnd() {
219             if (mHasSprungOnceThisGesture) {
220                 reset();
221             } else {
222                 mGestureEnded = true;
223                 startSpring();
224             }
225             mGestureInProgress = false;
226         }
227     }
228 
229     @Override
dumpLogs(String prefix, PrintWriter pw)230     public void dumpLogs(String prefix, PrintWriter pw) {
231         pw.println(prefix + "TaskbarTranslationController:");
232 
233         pw.println(prefix + "\tmTranslationYForSwipe=" + mTranslationYForSwipe.value);
234         pw.println(prefix + "\tmHasSprungOnceThisGesture=" + mHasSprungOnceThisGesture);
235         pw.println(prefix + "\tmAnimationToHomeRunning=" + mAnimationToHomeRunning);
236         pw.println(prefix + "\tmGestureEnded=" + mGestureEnded);
237         pw.println(prefix + "\tmSpringBounce is running=" + (mSpringBounce != null
238                 && mSpringBounce.isRunning()));
239     }
240 }
241 
242