• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 static com.android.launcher3.config.FeatureFlags.ENABLE_NEW_GESTURE_NAV_TUTORIAL;
19 
20 import android.animation.Animator;
21 import android.animation.AnimatorListenerAdapter;
22 import android.animation.AnimatorSet;
23 import android.animation.ObjectAnimator;
24 import android.animation.ValueAnimator;
25 import android.annotation.ColorInt;
26 import android.content.Context;
27 import android.graphics.Matrix;
28 import android.graphics.Outline;
29 import android.graphics.Rect;
30 import android.util.AttributeSet;
31 import android.util.Log;
32 import android.view.View;
33 import android.view.ViewOutlineProvider;
34 import android.view.animation.ScaleAnimation;
35 import android.view.animation.TranslateAnimation;
36 import android.widget.ViewAnimator;
37 
38 import androidx.annotation.NonNull;
39 import androidx.annotation.Nullable;
40 import androidx.constraintlayout.widget.ConstraintLayout;
41 
42 import com.android.launcher3.R;
43 
44 import java.util.ArrayList;
45 
46 /**
47  * Helper View for the gesture tutorial mock previous app task view.
48  *
49  * This helper class allows animating from a single-row layout to a two-row layout as seen in
50  * large screen devices.
51  */
52 public class AnimatedTaskView extends ConstraintLayout {
53 
54     private View mFullTaskView;
55     private View mTopTaskView;
56     private View mBottomTaskView;
57 
58     private ViewOutlineProvider mTaskViewOutlineProvider = null;
59     private final Rect mTaskViewAnimatedRect = new Rect();
60     private float mTaskViewAnimatedRadius;
61 
AnimatedTaskView(@onNull Context context)62     public AnimatedTaskView(@NonNull Context context) {
63         super(context);
64     }
65 
AnimatedTaskView(@onNull Context context, @Nullable AttributeSet attrs)66     public AnimatedTaskView(@NonNull Context context,
67             @Nullable AttributeSet attrs) {
68         super(context, attrs);
69     }
70 
AnimatedTaskView( @onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr)71     public AnimatedTaskView(
72             @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
73         super(context, attrs, defStyleAttr);
74     }
75 
AnimatedTaskView( @onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)76     public AnimatedTaskView(
77             @NonNull Context context,
78             @Nullable AttributeSet attrs,
79             int defStyleAttr,
80             int defStyleRes) {
81         super(context, attrs, defStyleAttr, defStyleRes);
82     }
83 
84     @Override
onFinishInflate()85     protected void onFinishInflate() {
86         super.onFinishInflate();
87 
88         mFullTaskView = findViewById(R.id.full_task_view);
89         mTopTaskView = findViewById(R.id.top_task_view);
90         mBottomTaskView = findViewById(R.id.bottom_task_view);
91 
92         setToSingleRowLayout(false);
93     }
94 
animateToFillScreen(@ullable Runnable onAnimationEndCallback)95     void animateToFillScreen(@Nullable  Runnable onAnimationEndCallback) {
96 
97         AnimatorSet set = new AnimatorSet();
98         ArrayList<Animator> animations = new ArrayList<>();
99 
100         // center view
101         animations.add(ObjectAnimator.ofFloat(this, TRANSLATION_X, 0));
102 
103         // calculate full screen scaling, scale should be 1:1 for x and y
104         Matrix matrix = getAnimationMatrix();
105         float[] matrixValues = new float[9];
106         matrix.getValues(matrixValues);
107         float scaleX = matrixValues[Matrix.MSCALE_X];
108         float scaleToFullScreen = 1 / scaleX;
109 
110         // scale view to full screen
111         ValueAnimator scale = ValueAnimator.ofFloat(1f, scaleToFullScreen);
112         scale.addUpdateListener(animation -> {
113             float value = (float) animation.getAnimatedValue();
114             mFullTaskView.setScaleX(value);
115             mFullTaskView.setScaleY(value);
116         });
117 
118         animations.add(scale);
119         set.playSequentially(animations);
120 
121         set.addListener(new AnimatorListenerAdapter() {
122             @Override
123             public void onAnimationEnd(Animator animation) {
124                 super.onAnimationEnd(animation);
125                 if (onAnimationEndCallback != null) {
126                     onAnimationEndCallback.run();
127                 }
128             }
129         });
130 
131         set.start();
132     }
133 
createAnimationToMultiRowLayout()134     AnimatorSet createAnimationToMultiRowLayout() {
135         if (mTaskViewOutlineProvider == null) {
136             // This is an illegal state.
137             return null;
138         }
139         Outline startOutline = new Outline();
140         mTaskViewOutlineProvider.getOutline(this, startOutline);
141         Rect outlineStartRect = new Rect();
142         startOutline.getRect(outlineStartRect);
143         int endRectBottom = mTopTaskView.getHeight();
144         float outlineStartRadius = startOutline.getRadius();
145         float outlineEndRadius = getContext().getResources().getDimensionPixelSize(
146                 R.dimen.gesture_tutorial_small_task_view_corner_radius);
147 
148         ValueAnimator outlineAnimator = ValueAnimator.ofFloat(0f, 1f);
149         outlineAnimator.addUpdateListener(valueAnimator -> {
150             float progress = (float) valueAnimator.getAnimatedValue();
151             mTaskViewAnimatedRect.bottom = (int) (outlineStartRect.bottom
152                     + progress * (endRectBottom - outlineStartRect.bottom));
153             mTaskViewAnimatedRadius = outlineStartRadius
154                     + progress * (outlineEndRadius - outlineStartRadius);
155             mFullTaskView.invalidateOutline();
156         });
157         outlineAnimator.addListener(new AnimatorListenerAdapter() {
158             @Override
159             public void onAnimationStart(Animator animation) {
160                 super.onAnimationStart(animation);
161 
162                 mTaskViewAnimatedRect.set(outlineStartRect);
163                 mTaskViewAnimatedRadius = outlineStartRadius;
164 
165                 mFullTaskView.setClipToOutline(true);
166                 mFullTaskView.setOutlineProvider(new ViewOutlineProvider() {
167                     @Override
168                     public void getOutline(View view, Outline outline) {
169                         outline.setRoundRect(mTaskViewAnimatedRect, mTaskViewAnimatedRadius);
170                     }
171                 });
172             }
173 
174             @Override
175             public void onAnimationEnd(Animator animation) {
176                 super.onAnimationEnd(animation);
177                 mFullTaskView.setOutlineProvider(mTaskViewOutlineProvider);
178             }
179 
180             @Override
181             public void onAnimationCancel(Animator animation) {
182                 super.onAnimationCancel(animation);
183                 mFullTaskView.setOutlineProvider(mTaskViewOutlineProvider);
184             }
185         });
186 
187         ArrayList<Animator> animations = new ArrayList<>();
188         animations.add(ObjectAnimator.ofFloat(
189                 mBottomTaskView, View.TRANSLATION_X, -mBottomTaskView.getWidth(), 0));
190         animations.add(outlineAnimator);
191 
192         AnimatorSet animatorSet = new AnimatorSet();
193         animatorSet.playTogether(animations);
194         animatorSet.addListener(new AnimatorListenerAdapter() {
195             @Override
196             public void onAnimationStart(Animator animation) {
197                 super.onAnimationStart(animation);
198                 setToSingleRowLayout(true);
199 
200                 setPadding(0, outlineStartRect.top, 0, getHeight() - outlineStartRect.bottom);
201             }
202 
203             @Override
204             public void onAnimationEnd(Animator animation) {
205                 super.onAnimationEnd(animation);
206                 setToMultiRowLayout();
207             }
208 
209             @Override
210             public void onAnimationCancel(Animator animation) {
211                 super.onAnimationCancel(animation);
212                 setToMultiRowLayout();
213             }
214         });
215 
216         return animatorSet;
217     }
218 
setToSingleRowLayout(boolean forAnimation)219     void setToSingleRowLayout(boolean forAnimation) {
220         mFullTaskView.setVisibility(VISIBLE);
221         mTopTaskView.setVisibility(INVISIBLE);
222         mBottomTaskView.setVisibility(forAnimation ? VISIBLE : INVISIBLE);
223     }
224 
setToMultiRowLayout()225     void setToMultiRowLayout() {
226         mFullTaskView.setVisibility(INVISIBLE);
227         mTopTaskView.setVisibility(VISIBLE);
228         mBottomTaskView.setVisibility(VISIBLE);
229     }
230 
setFakeTaskViewFillColor(@olorInt int colorResId)231     void setFakeTaskViewFillColor(@ColorInt int colorResId) {
232         mFullTaskView.setBackgroundColor(colorResId);
233 
234         if (ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()){
235             mTopTaskView.getBackground().setTint(colorResId);
236             mBottomTaskView.getBackground().setTint(colorResId);
237         }
238     }
239 
240     @Override
setClipToOutline(boolean clipToOutline)241     public void setClipToOutline(boolean clipToOutline) {
242         mFullTaskView.setClipToOutline(clipToOutline);
243     }
244 
245     @Override
setOutlineProvider(ViewOutlineProvider provider)246     public void setOutlineProvider(ViewOutlineProvider provider) {
247         mTaskViewOutlineProvider = provider;
248         mFullTaskView.setOutlineProvider(mTaskViewOutlineProvider);
249     }
250 }
251