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.util.ArrayMap; 20 import android.util.FloatProperty; 21 import android.util.Log; 22 import android.view.View; 23 24 import com.android.launcher3.Utilities; 25 26 /** 27 * Allows to combine multiple values set by several sources. 28 * 29 * The various sources are meant to use [set], providing different `setterIndex` params. When it is 30 * not set, 0 is used. This is meant to cover the case multiple animations are going on at the same 31 * time. 32 * 33 * This class behaves similarly to [MultiValueAlpha], but is meant to be more abstract and reusable. 34 * It sets the multiplication of all values, bounded to the max and the min values. 35 * 36 * @param <T> Type where to apply the property. 37 */ 38 public class MultiScalePropertyFactory<T extends View> { 39 40 private static final boolean DEBUG = false; 41 private static final String TAG = "MultiScaleProperty"; 42 private final String mName; 43 private final ArrayMap<Integer, MultiScaleProperty> mProperties = new ArrayMap<>(); 44 45 // This is an optimization for cases when set is called repeatedly with the same setterIndex. 46 private float mMinOfOthers = 0; 47 private float mMaxOfOthers = 0; 48 private float mMultiplicationOfOthers = 0; 49 private Integer mLastIndexSet = -1; 50 private float mLastAggregatedValue = 1.0f; 51 MultiScalePropertyFactory(String name)52 public MultiScalePropertyFactory(String name) { 53 mName = name; 54 } 55 56 /** Returns the [MultiFloatProperty] associated with [inx], creating it if not present. */ get(Integer index)57 public FloatProperty<T> get(Integer index) { 58 return mProperties.computeIfAbsent(index, 59 (k) -> new MultiScaleProperty(index, mName + "_" + index)); 60 } 61 62 /** 63 * Each [setValue] will be aggregated with the other properties values created by the 64 * corresponding factory. 65 */ 66 class MultiScaleProperty extends FloatProperty<T> { 67 private final int mInx; 68 private float mValue = 1.0f; 69 MultiScaleProperty(int inx, String name)70 MultiScaleProperty(int inx, String name) { 71 super(name); 72 mInx = inx; 73 } 74 75 @Override setValue(T obj, float newValue)76 public void setValue(T obj, float newValue) { 77 if (mLastIndexSet != mInx) { 78 mMinOfOthers = Float.MAX_VALUE; 79 mMaxOfOthers = Float.MIN_VALUE; 80 mMultiplicationOfOthers = 1.0f; 81 mProperties.forEach((key, property) -> { 82 if (key != mInx) { 83 mMinOfOthers = Math.min(mMinOfOthers, property.mValue); 84 mMaxOfOthers = Math.max(mMaxOfOthers, property.mValue); 85 mMultiplicationOfOthers *= property.mValue; 86 } 87 }); 88 mLastIndexSet = mInx; 89 } 90 float minValue = Math.min(mMinOfOthers, newValue); 91 float maxValue = Math.max(mMaxOfOthers, newValue); 92 float multValue = mMultiplicationOfOthers * newValue; 93 mLastAggregatedValue = Utilities.boundToRange(multValue, minValue, maxValue); 94 mValue = newValue; 95 apply(obj, mLastAggregatedValue); 96 97 if (DEBUG) { 98 Log.d(TAG, "name=" + mName 99 + " newValue=" + newValue + " mInx=" + mInx 100 + " aggregated=" + mLastAggregatedValue + " others= " + mProperties); 101 } 102 } 103 104 @Override get(T view)105 public Float get(T view) { 106 // The scale of the view should match mLastAggregatedValue. Still, if it has been 107 // changed without using this property, it can differ. As this get method is usually 108 // used to set the starting point on an animation, this would result in some jumps 109 // when the view scale is different than the last aggregated value. To stay on the 110 // safe side, let's return the real view scale. 111 return view.getScaleX(); 112 } 113 114 @Override toString()115 public String toString() { 116 return String.valueOf(mValue); 117 } 118 } 119 apply(View view, float value)120 protected void apply(View view, float value) { 121 view.setScaleX(value); 122 view.setScaleY(value); 123 } 124 } 125