1 /*
2  * Copyright (C) 2021 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.quickstep.interaction;
17 
18 import android.animation.Animator;
19 import android.animation.AnimatorListenerAdapter;
20 import android.animation.AnimatorSet;
21 import android.animation.ObjectAnimator;
22 import android.content.Context;
23 import android.util.AttributeSet;
24 import android.view.View;
25 import android.view.ViewGroup;
26 
27 import androidx.annotation.NonNull;
28 import androidx.annotation.Nullable;
29 import androidx.constraintlayout.widget.ConstraintLayout;
30 
31 import com.android.launcher3.R;
32 
33 import java.util.ArrayList;
34 
35 
36 /**
37  * Helper View for the gesture tutorial mock taskbar view.
38  *
39  * This helper class allows animating this mock taskview to and from a mock hotseat and the bottom
40  * of the screen.
41  */
42 public class AnimatedTaskbarView extends ConstraintLayout {
43 
44     private View mBackground;
45     private View mIconContainer;
46     private View mIcon1;
47     private View mIcon2;
48     private View mIcon3;
49     private View mIcon4;
50     private View mIcon5;
51     private View mIcon6;
52 
53     @Nullable private Animator mRunningAnimator;
54 
AnimatedTaskbarView(@onNull Context context)55     public AnimatedTaskbarView(@NonNull Context context) {
56         super(context);
57     }
58 
AnimatedTaskbarView(@onNull Context context, @Nullable AttributeSet attrs)59     public AnimatedTaskbarView(@NonNull Context context,
60             @Nullable AttributeSet attrs) {
61         super(context, attrs);
62     }
63 
AnimatedTaskbarView(@onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr)64     public AnimatedTaskbarView(@NonNull Context context, @Nullable AttributeSet attrs,
65             int defStyleAttr) {
66         super(context, attrs, defStyleAttr);
67     }
68 
AnimatedTaskbarView(@onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)69     public AnimatedTaskbarView(@NonNull Context context, @Nullable AttributeSet attrs,
70             int defStyleAttr,
71             int defStyleRes) {
72         super(context, attrs, defStyleAttr, defStyleRes);
73     }
74 
75     @Override
onFinishInflate()76     protected void onFinishInflate() {
77         super.onFinishInflate();
78 
79         mBackground = findViewById(R.id.taskbar_background);
80         mIconContainer = findViewById(R.id.icon_container);
81         mIcon1 = findViewById(R.id.taskbar_icon_1);
82         mIcon2 = findViewById(R.id.taskbar_icon_2);
83         mIcon3 = findViewById(R.id.taskbar_icon_3);
84         mIcon4 = findViewById(R.id.taskbar_icon_4);
85         mIcon5 = findViewById(R.id.taskbar_icon_5);
86         mIcon6 = findViewById(R.id.taskbar_icon_6);
87     }
88 
89     /**
90      * Animates this fake taskbar's disappearance into the given hotseat view.
91      */
animateDisappearanceToHotseat(ViewGroup hotseat)92     public void animateDisappearanceToHotseat(ViewGroup hotseat) {
93         ArrayList<Animator> animators = new ArrayList<>();
94         int hotseatTop = hotseat.getTop();
95 
96         animators.add(ObjectAnimator.ofFloat(
97                 mBackground, View.TRANSLATION_Y, 0, mBackground.getHeight()));
98         animators.add(ObjectAnimator.ofFloat(mBackground, View.ALPHA, 1f, 0f));
99         animators.add(createIconDisappearanceToHotseatAnimator(
100                 mIcon1, hotseat.findViewById(R.id.hotseat_icon_1), hotseatTop));
101         animators.add(createIconDisappearanceToHotseatAnimator(
102                 mIcon2, hotseat.findViewById(R.id.hotseat_icon_2), hotseatTop));
103         animators.add(createIconDisappearanceToHotseatAnimator(
104                 mIcon3, hotseat.findViewById(R.id.hotseat_icon_3), hotseatTop));
105         animators.add(createIconDisappearanceToHotseatAnimator(
106                 mIcon4, hotseat.findViewById(R.id.hotseat_icon_4), hotseatTop));
107         animators.add(createIconDisappearanceToHotseatAnimator(
108                 mIcon5, hotseat.findViewById(R.id.hotseat_icon_5), hotseatTop));
109         animators.add(createIconDisappearanceToHotseatAnimator(
110                 mIcon6, hotseat.findViewById(R.id.hotseat_icon_6), hotseatTop));
111 
112         AnimatorSet animatorSet = new AnimatorSet();
113 
114         animatorSet.playTogether(animators);
115         animatorSet.addListener(new AnimatorListenerAdapter() {
116             @Override
117             public void onAnimationEnd(Animator animation) {
118                 super.onAnimationEnd(animation);
119                 setVisibility(INVISIBLE);
120             }
121 
122             @Override
123             public void onAnimationStart(Animator animation) {
124                 super.onAnimationStart(animation);
125                 setVisibility(VISIBLE);
126             }
127         });
128 
129         start(animatorSet);
130     }
131 
132     /**
133      * Animates this fake taskbar's appearance from the given hotseat view.
134      */
animateAppearanceFromHotseat(ViewGroup hotseat)135     public void animateAppearanceFromHotseat(ViewGroup hotseat) {
136         ArrayList<Animator> animators = new ArrayList<>();
137         int hotseatTop = hotseat.getTop();
138 
139         animators.add(ObjectAnimator.ofFloat(
140                 mBackground, View.TRANSLATION_Y, mBackground.getHeight(), 0));
141         animators.add(ObjectAnimator.ofFloat(mBackground, View.ALPHA, 0f, 1f));
142         animators.add(createIconAppearanceFromHotseatAnimator(
143                 mIcon1, hotseat.findViewById(R.id.hotseat_icon_1), hotseatTop));
144         animators.add(createIconAppearanceFromHotseatAnimator(
145                 mIcon2, hotseat.findViewById(R.id.hotseat_icon_2), hotseatTop));
146         animators.add(createIconAppearanceFromHotseatAnimator(
147                 mIcon3, hotseat.findViewById(R.id.hotseat_icon_3), hotseatTop));
148         animators.add(createIconAppearanceFromHotseatAnimator(
149                 mIcon4, hotseat.findViewById(R.id.hotseat_icon_4), hotseatTop));
150         animators.add(createIconAppearanceFromHotseatAnimator(
151                 mIcon5, hotseat.findViewById(R.id.hotseat_icon_5), hotseatTop));
152         animators.add(createIconAppearanceFromHotseatAnimator(
153                 mIcon6, hotseat.findViewById(R.id.hotseat_icon_6), hotseatTop));
154 
155         AnimatorSet animatorSet = new AnimatorSet();
156 
157         animatorSet.playTogether(animators);
158         animatorSet.addListener(new AnimatorListenerAdapter() {
159             @Override
160             public void onAnimationStart(Animator animation) {
161                 super.onAnimationStart(animation);
162                 setVisibility(VISIBLE);
163             }
164         });
165 
166         start(animatorSet);
167     }
168 
169     /**
170      * Animates this fake taskbar's disappearance to the bottom of the screen.
171      */
animateDisappearanceToBottom()172     public void animateDisappearanceToBottom() {
173         ArrayList<Animator> animators = new ArrayList<>();
174 
175         animators.add(ObjectAnimator.ofFloat(
176                 mBackground, View.TRANSLATION_Y, 0, mBackground.getHeight()));
177         animators.add(ObjectAnimator.ofFloat(mBackground, View.ALPHA, 1f, 0f));
178         animators.add(ObjectAnimator.ofFloat(mIconContainer, View.SCALE_X, 1f, 0f));
179         animators.add(ObjectAnimator.ofFloat(mIconContainer, View.SCALE_Y, 1f, 0f));
180 
181         initializeIconContainerPivot();
182 
183         AnimatorSet animatorSet = new AnimatorSet();
184 
185         animatorSet.playTogether(animators);
186         animatorSet.addListener(new AnimatorListenerAdapter() {
187             @Override
188             public void onAnimationEnd(Animator animation) {
189                 super.onAnimationEnd(animation);
190                 setVisibility(INVISIBLE);
191                 resetIconContainerPivot();
192             }
193 
194             @Override
195             public void onAnimationCancel(Animator animation) {
196                 super.onAnimationCancel(animation);
197                 resetIconContainerPivot();
198             }
199 
200             @Override
201             public void onAnimationStart(Animator animation) {
202                 super.onAnimationStart(animation);
203                 setVisibility(VISIBLE);
204             }
205         });
206 
207         start(animatorSet);
208     }
209 
210     /**
211      * Animates this fake taskbar's appearance from the bottom of the screen.
212      */
animateAppearanceFromBottom()213     public void animateAppearanceFromBottom() {
214         ArrayList<Animator> animators = new ArrayList<>();
215 
216         animators.add(ObjectAnimator.ofFloat(
217                 mBackground, View.TRANSLATION_Y, mBackground.getHeight(), 0));
218         animators.add(ObjectAnimator.ofFloat(mBackground, View.ALPHA, 0f, 1f));
219         animators.add(ObjectAnimator.ofFloat(mIconContainer, View.SCALE_X, 0f, 1f));
220         animators.add(ObjectAnimator.ofFloat(mIconContainer, View.SCALE_Y, 0f, 1f));
221 
222         initializeIconContainerPivot();
223 
224         AnimatorSet animatorSet = new AnimatorSet();
225 
226         animatorSet.playTogether(animators);
227         animatorSet.addListener(new AnimatorListenerAdapter() {
228             @Override
229             public void onAnimationStart(Animator animation) {
230                 super.onAnimationStart(animation);
231                 setVisibility(VISIBLE);
232             }
233 
234             @Override
235             public void onAnimationEnd(Animator animation) {
236                 super.onAnimationEnd(animation);
237                 resetIconContainerPivot();
238             }
239 
240             @Override
241             public void onAnimationCancel(Animator animation) {
242                 super.onAnimationCancel(animation);
243                 resetIconContainerPivot();
244             }
245         });
246 
247         start(animatorSet);
248     }
249 
initializeIconContainerPivot()250     private void initializeIconContainerPivot() {
251         mIconContainer.setPivotX(getWidth() / 2f);
252         mIconContainer.setPivotY(getHeight() * 0.8f);
253     }
254 
resetIconContainerPivot()255     private void resetIconContainerPivot() {
256         mIconContainer.resetPivot();
257         mIconContainer.setScaleX(1f);
258         mIconContainer.setScaleY(1f);
259     }
260 
start(Animator animator)261     private void start(Animator animator) {
262         if (mRunningAnimator != null) {
263             mRunningAnimator.cancel();
264         }
265 
266         animator.addListener(new AnimatorListenerAdapter() {
267             @Override
268             public void onAnimationCancel(Animator animation) {
269                 super.onAnimationCancel(animation);
270                 mRunningAnimator = null;
271             }
272 
273             @Override
274             public void onAnimationEnd(Animator animation) {
275                 super.onAnimationEnd(animation);
276                 mRunningAnimator = null;
277             }
278 
279             @Override
280             public void onAnimationStart(Animator animation) {
281                 super.onAnimationStart(animation);
282                 mRunningAnimator = animator;
283             }
284         });
285 
286         animator.start();
287     }
288 
createIconDisappearanceToHotseatAnimator( View taskbarIcon, View hotseatIcon, int hotseatTop)289     private Animator createIconDisappearanceToHotseatAnimator(
290             View taskbarIcon, View hotseatIcon, int hotseatTop) {
291         ArrayList<Animator> animators = new ArrayList<>();
292 
293         animators.add(ObjectAnimator.ofFloat(
294                 taskbarIcon,
295                 View.TRANSLATION_Y,
296                 0,
297                 (hotseatTop + hotseatIcon.getTop()) - (getTop() + taskbarIcon.getTop())));
298         animators.add(ObjectAnimator.ofFloat(
299                 taskbarIcon, View.TRANSLATION_X, 0, hotseatIcon.getLeft() - taskbarIcon.getLeft()));
300         animators.add(ObjectAnimator.ofFloat(
301                 taskbarIcon,
302                 View.SCALE_X,
303                 1f,
304                 (float) hotseatIcon.getWidth() / (float) taskbarIcon.getWidth()));
305         animators.add(ObjectAnimator.ofFloat(
306                 taskbarIcon,
307                 View.SCALE_Y,
308                 1f,
309                 (float) hotseatIcon.getHeight() / (float) taskbarIcon.getHeight()));
310         animators.add(ObjectAnimator.ofFloat(taskbarIcon, View.ALPHA, 1f, 0f));
311 
312         AnimatorSet animatorSet = new AnimatorSet();
313 
314         animatorSet.playTogether(animators);
315         animatorSet.addListener(new AnimatorListenerAdapter() {
316             @Override
317             public void onAnimationEnd(Animator animation) {
318                 super.onAnimationEnd(animation);
319                 taskbarIcon.setVisibility(INVISIBLE);
320             }
321 
322             @Override
323             public void onAnimationStart(Animator animation) {
324                 super.onAnimationStart(animation);
325                 taskbarIcon.setVisibility(VISIBLE);
326             }
327         });
328 
329         return animatorSet;
330     }
331 
createIconAppearanceFromHotseatAnimator( View taskbarIcon, View hotseatIcon, int hotseatTop)332     private Animator createIconAppearanceFromHotseatAnimator(
333             View taskbarIcon, View hotseatIcon, int hotseatTop) {
334         ArrayList<Animator> animators = new ArrayList<>();
335 
336         animators.add(ObjectAnimator.ofFloat(
337                 taskbarIcon,
338                 View.TRANSLATION_Y,
339                 (hotseatTop + hotseatIcon.getTop()) - (getTop() + taskbarIcon.getTop()),
340                 0));
341         animators.add(ObjectAnimator.ofFloat(
342                 taskbarIcon, View.TRANSLATION_X, hotseatIcon.getLeft() - taskbarIcon.getLeft(), 0));
343         animators.add(ObjectAnimator.ofFloat(
344                 taskbarIcon,
345                 View.SCALE_X,
346                 (float) hotseatIcon.getWidth() / (float) taskbarIcon.getWidth(),
347                 1f));
348         animators.add(ObjectAnimator.ofFloat(
349                 taskbarIcon,
350                 View.SCALE_Y,
351                 (float) hotseatIcon.getHeight() / (float) taskbarIcon.getHeight(),
352                 1f));
353         animators.add(ObjectAnimator.ofFloat(taskbarIcon, View.ALPHA, 0f, 1f));
354 
355         AnimatorSet animatorSet = new AnimatorSet();
356 
357         animatorSet.playTogether(animators);
358         animatorSet.addListener(new AnimatorListenerAdapter() {
359             @Override
360             public void onAnimationStart(Animator animation) {
361                 super.onAnimationStart(animation);
362                 taskbarIcon.setVisibility(VISIBLE);
363             }
364         });
365 
366         return animatorSet;
367     }
368 }
369