• 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 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         mControllers.bubbleControllers.ifPresent(controllers -> {
96             controllers.bubbleBarViewController.setTranslationYForSwipe(transY);
97             controllers.bubbleStashedHandleViewController.setTranslationYForSwipe(transY);
98         });
99     }
100 
101     /**
102      * Starts a spring aniamtion to set the views back to the resting state.
103      */
startSpring()104     public void startSpring() {
105         if (mHasSprungOnceThisGesture || mAnimationToHomeRunning) {
106             return;
107         }
108         mSpringBounce = new SpringAnimationBuilder(mContext)
109                 .setStartValue(mTranslationYForSwipe.value)
110                 .setEndValue(0)
111                 .setDampingRatio(SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY)
112                 .setStiffness(SpringForce.STIFFNESS_LOW)
113                 .build(mTranslationYForSwipe, VALUE);
114         mSpringBounce.addListener(forEndCallback(() -> {
115             if (!mGestureEnded) {
116                 return;
117             }
118             reset();
119             if (mControllers.taskbarStashController.isInApp()
120                     && mControllers.taskbarStashController.isTaskbarVisibleAndNotStashing()) {
121                 mControllers.taskbarEduTooltipController.maybeShowFeaturesEdu();
122             }
123         }));
124         mSpringBounce.start();
125         mHasSprungOnceThisGesture = true;
126     }
127 
reset()128     private void reset() {
129         mGestureEnded = false;
130         mHasSprungOnceThisGesture = false;
131     }
132 
133     /**
134      * Returns a callback to help monitor the swipe gesture.
135      */
getTransitionCallback()136     public TransitionCallback getTransitionCallback() {
137         return mCallback;
138     }
139 
140     /**
141      * Returns true if we will animate to zero before the input duration.
142      */
willAnimateToZeroBefore(long duration)143     public boolean willAnimateToZeroBefore(long duration) {
144         if (mSpringBounce != null && mSpringBounce.isRunning()) {
145             long springDuration = mSpringBounce.getDuration();
146             long current = mSpringBounce.getCurrentPlayTime();
147             return (springDuration - current < duration);
148         }
149         if (mTranslationYForSwipe.isAnimatingToValue(0)) {
150             return mTranslationYForSwipe.getRemainingTime() < duration;
151         }
152         return false;
153     }
154 
155     /**
156      * Returns an animation to reset the taskbar translation to {@code 0}.
157      */
createAnimToResetTranslation(long duration)158     public ObjectAnimator createAnimToResetTranslation(long duration) {
159         ObjectAnimator animator = mTranslationYForSwipe.animateToValue(0);
160         animator.setInterpolator(Interpolators.LINEAR);
161         animator.setDuration(duration);
162         animator.addListener(new AnimatorListenerAdapter() {
163             @Override
164             public void onAnimationStart(Animator animation) {
165                 cancelSpringIfExists();
166                 reset();
167                 mAnimationToHomeRunning = true;
168             }
169 
170             @Override
171             public void onAnimationEnd(Animator animation) {
172                 mAnimationToHomeRunning = false;
173                 reset();
174             }
175         });
176         return animator;
177     }
178 
179     /**
180      * Helper class to communicate to/from  the input consumer.
181      */
182     public class TransitionCallback {
183 
184         /**
185          * Clears any existing animations so that user
186          * can take control over the movement of the taskbaer.
187          */
onActionDown()188         public void onActionDown() {
189             if (mAnimationToHomeRunning) {
190                 mTranslationYForSwipe.cancelAnimation();
191             }
192             mAnimationToHomeRunning = false;
193             cancelSpringIfExists();
194             reset();
195         }
196         /**
197          * Called when there is movement to move the taskbar.
198          */
onActionMove(float dY)199         public void onActionMove(float dY) {
200             if (mAnimationToHomeRunning
201                     || (mHasSprungOnceThisGesture && !mGestureEnded)) {
202                 return;
203             }
204 
205             mTranslationYForSwipe.updateValue(dY);
206         }
207 
208         /**
209          * Called when swipe gesture has ended.
210          */
onActionEnd()211         public void onActionEnd() {
212             if (mHasSprungOnceThisGesture) {
213                 reset();
214             } else {
215                 mGestureEnded = true;
216                 startSpring();
217             }
218         }
219     }
220 
221     @Override
dumpLogs(String prefix, PrintWriter pw)222     public void dumpLogs(String prefix, PrintWriter pw) {
223         pw.println(prefix + "TaskbarTranslationController:");
224 
225         pw.println(prefix + "\tmTranslationYForSwipe=" + mTranslationYForSwipe.value);
226         pw.println(prefix + "\tmHasSprungOnceThisGesture=" + mHasSprungOnceThisGesture);
227         pw.println(prefix + "\tmAnimationToHomeRunning=" + mAnimationToHomeRunning);
228         pw.println(prefix + "\tmGestureEnded=" + mGestureEnded);
229         pw.println(prefix + "\tmSpringBounce is running=" + (mSpringBounce != null
230                 && mSpringBounce.isRunning()));
231     }
232 }
233 
234