1 /* 2 * Copyright (C) 2010 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 android.animation; 18 19 import com.android.layoutlib.bridge.Bridge; 20 import com.android.layoutlib.bridge.impl.DelegateManager; 21 import com.android.tools.layoutlib.annotations.LayoutlibDelegate; 22 23 import java.lang.reflect.InvocationTargetException; 24 import java.lang.reflect.Method; 25 import java.util.Arrays; 26 import java.util.HashMap; 27 import java.util.Map; 28 29 /** 30 * Delegate implementing the native methods of android.animation.PropertyValuesHolder 31 * 32 * Through the layoutlib_create tool, the original native methods of PropertyValuesHolder have been 33 * replaced by calls to methods of the same name in this delegate class. 34 * 35 * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager} 36 * around to map int to instance of the delegate. 37 * 38 * The main goal of this class' methods are to provide a native way to access setters and getters 39 * on some object. We override these methods to use reflection since the original reflection 40 * implementation of the PropertyValuesHolder won't be able to access protected methods. 41 * 42 */ 43 public class PropertyValuesHolder_Delegate { 44 // This code is copied from android.animation.PropertyValuesHolder and must be kept in sync 45 // We try several different types when searching for appropriate setter/getter functions. 46 // The caller may have supplied values in a type that does not match the setter/getter 47 // functions (such as the integers 0 and 1 to represent floating point values for alpha). 48 // Also, the use of generics in constructors means that we end up with the Object versions 49 // of primitive types (Float vs. float). But most likely, the setter/getter functions 50 // will take primitive types instead. 51 // So we supply an ordered array of other types to try before giving up. 52 private static Class[] FLOAT_VARIANTS = {float.class, Float.class, double.class, int.class, 53 Double.class, Integer.class}; 54 private static Class[] INTEGER_VARIANTS = {int.class, Integer.class, float.class, double.class, 55 Float.class, Double.class}; 56 57 private static final Object sMethodIndexLock = new Object(); 58 private static final Map<Long, Method> ID_TO_METHOD = new HashMap<Long, Method>(); 59 private static final Map<String, Long> METHOD_NAME_TO_ID = new HashMap<String, Long>(); 60 private static long sNextId = 1; 61 registerMethod(Class<?> targetClass, String methodName, Class[] types, int nArgs)62 private static long registerMethod(Class<?> targetClass, String methodName, Class[] types, 63 int nArgs) { 64 // Encode the number of arguments in the method name 65 String methodIndexName = String.format("%1$s.%2$s#%3$d", targetClass.getSimpleName(), 66 methodName, nArgs); 67 synchronized (sMethodIndexLock) { 68 Long methodId = METHOD_NAME_TO_ID.get(methodIndexName); 69 70 if (methodId != null) { 71 // The method was already registered, check whether the class loader has changed 72 Method method = ID_TO_METHOD.get(methodId); 73 if (targetClass.equals(method.getDeclaringClass())) { 74 return methodId; 75 } 76 } 77 78 Class[] args = new Class[nArgs]; 79 Method method = null; 80 for (Class typeVariant : types) { 81 for (int i = 0; i < nArgs; i++) { 82 args[i] = typeVariant; 83 } 84 try { 85 method = targetClass.getDeclaredMethod(methodName, args); 86 } catch (NoSuchMethodException ignore) { 87 } 88 } 89 90 if (method != null) { 91 if (methodId == null) { 92 methodId = sNextId++; 93 } 94 ID_TO_METHOD.put(methodId, method); 95 METHOD_NAME_TO_ID.put(methodIndexName, methodId); 96 97 return methodId; 98 } 99 } 100 101 // Method not found 102 return 0; 103 } 104 callMethod(Object target, long methodID, Object... args)105 private static void callMethod(Object target, long methodID, Object... args) { 106 Method method = ID_TO_METHOD.get(methodID); 107 assert method != null; 108 109 try { 110 method.setAccessible(true); 111 method.invoke(target, args); 112 } catch (IllegalAccessException | InvocationTargetException e) { 113 Bridge.getLog().error(null, "Unable to update property during animation", e, null, 114 null); 115 } 116 } 117 118 @LayoutlibDelegate nGetIntMethod(Class<?> targetClass, String methodName)119 /*package*/ static long nGetIntMethod(Class<?> targetClass, String methodName) { 120 return nGetMultipleIntMethod(targetClass, methodName, 1); 121 } 122 123 @LayoutlibDelegate nGetFloatMethod(Class<?> targetClass, String methodName)124 /*package*/ static long nGetFloatMethod(Class<?> targetClass, String methodName) { 125 return nGetMultipleFloatMethod(targetClass, methodName, 1); 126 } 127 128 @LayoutlibDelegate nGetMultipleIntMethod(Class<?> targetClass, String methodName, int numParams)129 /*package*/ static long nGetMultipleIntMethod(Class<?> targetClass, String methodName, 130 int numParams) { 131 return registerMethod(targetClass, methodName, INTEGER_VARIANTS, numParams); 132 } 133 134 @LayoutlibDelegate nGetMultipleFloatMethod(Class<?> targetClass, String methodName, int numParams)135 /*package*/ static long nGetMultipleFloatMethod(Class<?> targetClass, String methodName, 136 int numParams) { 137 return registerMethod(targetClass, methodName, FLOAT_VARIANTS, numParams); 138 } 139 140 @LayoutlibDelegate nCallIntMethod(Object target, long methodID, int arg)141 /*package*/ static void nCallIntMethod(Object target, long methodID, int arg) { 142 callMethod(target, methodID, arg); 143 } 144 145 @LayoutlibDelegate nCallFloatMethod(Object target, long methodID, float arg)146 /*package*/ static void nCallFloatMethod(Object target, long methodID, float arg) { 147 callMethod(target, methodID, arg); 148 } 149 150 @LayoutlibDelegate nCallTwoIntMethod(Object target, long methodID, int arg1, int arg2)151 /*package*/ static void nCallTwoIntMethod(Object target, long methodID, int arg1, 152 int arg2) { 153 callMethod(target, methodID, arg1, arg2); 154 } 155 156 @LayoutlibDelegate nCallFourIntMethod(Object target, long methodID, int arg1, int arg2, int arg3, int arg4)157 /*package*/ static void nCallFourIntMethod(Object target, long methodID, int arg1, 158 int arg2, int arg3, int arg4) { 159 callMethod(target, methodID, arg1, arg2, arg3, arg4); 160 } 161 162 @LayoutlibDelegate nCallMultipleIntMethod(Object target, long methodID, int[] args)163 /*package*/ static void nCallMultipleIntMethod(Object target, long methodID, 164 int[] args) { 165 assert args != null; 166 167 // Box parameters 168 Object[] params = new Object[args.length]; 169 for (int i = 0; i < args.length; i++) { 170 params[i] = args; 171 } 172 callMethod(target, methodID, params); 173 } 174 175 @LayoutlibDelegate nCallTwoFloatMethod(Object target, long methodID, float arg1, float arg2)176 /*package*/ static void nCallTwoFloatMethod(Object target, long methodID, float arg1, 177 float arg2) { 178 callMethod(target, methodID, arg1, arg2); 179 } 180 181 @LayoutlibDelegate nCallFourFloatMethod(Object target, long methodID, float arg1, float arg2, float arg3, float arg4)182 /*package*/ static void nCallFourFloatMethod(Object target, long methodID, float arg1, 183 float arg2, float arg3, float arg4) { 184 callMethod(target, methodID, arg1, arg2, arg3, arg4); 185 } 186 187 @LayoutlibDelegate nCallMultipleFloatMethod(Object target, long methodID, float[] args)188 /*package*/ static void nCallMultipleFloatMethod(Object target, long methodID, 189 float[] args) { 190 assert args != null; 191 192 // Box parameters 193 Object[] params = new Object[args.length]; 194 for (int i = 0; i < args.length; i++) { 195 params[i] = args; 196 } 197 callMethod(target, methodID, params); 198 } 199 clearCaches()200 public static void clearCaches() { 201 ID_TO_METHOD.clear(); 202 METHOD_NAME_TO_ID.clear(); 203 } 204 } 205