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