• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.util;
17 
18 import android.animation.Animator;
19 import android.animation.AnimatorListenerAdapter;
20 import android.animation.ValueAnimator;
21 import android.graphics.Canvas;
22 import android.graphics.ColorFilter;
23 import android.graphics.PixelFormat;
24 import android.graphics.drawable.Drawable;
25 import android.util.FloatProperty;
26 import android.view.View;
27 
28 import com.android.launcher3.Utilities;
29 import com.android.quickstep.views.RecentsView;
30 import com.android.quickstep.views.TaskThumbnailView;
31 import com.android.quickstep.views.TaskView;
32 
33 public class TaskViewDrawable extends Drawable {
34 
35     public static final FloatProperty<TaskViewDrawable> PROGRESS =
36             new FloatProperty<TaskViewDrawable>("progress") {
37                 @Override
38                 public void setValue(TaskViewDrawable taskViewDrawable, float v) {
39                     taskViewDrawable.setProgress(v);
40                 }
41 
42                 @Override
43                 public Float get(TaskViewDrawable taskViewDrawable) {
44                     return taskViewDrawable.mProgress;
45                 }
46             };
47 
48     /**
49      * The progress at which we play the atomic icon scale animation.
50      */
51     private static final float ICON_SCALE_THRESHOLD = 0.95f;
52 
53     private final RecentsView mParent;
54     private final View mIconView;
55     private final int[] mIconPos;
56 
57     private final TaskThumbnailView mThumbnailView;
58 
59     private final ClipAnimationHelper mClipAnimationHelper;
60 
61     private float mProgress = 1;
62     private boolean mPassedIconScaleThreshold;
63     private ValueAnimator mIconScaleAnimator;
64     private float mIconScale;
65 
TaskViewDrawable(TaskView tv, RecentsView parent)66     public TaskViewDrawable(TaskView tv, RecentsView parent) {
67         mParent = parent;
68         mIconView = tv.getIconView();
69         mIconPos = new int[2];
70         mIconScale = mIconView.getScaleX();
71         Utilities.getDescendantCoordRelativeToAncestor(mIconView, parent, mIconPos, true);
72 
73         mThumbnailView = tv.getThumbnail();
74         mClipAnimationHelper = new ClipAnimationHelper();
75         mClipAnimationHelper.fromTaskThumbnailView(mThumbnailView, parent);
76     }
77 
setProgress(float progress)78     public void setProgress(float progress) {
79         mProgress = progress;
80         mParent.invalidate();
81         boolean passedIconScaleThreshold = progress <= ICON_SCALE_THRESHOLD;
82         if (mPassedIconScaleThreshold != passedIconScaleThreshold) {
83             mPassedIconScaleThreshold = passedIconScaleThreshold;
84             animateIconScale(mPassedIconScaleThreshold ? 0 : 1);
85         }
86     }
87 
animateIconScale(float toScale)88     private void animateIconScale(float toScale) {
89         if (mIconScaleAnimator != null) {
90             mIconScaleAnimator.cancel();
91         }
92         mIconScaleAnimator = ValueAnimator.ofFloat(mIconScale, toScale);
93         mIconScaleAnimator.addUpdateListener(valueAnimator -> {
94             mIconScale = (float) valueAnimator.getAnimatedValue();
95             if (mProgress > ICON_SCALE_THRESHOLD) {
96                 // Speed up the icon scale to ensure it is 1 when progress is 1.
97                 float iconProgress = (mProgress - ICON_SCALE_THRESHOLD) / (1 - ICON_SCALE_THRESHOLD);
98                 if (iconProgress > mIconScale) {
99                     mIconScale = iconProgress;
100                 }
101             }
102             invalidateSelf();
103         });
104         mIconScaleAnimator.addListener(new AnimatorListenerAdapter() {
105             @Override
106             public void onAnimationEnd(Animator animation) {
107                 mIconScaleAnimator = null;
108             }
109         });
110         mIconScaleAnimator.setDuration(TaskView.SCALE_ICON_DURATION);
111         mIconScaleAnimator.start();
112     }
113 
114     @Override
draw(Canvas canvas)115     public void draw(Canvas canvas) {
116         canvas.save();
117         canvas.translate(mParent.getScrollX(), mParent.getScrollY());
118         mClipAnimationHelper.drawForProgress(mThumbnailView, canvas, mProgress);
119         canvas.restore();
120 
121         canvas.save();
122         canvas.translate(mIconPos[0], mIconPos[1]);
123         canvas.scale(mIconScale, mIconScale, mIconView.getWidth() / 2, mIconView.getHeight() / 2);
124         mIconView.draw(canvas);
125         canvas.restore();
126     }
127 
getClipAnimationHelper()128     public ClipAnimationHelper getClipAnimationHelper() {
129         return mClipAnimationHelper;
130     }
131 
132     @Override
setAlpha(int i)133     public void setAlpha(int i) { }
134 
135     @Override
setColorFilter(ColorFilter colorFilter)136     public void setColorFilter(ColorFilter colorFilter) { }
137 
138     @Override
getOpacity()139     public int getOpacity() {
140         return PixelFormat.TRANSLUCENT;
141     }
142 }
143