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.animation.ValueAnimator;
23 import android.annotation.ColorInt;
24 import android.content.Context;
25 import android.graphics.Outline;
26 import android.graphics.Rect;
27 import android.util.AttributeSet;
28 import android.view.View;
29 import android.view.ViewOutlineProvider;
30 
31 import androidx.annotation.NonNull;
32 import androidx.annotation.Nullable;
33 import androidx.cardview.widget.CardView;
34 import androidx.constraintlayout.widget.ConstraintLayout;
35 
36 import com.android.launcher3.R;
37 
38 import java.util.ArrayList;
39 
40 /**
41  * Helper View for the gesture tutorial mock previous app task view.
42  *
43  * This helper class allows animating from a single-row layout to a two-row layout as seen in
44  * large screen devices.
45  */
46 public class AnimatedTaskView extends ConstraintLayout {
47 
48     private View mFullTaskView;
49     private CardView mTopTaskView;
50     private CardView mBottomTaskView;
51 
52     private ViewOutlineProvider mTaskViewOutlineProvider = null;
53     private final Rect mTaskViewAnimatedRect = new Rect();
54     private float mTaskViewAnimatedRadius;
55 
AnimatedTaskView(@onNull Context context)56     public AnimatedTaskView(@NonNull Context context) {
57         super(context);
58     }
59 
AnimatedTaskView(@onNull Context context, @Nullable AttributeSet attrs)60     public AnimatedTaskView(@NonNull Context context,
61             @Nullable AttributeSet attrs) {
62         super(context, attrs);
63     }
64 
AnimatedTaskView( @onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr)65     public AnimatedTaskView(
66             @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
67         super(context, attrs, defStyleAttr);
68     }
69 
AnimatedTaskView( @onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)70     public AnimatedTaskView(
71             @NonNull Context context,
72             @Nullable AttributeSet attrs,
73             int defStyleAttr,
74             int defStyleRes) {
75         super(context, attrs, defStyleAttr, defStyleRes);
76     }
77 
78     @Override
onFinishInflate()79     protected void onFinishInflate() {
80         super.onFinishInflate();
81 
82         mFullTaskView = findViewById(R.id.full_task_view);
83         mTopTaskView = findViewById(R.id.top_task_view);
84         mBottomTaskView = findViewById(R.id.bottom_task_view);
85 
86         setToSingleRowLayout(false);
87     }
88 
createAnimationToMultiRowLayout()89     AnimatorSet createAnimationToMultiRowLayout() {
90         if (mTaskViewOutlineProvider == null) {
91             // This is an illegal state.
92             return null;
93         }
94         Outline startOutline = new Outline();
95         mTaskViewOutlineProvider.getOutline(this, startOutline);
96         Rect outlineStartRect = new Rect();
97         startOutline.getRect(outlineStartRect);
98         int endRectBottom = mTopTaskView.getHeight();
99         float outlineStartRadius = startOutline.getRadius();
100         float outlineEndRadius = getContext().getResources().getDimensionPixelSize(
101                 R.dimen.gesture_tutorial_small_task_view_corner_radius);
102 
103         ValueAnimator outlineAnimator = ValueAnimator.ofFloat(0f, 1f);
104         outlineAnimator.addUpdateListener(valueAnimator -> {
105             float progress = (float) valueAnimator.getAnimatedValue();
106             mTaskViewAnimatedRect.bottom = (int) (outlineStartRect.bottom
107                     + progress * (endRectBottom - outlineStartRect.bottom));
108             mTaskViewAnimatedRadius = outlineStartRadius
109                     + progress * (outlineEndRadius - outlineStartRadius);
110             mFullTaskView.invalidateOutline();
111         });
112         outlineAnimator.addListener(new AnimatorListenerAdapter() {
113             @Override
114             public void onAnimationStart(Animator animation) {
115                 super.onAnimationStart(animation);
116 
117                 mTaskViewAnimatedRect.set(outlineStartRect);
118                 mTaskViewAnimatedRadius = outlineStartRadius;
119 
120                 mFullTaskView.setClipToOutline(true);
121                 mFullTaskView.setOutlineProvider(new ViewOutlineProvider() {
122                     @Override
123                     public void getOutline(View view, Outline outline) {
124                         outline.setRoundRect(mTaskViewAnimatedRect, mTaskViewAnimatedRadius);
125                     }
126                 });
127             }
128 
129             @Override
130             public void onAnimationEnd(Animator animation) {
131                 super.onAnimationEnd(animation);
132                 mFullTaskView.setOutlineProvider(mTaskViewOutlineProvider);
133             }
134 
135             @Override
136             public void onAnimationCancel(Animator animation) {
137                 super.onAnimationCancel(animation);
138                 mFullTaskView.setOutlineProvider(mTaskViewOutlineProvider);
139             }
140         });
141 
142         ArrayList<Animator> animations = new ArrayList<>();
143         animations.add(ObjectAnimator.ofFloat(
144                 mBottomTaskView, View.TRANSLATION_X, -mBottomTaskView.getWidth(), 0));
145         animations.add(outlineAnimator);
146 
147         AnimatorSet animatorSet = new AnimatorSet();
148         animatorSet.playTogether(animations);
149         animatorSet.addListener(new AnimatorListenerAdapter() {
150             @Override
151             public void onAnimationStart(Animator animation) {
152                 super.onAnimationStart(animation);
153                 setToSingleRowLayout(true);
154 
155                 setPadding(0, outlineStartRect.top, 0, getHeight() - outlineStartRect.bottom);
156             }
157 
158             @Override
159             public void onAnimationEnd(Animator animation) {
160                 super.onAnimationEnd(animation);
161                 setToMultiRowLayout();
162             }
163 
164             @Override
165             public void onAnimationCancel(Animator animation) {
166                 super.onAnimationCancel(animation);
167                 setToMultiRowLayout();
168             }
169         });
170 
171         return animatorSet;
172     }
173 
setToSingleRowLayout(boolean forAnimation)174     void setToSingleRowLayout(boolean forAnimation) {
175         mFullTaskView.setVisibility(VISIBLE);
176         mTopTaskView.setVisibility(INVISIBLE);
177         mBottomTaskView.setVisibility(forAnimation ? VISIBLE : INVISIBLE);
178     }
179 
setToMultiRowLayout()180     void setToMultiRowLayout() {
181         mFullTaskView.setVisibility(INVISIBLE);
182         mTopTaskView.setVisibility(VISIBLE);
183         mBottomTaskView.setVisibility(VISIBLE);
184     }
185 
setFakeTaskViewFillColor(@olorInt int colorResId)186     void setFakeTaskViewFillColor(@ColorInt int colorResId) {
187         mFullTaskView.setBackgroundColor(colorResId);
188         mTopTaskView.setCardBackgroundColor(colorResId);
189         mBottomTaskView.setCardBackgroundColor(colorResId);
190     }
191 
192     @Override
setClipToOutline(boolean clipToOutline)193     public void setClipToOutline(boolean clipToOutline) {
194         mFullTaskView.setClipToOutline(clipToOutline);
195     }
196 
197     @Override
setOutlineProvider(ViewOutlineProvider provider)198     public void setOutlineProvider(ViewOutlineProvider provider) {
199         mTaskViewOutlineProvider = provider;
200         mFullTaskView.setOutlineProvider(mTaskViewOutlineProvider);
201     }
202 }
203