1 /*
2  * Copyright (C) 2020 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.systemui.bubbles.animation;
17 
18 import android.graphics.Matrix;
19 
20 import androidx.dynamicanimation.animation.DynamicAnimation;
21 import androidx.dynamicanimation.animation.FloatPropertyCompat;
22 
23 /**
24  * Matrix whose scale properties can be animated using physics animations, via the {@link #SCALE_X}
25  * and {@link #SCALE_Y} FloatProperties.
26  *
27  * This is useful when you need to perform a scale animation with a pivot point, since pivot points
28  * are not supported by standard View scale operations but are supported by matrices.
29  *
30  * NOTE: DynamicAnimation assumes that all custom properties are denominated in pixels, and thus
31  * considers 1 to be the smallest user-visible change for custom properties. This means that if you
32  * animate {@link #SCALE_X} and {@link #SCALE_Y} to 3f, for example, the animation would have only
33  * three frames.
34  *
35  * To work around this, whenever animating to a desired scale value, animate to the value returned
36  * by {@link #getAnimatableValueForScaleFactor} instead. The SCALE_X and SCALE_Y properties will
37  * convert that (larger) value into the appropriate scale factor when scaling the matrix.
38  */
39 public class AnimatableScaleMatrix extends Matrix {
40 
41     /**
42      * The X value of the scale.
43      *
44      * NOTE: This must be set or animated to the value returned by
45      * {@link #getAnimatableValueForScaleFactor}, not the desired scale factor itself.
46      */
47     public static final FloatPropertyCompat<AnimatableScaleMatrix> SCALE_X =
48             new FloatPropertyCompat<AnimatableScaleMatrix>("matrixScaleX") {
49         @Override
50         public float getValue(AnimatableScaleMatrix object) {
51             return getAnimatableValueForScaleFactor(object.mScaleX);
52         }
53 
54         @Override
55         public void setValue(AnimatableScaleMatrix object, float value) {
56             object.setScaleX(value * DynamicAnimation.MIN_VISIBLE_CHANGE_SCALE);
57         }
58     };
59 
60     /**
61      * The Y value of the scale.
62      *
63      * NOTE: This must be set or animated to the value returned by
64      * {@link #getAnimatableValueForScaleFactor}, not the desired scale factor itself.
65      */
66     public static final FloatPropertyCompat<AnimatableScaleMatrix> SCALE_Y =
67             new FloatPropertyCompat<AnimatableScaleMatrix>("matrixScaleY") {
68                 @Override
69                 public float getValue(AnimatableScaleMatrix object) {
70                     return getAnimatableValueForScaleFactor(object.mScaleY);
71                 }
72 
73                 @Override
74                 public void setValue(AnimatableScaleMatrix object, float value) {
75                     object.setScaleY(value * DynamicAnimation.MIN_VISIBLE_CHANGE_SCALE);
76                 }
77             };
78 
79     private float mScaleX = 1f;
80     private float mScaleY = 1f;
81 
82     private float mPivotX = 0f;
83     private float mPivotY = 0f;
84 
85     /**
86      * Return the value to animate SCALE_X or SCALE_Y to in order to achieve the desired scale
87      * factor.
88      */
getAnimatableValueForScaleFactor(float scale)89     public static float getAnimatableValueForScaleFactor(float scale) {
90         return scale * (1f / DynamicAnimation.MIN_VISIBLE_CHANGE_SCALE);
91     }
92 
93     @Override
setScale(float sx, float sy, float px, float py)94     public void setScale(float sx, float sy, float px, float py) {
95         mScaleX = sx;
96         mScaleY = sy;
97         mPivotX = px;
98         mPivotY = py;
99         super.setScale(mScaleX, mScaleY, mPivotX, mPivotY);
100     }
101 
setScaleX(float scaleX)102     public void setScaleX(float scaleX) {
103         mScaleX = scaleX;
104         super.setScale(mScaleX, mScaleY, mPivotX, mPivotY);
105     }
106 
setScaleY(float scaleY)107     public void setScaleY(float scaleY) {
108         mScaleY = scaleY;
109         super.setScale(mScaleX, mScaleY, mPivotX, mPivotY);
110     }
111 
setPivotX(float pivotX)112     public void setPivotX(float pivotX) {
113         mPivotX = pivotX;
114         super.setScale(mScaleX, mScaleY, mPivotX, mPivotY);
115     }
116 
setPivotY(float pivotY)117     public void setPivotY(float pivotY) {
118         mPivotY = pivotY;
119         super.setScale(mScaleX, mScaleY, mPivotX, mPivotY);
120     }
121 
getScaleX()122     public float getScaleX() {
123         return mScaleX;
124     }
125 
getScaleY()126     public float getScaleY() {
127         return mScaleY;
128     }
129 
getPivotX()130     public float getPivotX() {
131         return mPivotX;
132     }
133 
getPivotY()134     public float getPivotY() {
135         return mPivotY;
136     }
137 
138     @Override
equals(Object obj)139     public boolean equals(Object obj) {
140         // Use object equality to allow this matrix to be used as a map key (which is required for
141         // PhysicsAnimator's animator caching).
142         return obj == this;
143     }
144 }
145