• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 
17 package com.android.launcher3.util;
18 
19 import android.animation.Animator;
20 import android.animation.ObjectAnimator;
21 import android.util.FloatProperty;
22 import android.util.Log;
23 
24 import java.io.PrintWriter;
25 import java.util.Arrays;
26 
27 /**
28  * Allows to combine multiple values set by several sources.
29  *
30  * The various sources are meant to use [set], providing different `setterIndex` params. When it is
31  * not set, 0 is used. This is meant to cover the case multiple animations are going on at the same
32  * time.
33  *
34  * This class behaves similarly to [MultiValueAlpha], but is meant to be more abstract and reusable.
35  * It aggregate all values using the provided [aggregator].
36  *
37  * @param <T> Type where to apply the property.
38  */
39 public class MultiPropertyFactory<T> {
40 
41     public static final FloatProperty<MultiPropertyFactory<?>.MultiProperty> MULTI_PROPERTY_VALUE =
42             new FloatProperty<MultiPropertyFactory<?>.MultiProperty>("value") {
43 
44                 @Override
45                 public Float get(MultiPropertyFactory<?>.MultiProperty property) {
46                     return property.mValue;
47                 }
48 
49                 @Override
50                 public void setValue(MultiPropertyFactory<?>.MultiProperty property, float value) {
51                     property.setValue(value);
52                 }
53             };
54 
55     private static final boolean DEBUG = false;
56     private static final String TAG = "MultiPropertyFactory";
57     private final MultiPropertyFactory<?>.MultiProperty[] mProperties;
58 
59     // This is an optimization for cases when set is called repeatedly with the same setterIndex.
60     private float mAggregationOfOthers = 0f;
61     private int mLastIndexSet = -1;
62 
63     protected final T mTarget;
64     private final FloatProperty<T> mProperty;
65     private final FloatBiFunction mAggregator;
66 
67     /**
68      * Represents a function that accepts two float and produces a float.
69      */
70     public interface FloatBiFunction {
71         /**
72          * Applies this function to the given arguments.
73          */
apply(float a, float b)74         float apply(float a, float b);
75     }
76 
MultiPropertyFactory(T target, FloatProperty<T> property, int size, FloatBiFunction aggregator)77     public MultiPropertyFactory(T target, FloatProperty<T> property, int size,
78             FloatBiFunction aggregator) {
79         this(target, property, size, aggregator, 0);
80     }
81 
MultiPropertyFactory(T target, FloatProperty<T> property, int size, FloatBiFunction aggregator, float defaultPropertyValue)82     public MultiPropertyFactory(T target, FloatProperty<T> property, int size,
83             FloatBiFunction aggregator, float defaultPropertyValue) {
84         mTarget = target;
85         mProperty = property;
86         mAggregator = aggregator;
87 
88         mProperties = new MultiPropertyFactory<?>.MultiProperty[size];
89         for (int i = 0; i < size; i++) {
90             mProperties[i] = new MultiProperty(i, defaultPropertyValue);
91         }
92     }
93 
94     /** Returns the [MultiFloatProperty] associated with [inx], creating it if not present. */
get(int index)95     public MultiProperty get(int index) {
96         return (MultiProperty) mProperties[index];
97     }
98 
99     @Override
toString()100     public String toString() {
101         return Arrays.deepToString(mProperties);
102     }
103 
104     /**
105      * Dumps the alpha channel values to the given PrintWriter
106      *
107      * @param prefix String to be used before every line
108      * @param pw PrintWriter where the logs should be dumped
109      * @param label String used to help identify this object
110      * @param alphaIndexLabels Strings that represent each alpha channel, these should be entered
111      *                         in the order of the indexes they represent, starting from 0.
112      */
dump(String prefix, PrintWriter pw, String label, String... alphaIndexLabels)113     public void dump(String prefix, PrintWriter pw, String label, String... alphaIndexLabels) {
114         pw.println(prefix + label);
115 
116         String innerPrefix = prefix + '\t';
117         for (int i = 0; i < alphaIndexLabels.length; i++) {
118             if (i >= mProperties.length) {
119                 pw.println(innerPrefix + alphaIndexLabels[i] + " given for alpha index " + i
120                         + " however there are only " + mProperties.length + " alpha channels.");
121                 continue;
122             }
123             pw.println(innerPrefix + alphaIndexLabels[i] + "=" + get(i).getValue());
124         }
125     }
126 
127     /**
128      * Each [setValue] will be aggregated with the other properties values created by the
129      * corresponding factory.
130      */
131     public class MultiProperty {
132 
133         private final int mInx;
134         private final float mDefaultValue;
135         private float mValue;
136 
MultiProperty(int inx, float defaultValue)137         MultiProperty(int inx, float defaultValue) {
138             mInx = inx;
139             mDefaultValue = defaultValue;
140             mValue = defaultValue;
141         }
142 
setValue(float newValue)143         public void setValue(float newValue) {
144             if (mLastIndexSet != mInx) {
145                 mAggregationOfOthers = mDefaultValue;
146                 for (MultiPropertyFactory<?>.MultiProperty other : mProperties) {
147                     if (other.mInx != mInx) {
148                         mAggregationOfOthers =
149                                 mAggregator.apply(mAggregationOfOthers, other.mValue);
150                     }
151                 }
152 
153                 mLastIndexSet = mInx;
154             }
155             float lastAggregatedValue = mAggregator.apply(mAggregationOfOthers, newValue);
156             mValue = newValue;
157             apply(lastAggregatedValue);
158 
159             if (DEBUG) {
160                 Log.d(TAG, "name=" + mProperty.getName()
161                         + " target=" + mTarget.getClass()
162                         + " newValue=" + newValue
163                         + " mInx=" + mInx
164                         + " aggregated=" + lastAggregatedValue
165                         + " others= " + Arrays.deepToString(mProperties));
166             }
167         }
168 
getValue()169         public float getValue() {
170             return mValue;
171         }
172 
173         @Override
toString()174         public String toString() {
175             return String.valueOf(mValue);
176         }
177 
178         /**
179          * Creates and returns an Animator from the current value to the given value. Future
180          * animator on the same target automatically cancels the previous one.
181          */
animateToValue(float value)182         public Animator animateToValue(float value) {
183             ObjectAnimator animator = ObjectAnimator.ofFloat(this, MULTI_PROPERTY_VALUE, value);
184             animator.setAutoCancel(true);
185             return animator;
186         }
187     }
188 
apply(float value)189     protected void apply(float value) {
190         mProperty.set(mTarget, value);
191     }
192 }
193