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