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 | InvocationTargetException e) { 110 Bridge.getLog().error(null, "Unable to update property during animation", e, null); 111 } 112 } 113 114 @LayoutlibDelegate nGetIntMethod(Class<?> targetClass, String methodName)115 /*package*/ static long nGetIntMethod(Class<?> targetClass, String methodName) { 116 return nGetMultipleIntMethod(targetClass, methodName, 1); 117 } 118 119 @LayoutlibDelegate nGetFloatMethod(Class<?> targetClass, String methodName)120 /*package*/ static long nGetFloatMethod(Class<?> targetClass, String methodName) { 121 return nGetMultipleFloatMethod(targetClass, methodName, 1); 122 } 123 124 @LayoutlibDelegate nGetMultipleIntMethod(Class<?> targetClass, String methodName, int numParams)125 /*package*/ static long nGetMultipleIntMethod(Class<?> targetClass, String methodName, 126 int numParams) { 127 return registerMethod(targetClass, methodName, INTEGER_VARIANTS, numParams); 128 } 129 130 @LayoutlibDelegate nGetMultipleFloatMethod(Class<?> targetClass, String methodName, int numParams)131 /*package*/ static long nGetMultipleFloatMethod(Class<?> targetClass, String methodName, 132 int numParams) { 133 return registerMethod(targetClass, methodName, FLOAT_VARIANTS, numParams); 134 } 135 136 @LayoutlibDelegate nCallIntMethod(Object target, long methodID, int arg)137 /*package*/ static void nCallIntMethod(Object target, long methodID, int arg) { 138 callMethod(target, methodID, arg); 139 } 140 141 @LayoutlibDelegate nCallFloatMethod(Object target, long methodID, float arg)142 /*package*/ static void nCallFloatMethod(Object target, long methodID, float arg) { 143 callMethod(target, methodID, arg); 144 } 145 146 @LayoutlibDelegate nCallTwoIntMethod(Object target, long methodID, int arg1, int arg2)147 /*package*/ static void nCallTwoIntMethod(Object target, long methodID, int arg1, 148 int arg2) { 149 callMethod(target, methodID, arg1, arg2); 150 } 151 152 @LayoutlibDelegate nCallFourIntMethod(Object target, long methodID, int arg1, int arg2, int arg3, int arg4)153 /*package*/ static void nCallFourIntMethod(Object target, long methodID, int arg1, 154 int arg2, int arg3, int arg4) { 155 callMethod(target, methodID, arg1, arg2, arg3, arg4); 156 } 157 158 @LayoutlibDelegate nCallMultipleIntMethod(Object target, long methodID, int[] args)159 /*package*/ static void nCallMultipleIntMethod(Object target, long methodID, 160 int[] args) { 161 assert args != null; 162 163 // Box parameters 164 Object[] params = new Object[args.length]; 165 for (int i = 0; i < args.length; i++) { 166 params[i] = args; 167 } 168 callMethod(target, methodID, params); 169 } 170 171 @LayoutlibDelegate nCallTwoFloatMethod(Object target, long methodID, float arg1, float arg2)172 /*package*/ static void nCallTwoFloatMethod(Object target, long methodID, float arg1, 173 float arg2) { 174 callMethod(target, methodID, arg1, arg2); 175 } 176 177 @LayoutlibDelegate nCallFourFloatMethod(Object target, long methodID, float arg1, float arg2, float arg3, float arg4)178 /*package*/ static void nCallFourFloatMethod(Object target, long methodID, float arg1, 179 float arg2, float arg3, float arg4) { 180 callMethod(target, methodID, arg1, arg2, arg3, arg4); 181 } 182 183 @LayoutlibDelegate nCallMultipleFloatMethod(Object target, long methodID, float[] args)184 /*package*/ static void nCallMultipleFloatMethod(Object target, long methodID, 185 float[] args) { 186 assert args != null; 187 188 // Box parameters 189 Object[] params = new Object[args.length]; 190 for (int i = 0; i < args.length; i++) { 191 params[i] = args; 192 } 193 callMethod(target, methodID, params); 194 } 195 } 196