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 /*package*/ 44 @SuppressWarnings("unused") 45 class PropertyValuesHolder_Delegate { 46 // This code is copied from android.animation.PropertyValuesHolder and must be kept in sync 47 // We try several different types when searching for appropriate setter/getter functions. 48 // The caller may have supplied values in a type that does not match the setter/getter 49 // functions (such as the integers 0 and 1 to represent floating point values for alpha). 50 // Also, the use of generics in constructors means that we end up with the Object versions 51 // of primitive types (Float vs. float). But most likely, the setter/getter functions 52 // will take primitive types instead. 53 // So we supply an ordered array of other types to try before giving up. 54 private static Class[] FLOAT_VARIANTS = {float.class, Float.class, double.class, int.class, 55 Double.class, Integer.class}; 56 private static Class[] INTEGER_VARIANTS = {int.class, Integer.class, float.class, double.class, 57 Float.class, Double.class}; 58 59 private static final Object sMethodIndexLock = new Object(); 60 private static final Map<Long, Method> ID_TO_METHOD = new HashMap<Long, Method>(); 61 private static final Map<String, Long> METHOD_NAME_TO_ID = new HashMap<String, Long>(); 62 private static long sNextId = 1; 63 registerMethod(Class<?> targetClass, String methodName, Class[] types, int nArgs)64 private static long registerMethod(Class<?> targetClass, String methodName, Class[] types, 65 int nArgs) { 66 // Encode the number of arguments in the method name 67 String methodIndexName = String.format("%1$s.%2$s#%3$d", targetClass.getSimpleName(), 68 methodName, nArgs); 69 synchronized (sMethodIndexLock) { 70 Long methodId = METHOD_NAME_TO_ID.get(methodIndexName); 71 72 if (methodId != null) { 73 // The method was already registered 74 return methodId; 75 } 76 77 Class[] args = new Class[nArgs]; 78 Method method = null; 79 for (Class typeVariant : types) { 80 for (int i = 0; i < nArgs; i++) { 81 args[i] = typeVariant; 82 } 83 try { 84 method = targetClass.getDeclaredMethod(methodName, args); 85 } catch (NoSuchMethodException ignore) { 86 } 87 } 88 89 if (method != null) { 90 methodId = sNextId++; 91 ID_TO_METHOD.put(methodId, method); 92 METHOD_NAME_TO_ID.put(methodIndexName, methodId); 93 94 return methodId; 95 } 96 } 97 98 // Method not found 99 return 0; 100 } 101 callMethod(Object target, long methodID, Object... args)102 private static void callMethod(Object target, long methodID, Object... args) { 103 Method method = ID_TO_METHOD.get(methodID); 104 assert method != null; 105 106 try { 107 method.setAccessible(true); 108 method.invoke(target, args); 109 } catch (IllegalAccessException e) { 110 Bridge.getLog().error(null, "Unable to update property during animation", e, null); 111 } catch (InvocationTargetException e) { 112 Bridge.getLog().error(null, "Unable to update property during animation", e, null); 113 } 114 } 115 116 @LayoutlibDelegate nGetIntMethod(Class<?> targetClass, String methodName)117 /*package*/ static long nGetIntMethod(Class<?> targetClass, String methodName) { 118 return nGetMultipleIntMethod(targetClass, methodName, 1); 119 } 120 121 @LayoutlibDelegate nGetFloatMethod(Class<?> targetClass, String methodName)122 /*package*/ static long nGetFloatMethod(Class<?> targetClass, String methodName) { 123 return nGetMultipleFloatMethod(targetClass, methodName, 1); 124 } 125 126 @LayoutlibDelegate nGetMultipleIntMethod(Class<?> targetClass, String methodName, int numParams)127 /*package*/ static long nGetMultipleIntMethod(Class<?> targetClass, String methodName, 128 int numParams) { 129 return registerMethod(targetClass, methodName, INTEGER_VARIANTS, numParams); 130 } 131 132 @LayoutlibDelegate nGetMultipleFloatMethod(Class<?> targetClass, String methodName, int numParams)133 /*package*/ static long nGetMultipleFloatMethod(Class<?> targetClass, String methodName, 134 int numParams) { 135 return registerMethod(targetClass, methodName, FLOAT_VARIANTS, numParams); 136 } 137 138 @LayoutlibDelegate nCallIntMethod(Object target, long methodID, int arg)139 /*package*/ static void nCallIntMethod(Object target, long methodID, int arg) { 140 callMethod(target, methodID, arg); 141 } 142 143 @LayoutlibDelegate nCallFloatMethod(Object target, long methodID, float arg)144 /*package*/ static void nCallFloatMethod(Object target, long methodID, float arg) { 145 callMethod(target, methodID, arg); 146 } 147 148 @LayoutlibDelegate nCallTwoIntMethod(Object target, long methodID, int arg1, int arg2)149 /*package*/ static void nCallTwoIntMethod(Object target, long methodID, int arg1, 150 int arg2) { 151 callMethod(target, methodID, arg1, arg2); 152 } 153 154 @LayoutlibDelegate nCallFourIntMethod(Object target, long methodID, int arg1, int arg2, int arg3, int arg4)155 /*package*/ static void nCallFourIntMethod(Object target, long methodID, int arg1, 156 int arg2, int arg3, int arg4) { 157 callMethod(target, methodID, arg1, arg2, arg3, arg4); 158 } 159 160 @LayoutlibDelegate nCallMultipleIntMethod(Object target, long methodID, int[] args)161 /*package*/ static void nCallMultipleIntMethod(Object target, long methodID, 162 int[] args) { 163 assert args != null; 164 165 // Box parameters 166 Object[] params = new Object[args.length]; 167 for (int i = 0; i < args.length; i++) { 168 params[i] = args; 169 } 170 callMethod(target, methodID, params); 171 } 172 173 @LayoutlibDelegate nCallTwoFloatMethod(Object target, long methodID, float arg1, float arg2)174 /*package*/ static void nCallTwoFloatMethod(Object target, long methodID, float arg1, 175 float arg2) { 176 callMethod(target, methodID, arg1, arg2); 177 } 178 179 @LayoutlibDelegate nCallFourFloatMethod(Object target, long methodID, float arg1, float arg2, float arg3, float arg4)180 /*package*/ static void nCallFourFloatMethod(Object target, long methodID, float arg1, 181 float arg2, float arg3, float arg4) { 182 callMethod(target, methodID, arg1, arg2, arg3, arg4); 183 } 184 185 @LayoutlibDelegate nCallMultipleFloatMethod(Object target, long methodID, float[] args)186 /*package*/ static void nCallMultipleFloatMethod(Object target, long methodID, 187 float[] args) { 188 assert args != null; 189 190 // Box parameters 191 Object[] params = new Object[args.length]; 192 for (int i = 0; i < args.length; i++) { 193 params[i] = args; 194 } 195 callMethod(target, methodID, params); 196 } 197 } 198