PropertyValuesHolder_Delegate.java revision 29ed07524ce0fc2e5950f5340d306247145d0efa
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 17package android.animation; 18 19import com.android.layoutlib.bridge.Bridge; 20import com.android.layoutlib.bridge.impl.DelegateManager; 21import com.android.tools.layoutlib.annotations.LayoutlibDelegate; 22 23import java.lang.reflect.InvocationTargetException; 24import java.lang.reflect.Method; 25import java.util.Arrays; 26import java.util.HashMap; 27import 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") 45class 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 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$d", methodName, nArgs); 68 synchronized (sMethodIndexLock) { 69 Long methodId = METHOD_NAME_TO_ID.get(methodIndexName); 70 71 if (methodId != null) { 72 // The method was already registered 73 return methodId; 74 } 75 76 Class[] args = new Class[nArgs]; 77 Method method = null; 78 for (Class typeVariant : types) { 79 for (int i = 0; i < nArgs; i++) { 80 args[i] = typeVariant; 81 } 82 try { 83 method = targetClass.getDeclaredMethod(methodName, args); 84 } catch (NoSuchMethodException ignore) { 85 } 86 } 87 88 if (method != null) { 89 methodId = sNextId++; 90 ID_TO_METHOD.put(methodId, method); 91 METHOD_NAME_TO_ID.put(methodIndexName, methodId); 92 93 return methodId; 94 } 95 } 96 97 // Method not found 98 return 0; 99 } 100 101 private static void callMethod(Object target, long methodID, Object... args) { 102 Method method = ID_TO_METHOD.get(methodID); 103 assert method != null; 104 105 try { 106 method.setAccessible(true); 107 method.invoke(target, args); 108 } catch (IllegalAccessException e) { 109 Bridge.getLog().error(null, "Unable to update property during animation", e, null); 110 } catch (InvocationTargetException e) { 111 Bridge.getLog().error(null, "Unable to update property during animation", e, null); 112 } 113 } 114 115 @LayoutlibDelegate 116 /*package*/ static long nGetIntMethod(Class<?> targetClass, String methodName) { 117 return nGetMultipleIntMethod(targetClass, methodName, 1); 118 } 119 120 @LayoutlibDelegate 121 /*package*/ static long nGetFloatMethod(Class<?> targetClass, String methodName) { 122 return nGetMultipleFloatMethod(targetClass, methodName, 1); 123 } 124 125 @LayoutlibDelegate 126 /*package*/ static long nGetMultipleIntMethod(Class<?> targetClass, String methodName, 127 int numParams) { 128 return registerMethod(targetClass, methodName, INTEGER_VARIANTS, numParams); 129 } 130 131 @LayoutlibDelegate 132 /*package*/ static long nGetMultipleFloatMethod(Class<?> targetClass, String methodName, 133 int numParams) { 134 return registerMethod(targetClass, methodName, FLOAT_VARIANTS, numParams); 135 } 136 137 @LayoutlibDelegate 138 /*package*/ static void nCallIntMethod(Object target, long methodID, int arg) { 139 callMethod(target, methodID, arg); 140 } 141 142 @LayoutlibDelegate 143 /*package*/ static void nCallFloatMethod(Object target, long methodID, float arg) { 144 callMethod(target, methodID, arg); 145 } 146 147 @LayoutlibDelegate 148 /*package*/ static void nCallTwoIntMethod(Object target, long methodID, int arg1, 149 int arg2) { 150 callMethod(target, methodID, arg1, arg2); 151 } 152 153 @LayoutlibDelegate 154 /*package*/ static void nCallFourIntMethod(Object target, long methodID, int arg1, 155 int arg2, int arg3, int arg4) { 156 callMethod(target, methodID, arg1, arg2, arg3, arg4); 157 } 158 159 @LayoutlibDelegate 160 /*package*/ static void nCallMultipleIntMethod(Object target, long methodID, 161 int[] args) { 162 assert args != null; 163 164 // Box parameters 165 Object[] params = new Object[args.length]; 166 for (int i = 0; i < args.length; i++) { 167 params[i] = args; 168 } 169 callMethod(target, methodID, params); 170 } 171 172 @LayoutlibDelegate 173 /*package*/ static void nCallTwoFloatMethod(Object target, long methodID, float arg1, 174 float arg2) { 175 callMethod(target, methodID, arg1, arg2); 176 } 177 178 @LayoutlibDelegate 179 /*package*/ static void nCallFourFloatMethod(Object target, long methodID, float arg1, 180 float arg2, float arg3, float arg4) { 181 callMethod(target, methodID, arg1, arg2, arg3, arg4); 182 } 183 184 @LayoutlibDelegate 185 /*package*/ static void nCallMultipleFloatMethod(Object target, long methodID, 186 float[] args) { 187 assert args != null; 188 189 // Box parameters 190 Object[] params = new Object[args.length]; 191 for (int i = 0; i < args.length; i++) { 192 params[i] = args; 193 } 194 callMethod(target, methodID, params); 195 } 196} 197