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