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 androidx.constraintlayout.core.motion;
17 
18 import androidx.constraintlayout.core.motion.utils.TypedValues;
19 
20 /**
21  * Defines non standard Attributes
22  */
23 public class CustomVariable {
24     private static final String TAG = "TransitionLayout";
25     String mName;
26     private int mType;
27     private int mIntegerValue = Integer.MIN_VALUE;
28     private float mFloatValue = Float.NaN;
29     private String mStringValue = null;
30     boolean mBooleanValue;
31 
32     // @TODO: add description
copy()33     public CustomVariable copy() {
34         return new CustomVariable(this);
35     }
36 
CustomVariable(CustomVariable c)37     public CustomVariable(CustomVariable c) {
38         mName = c.mName;
39         mType = c.mType;
40         mIntegerValue = c.mIntegerValue;
41         mFloatValue = c.mFloatValue;
42         mStringValue = c.mStringValue;
43         mBooleanValue = c.mBooleanValue;
44     }
45 
CustomVariable(String name, int type, String value)46     public CustomVariable(String name, int type, String value) {
47         mName = name;
48         mType = type;
49         mStringValue = value;
50     }
51 
CustomVariable(String name, int type, int value)52     public CustomVariable(String name, int type, int value) {
53         mName = name;
54         mType = type;
55         if (type == TypedValues.Custom.TYPE_FLOAT) { // catch int ment for float
56             mFloatValue = value;
57         } else {
58             mIntegerValue = value;
59         }
60     }
61 
CustomVariable(String name, int type, float value)62     public CustomVariable(String name, int type, float value) {
63         mName = name;
64         mType = type;
65         mFloatValue = value;
66     }
67 
CustomVariable(String name, int type, boolean value)68     public CustomVariable(String name, int type, boolean value) {
69         mName = name;
70         mType = type;
71         mBooleanValue = value;
72     }
73 
74     // @TODO: add description
colorString(int v)75     public static String colorString(int v) {
76         String str = "00000000" + Integer.toHexString(v);
77         return "#" + str.substring(str.length() - 8);
78     }
79 
80     @Override
toString()81     public String toString() {
82         String str = mName + ':';
83         switch (mType) {
84             case TypedValues.Custom.TYPE_INT:
85                 return str + mIntegerValue;
86             case TypedValues.Custom.TYPE_FLOAT:
87                 return str + mFloatValue;
88             case TypedValues.Custom.TYPE_COLOR:
89                 return str + colorString(mIntegerValue);
90             case TypedValues.Custom.TYPE_STRING:
91                 return str + mStringValue;
92             case TypedValues.Custom.TYPE_BOOLEAN:
93                 return str + (Boolean) mBooleanValue;
94             case TypedValues.Custom.TYPE_DIMENSION:
95                 return str + mFloatValue;
96         }
97         return str + "????";
98     }
99 
getType()100     public int getType() {
101         return mType;
102     }
103 
getBooleanValue()104     public boolean getBooleanValue() {
105         return mBooleanValue;
106     }
107 
getFloatValue()108     public float getFloatValue() {
109         return mFloatValue;
110     }
111 
getColorValue()112     public int getColorValue() {
113         return mIntegerValue;
114     }
115 
getIntegerValue()116     public int getIntegerValue() {
117         return mIntegerValue;
118     }
119 
getStringValue()120     public String getStringValue() {
121         return mStringValue;
122     }
123 
124     /**
125      * Continuous types are interpolated they are fired only at
126      */
isContinuous()127     public boolean isContinuous() {
128         switch (mType) {
129             case TypedValues.Custom.TYPE_REFERENCE:
130             case TypedValues.Custom.TYPE_BOOLEAN:
131             case TypedValues.Custom.TYPE_STRING:
132                 return false;
133             default:
134                 return true;
135         }
136     }
137 
setFloatValue(float value)138     public void setFloatValue(float value) {
139         mFloatValue = value;
140     }
141 
setBooleanValue(boolean value)142     public void setBooleanValue(boolean value) {
143         mBooleanValue = value;
144     }
145 
setIntValue(int value)146     public void setIntValue(int value) {
147         mIntegerValue = value;
148     }
149 
setStringValue(String value)150     public void setStringValue(String value) {
151         mStringValue = value;
152     }
153 
154     /**
155      * The number of interpolation values that need to be interpolated
156      * Typically 1 but 3 for colors.
157      *
158      * @return Typically 1 but 3 for colors.
159      */
numberOfInterpolatedValues()160     public int numberOfInterpolatedValues() {
161         switch (mType) {
162             case TypedValues.Custom.TYPE_COLOR:
163                 return 4;
164             default:
165                 return 1;
166         }
167     }
168 
169     /**
170      * Transforms value to a float for the purpose of interpolation
171      *
172      * @return interpolation value
173      */
getValueToInterpolate()174     public float getValueToInterpolate() {
175         switch (mType) {
176             case TypedValues.Custom.TYPE_INT:
177                 return mIntegerValue;
178             case TypedValues.Custom.TYPE_FLOAT:
179                 return mFloatValue;
180             case TypedValues.Custom.TYPE_COLOR:
181                 throw new RuntimeException("Color does not have a single color to interpolate");
182             case TypedValues.Custom.TYPE_STRING:
183                 throw new RuntimeException("Cannot interpolate String");
184             case TypedValues.Custom.TYPE_BOOLEAN:
185                 return mBooleanValue ? 1 : 0;
186             case TypedValues.Custom.TYPE_DIMENSION:
187                 return mFloatValue;
188         }
189         return Float.NaN;
190     }
191 
192     // @TODO: add description
getValuesToInterpolate(float[] ret)193     public void getValuesToInterpolate(float[] ret) {
194         switch (mType) {
195             case TypedValues.Custom.TYPE_INT:
196                 ret[0] = mIntegerValue;
197                 break;
198             case TypedValues.Custom.TYPE_FLOAT:
199                 ret[0] = mFloatValue;
200                 break;
201             case TypedValues.Custom.TYPE_COLOR:
202                 int a = 0xFF & (mIntegerValue >> 24);
203                 int r = 0xFF & (mIntegerValue >> 16);
204                 int g = 0xFF & (mIntegerValue >> 8);
205                 int b = 0xFF & mIntegerValue;
206                 float f_r = (float) Math.pow(r / 255.0f, 2.2);
207                 float f_g = (float) Math.pow(g / 255.0f, 2.2);
208                 float f_b = (float) Math.pow(b / 255.0f, 2.2);
209                 ret[0] = f_r;
210                 ret[1] = f_g;
211                 ret[2] = f_b;
212                 ret[3] = a / 255f;
213                 break;
214             case TypedValues.Custom.TYPE_STRING:
215                 throw new RuntimeException("Cannot interpolate String");
216             case TypedValues.Custom.TYPE_BOOLEAN:
217                 ret[0] = mBooleanValue ? 1 : 0;
218                 break;
219             case TypedValues.Custom.TYPE_DIMENSION:
220                 ret[0] = mFloatValue;
221                 break;
222         }
223     }
224 
225     // @TODO: add description
setValue(float[] value)226     public void setValue(float[] value) {
227         switch (mType) {
228             case TypedValues.Custom.TYPE_REFERENCE:
229             case TypedValues.Custom.TYPE_INT:
230                 mIntegerValue = (int) value[0];
231                 break;
232             case TypedValues.Custom.TYPE_FLOAT:
233             case TypedValues.Custom.TYPE_DIMENSION:
234                 mFloatValue = value[0];
235                 break;
236             case TypedValues.Custom.TYPE_COLOR:
237                 float f_r = value[0];
238                 float f_g = value[1];
239                 float f_b = value[2];
240                 int r = 0xFF & Math.round((float) Math.pow(f_r, 1.0 / 2.0) * 255.0f);
241                 int g = 0xFF & Math.round((float) Math.pow(f_g, 1.0 / 2.0) * 255.0f);
242                 int b = 0xFF & Math.round((float) Math.pow(f_b, 1.0 / 2.0) * 255.0f);
243                 int a = 0xFF & Math.round(value[3] * 255.0f);
244                 mIntegerValue = a << 24 | r << 16 | g << 8 | b;
245                 break;
246             case TypedValues.Custom.TYPE_STRING:
247                 throw new RuntimeException("Cannot interpolate String");
248             case TypedValues.Custom.TYPE_BOOLEAN:
249                 mBooleanValue = value[0] > 0.5;
250                 break;
251         }
252     }
253 
254     // @TODO: add description
hsvToRgb(float hue, float saturation, float value)255     public static int hsvToRgb(float hue, float saturation, float value) {
256         int h = (int) (hue * 6);
257         float f = hue * 6 - h;
258         int p = (int) (0.5f + 255 * value * (1 - saturation));
259         int q = (int) (0.5f + 255 * value * (1 - f * saturation));
260         int t = (int) (0.5f + 255 * value * (1 - (1 - f) * saturation));
261         int v = (int) (0.5f + 255 * value);
262         switch (h) {
263             case 0:
264                 return 0XFF000000 | (v << 16) + (t << 8) + p;
265             case 1:
266                 return 0XFF000000 | (q << 16) + (v << 8) + p;
267             case 2:
268                 return 0XFF000000 | (p << 16) + (v << 8) + t;
269             case 3:
270                 return 0XFF000000 | (p << 16) + (q << 8) + v;
271             case 4:
272                 return 0XFF000000 | (t << 16) + (p << 8) + v;
273             case 5:
274                 return 0XFF000000 | (v << 16) + (p << 8) + q;
275 
276         }
277         return 0;
278     }
279 
280     /**
281      * test if the two attributes are different
282      */
diff(CustomVariable customAttribute)283     public boolean diff(CustomVariable customAttribute) {
284         if (customAttribute == null || mType != customAttribute.mType) {
285             return false;
286         }
287         switch (mType) {
288             case TypedValues.Custom.TYPE_INT:
289             case TypedValues.Custom.TYPE_REFERENCE:
290                 return mIntegerValue == customAttribute.mIntegerValue;
291             case TypedValues.Custom.TYPE_FLOAT:
292                 return mFloatValue == customAttribute.mFloatValue;
293             case TypedValues.Custom.TYPE_COLOR:
294                 return mIntegerValue == customAttribute.mIntegerValue;
295             case TypedValues.Custom.TYPE_STRING:
296                 return mIntegerValue == customAttribute.mIntegerValue;
297             case TypedValues.Custom.TYPE_BOOLEAN:
298                 return mBooleanValue == customAttribute.mBooleanValue;
299             case TypedValues.Custom.TYPE_DIMENSION:
300                 return mFloatValue == customAttribute.mFloatValue;
301         }
302         return false;
303     }
304 
CustomVariable(String name, int attributeType)305     public CustomVariable(String name, int attributeType) {
306         mName = name;
307         mType = attributeType;
308     }
309 
CustomVariable(String name, int attributeType, Object value)310     public CustomVariable(String name, int attributeType, Object value) {
311         mName = name;
312         mType = attributeType;
313         setValue(value);
314     }
315 
CustomVariable(CustomVariable source, Object value)316     public CustomVariable(CustomVariable source, Object value) {
317         mName = source.mName;
318         mType = source.mType;
319         setValue(value);
320 
321     }
322 
323     // @TODO: add description
setValue(Object value)324     public void setValue(Object value) {
325         switch (mType) {
326             case TypedValues.Custom.TYPE_REFERENCE:
327             case TypedValues.Custom.TYPE_INT:
328                 mIntegerValue = (Integer) value;
329                 break;
330             case TypedValues.Custom.TYPE_FLOAT:
331                 mFloatValue = (Float) value;
332                 break;
333             case TypedValues.Custom.TYPE_COLOR:
334                 mIntegerValue = (Integer) value;
335                 break;
336             case TypedValues.Custom.TYPE_STRING:
337                 mStringValue = (String) value;
338                 break;
339             case TypedValues.Custom.TYPE_BOOLEAN:
340                 mBooleanValue = (Boolean) value;
341                 break;
342             case TypedValues.Custom.TYPE_DIMENSION:
343                 mFloatValue = (Float) value;
344                 break;
345         }
346     }
347 
clamp(int c)348     private static int clamp(int c) {
349         int n = 255;
350         c &= ~(c >> 31);
351         c -= n;
352         c &= (c >> 31);
353         c += n;
354         return c;
355     }
356 
357     // @TODO: add description
getInterpolatedColor(float[] value)358     public int getInterpolatedColor(float[] value) {
359         int r = clamp((int) ((float) Math.pow(value[0], 1.0 / 2.2) * 255.0f));
360         int g = clamp((int) ((float) Math.pow(value[1], 1.0 / 2.2) * 255.0f));
361         int b = clamp((int) ((float) Math.pow(value[2], 1.0 / 2.2) * 255.0f));
362         int a = clamp((int) (value[3] * 255.0f));
363         int color = (a << 24) | (r << 16) | (g << 8) | b;
364         return color;
365     }
366 
367     // @TODO: add description
setInterpolatedValue(MotionWidget view, float[] value)368     public void setInterpolatedValue(MotionWidget view, float[] value) {
369 
370         switch (mType) {
371             case TypedValues.Custom.TYPE_INT:
372                 view.setCustomAttribute(mName, mType, (int) value[0]);
373                 break;
374             case TypedValues.Custom.TYPE_COLOR:
375                 int r = clamp((int) ((float) Math.pow(value[0], 1.0 / 2.2) * 255.0f));
376                 int g = clamp((int) ((float) Math.pow(value[1], 1.0 / 2.2) * 255.0f));
377                 int b = clamp((int) ((float) Math.pow(value[2], 1.0 / 2.2) * 255.0f));
378                 int a = clamp((int) (value[3] * 255.0f));
379                 int color = (a << 24) | (r << 16) | (g << 8) | b;
380                 view.setCustomAttribute(mName, mType, color);
381                 break;
382             case TypedValues.Custom.TYPE_REFERENCE:
383             case TypedValues.Custom.TYPE_STRING:
384                 throw new RuntimeException("unable to interpolate " + mName);
385             case TypedValues.Custom.TYPE_BOOLEAN:
386                 view.setCustomAttribute(mName, mType, value[0] > 0.5f);
387                 break;
388             case TypedValues.Custom.TYPE_DIMENSION:
389             case TypedValues.Custom.TYPE_FLOAT:
390                 view.setCustomAttribute(mName, mType, value[0]);
391                 break;
392         }
393     }
394 
395     // @TODO: add description
rgbaTocColor(float r, float g, float b, float a)396     public static int rgbaTocColor(float r, float g, float b, float a) {
397         int ir = clamp((int) (r * 255f));
398         int ig = clamp((int) (g * 255f));
399         int ib = clamp((int) (b * 255f));
400         int ia = clamp((int) (a * 255f));
401         int color = (ia << 24) | (ir << 16) | (ig << 8) | ib;
402         return color;
403     }
404 
405     // @TODO: add description
applyToWidget(MotionWidget view)406     public void applyToWidget(MotionWidget view) {
407         switch (mType) {
408             case TypedValues.Custom.TYPE_INT:
409             case TypedValues.Custom.TYPE_COLOR:
410             case TypedValues.Custom.TYPE_REFERENCE:
411                 view.setCustomAttribute(mName, mType, mIntegerValue);
412                 break;
413             case TypedValues.Custom.TYPE_STRING:
414                 view.setCustomAttribute(mName, mType, mStringValue);
415                 break;
416             case TypedValues.Custom.TYPE_BOOLEAN:
417                 view.setCustomAttribute(mName, mType, mBooleanValue);
418                 break;
419             case TypedValues.Custom.TYPE_DIMENSION:
420             case TypedValues.Custom.TYPE_FLOAT:
421                 view.setCustomAttribute(mName, mType, mFloatValue);
422                 break;
423         }
424     }
425 
getName()426     public String getName() {
427         return mName;
428     }
429 }
430