Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 29ed0752 authored by Diego Perez's avatar Diego Perez
Browse files

Add support for Choreographer animations

First step to add support for Choreographer based animations. The
Choreographer_Delegate avoid using a handler so the animation callbacks
can be called on-demand (by using doFrame). This allows things like
frame by frame animation or selecting a specific frame, and doesn't need
a separate thread to run.

The CL also changes the System and SystemClock implementations to allow
to set specific times. Because animations heavily rely on the system
time, this allows controlling it. It can also be useful to ensure that
the rendering produces a deterministic result when using controls like
the Calendar widget.

Change-Id: Iff221d2698a82075cafbb60f341be01741f7aa13
parent 1bd58bfd
Loading
Loading
Loading
Loading
+0 −52
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.animation;

/**
 * A fake implementation of Animator which doesn't do anything.
 */
public class FakeAnimator extends Animator {
    @Override
    public long getStartDelay() {
        return 0;
    }

    @Override
    public void setStartDelay(long startDelay) {

    }

    @Override
    public Animator setDuration(long duration) {
        return this;
    }

    @Override
    public long getDuration() {
        return 0;
    }

    @Override
    public void setInterpolator(TimeInterpolator value) {

    }

    @Override
    public boolean isRunning() {
        return false;
    }
}
+106 −19
Original line number Original line Diff line number Diff line
@@ -16,9 +16,16 @@


package android.animation;
package android.animation;


import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;


import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
/**
 * Delegate implementing the native methods of android.animation.PropertyValuesHolder
 * Delegate implementing the native methods of android.animation.PropertyValuesHolder
 *
 *
@@ -29,81 +36,161 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 * around to map int to instance of the delegate.
 * around to map int to instance of the delegate.
 *
 *
 * The main goal of this class' methods are to provide a native way to access setters and getters
 * The main goal of this class' methods are to provide a native way to access setters and getters
 * on some object. In this case we want to default to using Java reflection instead so the native
 * on some object. We override these methods to use reflection since the original reflection
 * methods do nothing.
 * implementation of the PropertyValuesHolder won't be able to access protected methods.
 *
 *
 */
 */
/*package*/ class PropertyValuesHolder_Delegate {
/*package*/
@SuppressWarnings("unused")
class PropertyValuesHolder_Delegate {
    // This code is copied from android.animation.PropertyValuesHolder and must be kept in sync
    // We try several different types when searching for appropriate setter/getter functions.
    // The caller may have supplied values in a type that does not match the setter/getter
    // functions (such as the integers 0 and 1 to represent floating point values for alpha).
    // Also, the use of generics in constructors means that we end up with the Object versions
    // of primitive types (Float vs. float). But most likely, the setter/getter functions
    // will take primitive types instead.
    // So we supply an ordered array of other types to try before giving up.
    private static Class[] FLOAT_VARIANTS = {float.class, Float.class, double.class, int.class,
            Double.class, Integer.class};
    private static Class[] INTEGER_VARIANTS = {int.class, Integer.class, float.class, double.class,
            Float.class, Double.class};

    private static final Object sMethodIndexLock = new Object();
    private static final Map<Long, Method> ID_TO_METHOD = new HashMap<Long, Method>();
    private static final Map<String, Long> METHOD_NAME_TO_ID = new HashMap<String, Long>();
    private static long sNextId = 1;

    private static long registerMethod(Class<?> targetClass, String methodName, Class[] types,
            int nArgs) {
        // Encode the number of arguments in the method name
        String methodIndexName = String.format("%1$s#%2$d", methodName, nArgs);
        synchronized (sMethodIndexLock) {
            Long methodId = METHOD_NAME_TO_ID.get(methodIndexName);

            if (methodId != null) {
                // The method was already registered
                return methodId;
            }

            Class[] args = new Class[nArgs];
            Method method = null;
            for (Class typeVariant : types) {
                for (int i = 0; i < nArgs; i++) {
                    args[i] = typeVariant;
                }
                try {
                    method = targetClass.getDeclaredMethod(methodName, args);
                } catch (NoSuchMethodException ignore) {
                }
            }

            if (method != null) {
                methodId = sNextId++;
                ID_TO_METHOD.put(methodId, method);
                METHOD_NAME_TO_ID.put(methodIndexName, methodId);

                return methodId;
            }
        }

        // Method not found
        return 0;
    }

    private static void callMethod(Object target, long methodID, Object... args) {
        Method method = ID_TO_METHOD.get(methodID);
        assert method != null;

        try {
            method.setAccessible(true);
            method.invoke(target, args);
        } catch (IllegalAccessException e) {
            Bridge.getLog().error(null, "Unable to update property during animation", e, null);
        } catch (InvocationTargetException e) {
            Bridge.getLog().error(null, "Unable to update property during animation", e, null);
        }
    }


    @LayoutlibDelegate
    @LayoutlibDelegate
    /*package*/ static long nGetIntMethod(Class<?> targetClass, String methodName) {
    /*package*/ static long nGetIntMethod(Class<?> targetClass, String methodName) {
        // return 0 to force PropertyValuesHolder to use Java reflection.
        return nGetMultipleIntMethod(targetClass, methodName, 1);
        return 0;
    }
    }


    @LayoutlibDelegate
    @LayoutlibDelegate
    /*package*/ static long nGetFloatMethod(Class<?> targetClass, String methodName) {
    /*package*/ static long nGetFloatMethod(Class<?> targetClass, String methodName) {
        // return 0 to force PropertyValuesHolder to use Java reflection.
        return nGetMultipleFloatMethod(targetClass, methodName, 1);
        return 0;
    }
    }


    @LayoutlibDelegate
    @LayoutlibDelegate
    /*package*/ static long nGetMultipleIntMethod(Class<?> targetClass, String methodName,
    /*package*/ static long nGetMultipleIntMethod(Class<?> targetClass, String methodName,
            int numParams) {
            int numParams) {
        // TODO: return the right thing.
        return registerMethod(targetClass, methodName, INTEGER_VARIANTS, numParams);
        return 0;
    }
    }


    @LayoutlibDelegate
    @LayoutlibDelegate
    /*package*/ static long nGetMultipleFloatMethod(Class<?> targetClass, String methodName,
    /*package*/ static long nGetMultipleFloatMethod(Class<?> targetClass, String methodName,
            int numParams) {
            int numParams) {
        // TODO: return the right thing.
        return registerMethod(targetClass, methodName, FLOAT_VARIANTS, numParams);
        return 0;
    }
    }


    @LayoutlibDelegate
    @LayoutlibDelegate
    /*package*/ static void nCallIntMethod(Object target, long methodID, int arg) {
    /*package*/ static void nCallIntMethod(Object target, long methodID, int arg) {
        // do nothing
        callMethod(target, methodID, arg);
    }
    }


    @LayoutlibDelegate
    @LayoutlibDelegate
    /*package*/ static void nCallFloatMethod(Object target, long methodID, float arg) {
    /*package*/ static void nCallFloatMethod(Object target, long methodID, float arg) {
        // do nothing
        callMethod(target, methodID, arg);
    }
    }


    @LayoutlibDelegate
    @LayoutlibDelegate
    /*package*/ static void nCallTwoIntMethod(Object target, long methodID, int arg1,
    /*package*/ static void nCallTwoIntMethod(Object target, long methodID, int arg1,
            int arg2) {
            int arg2) {
        // do nothing
        callMethod(target, methodID, arg1, arg2);
    }
    }


    @LayoutlibDelegate
    @LayoutlibDelegate
    /*package*/ static void nCallFourIntMethod(Object target, long methodID, int arg1,
    /*package*/ static void nCallFourIntMethod(Object target, long methodID, int arg1,
            int arg2, int arg3, int arg4) {
            int arg2, int arg3, int arg4) {
        // do nothing
        callMethod(target, methodID, arg1, arg2, arg3, arg4);
    }
    }


    @LayoutlibDelegate
    @LayoutlibDelegate
    /*package*/ static void nCallMultipleIntMethod(Object target, long methodID,
    /*package*/ static void nCallMultipleIntMethod(Object target, long methodID,
            int[] args) {
            int[] args) {
        // do nothing
        assert args != null;

        // Box parameters
        Object[] params = new Object[args.length];
        for (int i = 0; i < args.length; i++) {
            params[i] = args;
        }
        callMethod(target, methodID, params);
    }
    }


    @LayoutlibDelegate
    @LayoutlibDelegate
    /*package*/ static void nCallTwoFloatMethod(Object target, long methodID, float arg1,
    /*package*/ static void nCallTwoFloatMethod(Object target, long methodID, float arg1,
            float arg2) {
            float arg2) {
        // do nothing
        callMethod(target, methodID, arg1, arg2);
    }
    }


    @LayoutlibDelegate
    @LayoutlibDelegate
    /*package*/ static void nCallFourFloatMethod(Object target, long methodID, float arg1,
    /*package*/ static void nCallFourFloatMethod(Object target, long methodID, float arg1,
            float arg2, float arg3, float arg4) {
            float arg2, float arg3, float arg4) {
        // do nothing
        callMethod(target, methodID, arg1, arg2, arg3, arg4);
    }
    }


    @LayoutlibDelegate
    @LayoutlibDelegate
    /*package*/ static void nCallMultipleFloatMethod(Object target, long methodID,
    /*package*/ static void nCallMultipleFloatMethod(Object target, long methodID,
            float[] args) {
            float[] args) {
        // do nothing
        assert args != null;

        // Box parameters
        Object[] params = new Object[args.length];
        for (int i = 0; i < args.length; i++) {
            params[i] = args;
        }
        callMethod(target, methodID, params);
    }
    }
}
}
+1 −1
Original line number Original line Diff line number Diff line
@@ -44,7 +44,7 @@ public final class PathMeasure_Delegate {


    // ---- delegate data ----
    // ---- delegate data ----
    // This governs how accurate the approximation of the Path is.
    // This governs how accurate the approximation of the Path is.
    private static final float PRECISION = 0.002f;
    private static final float PRECISION = 0.0002f;


    /**
    /**
     * Array containing the path points components. There are three components for each point:
     * Array containing the path points components. There are three components for each point:
+6 −8
Original line number Original line Diff line number Diff line
@@ -18,6 +18,7 @@ package android.os;


import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import com.android.tools.layoutlib.java.System_Delegate;


/**
/**
 * Delegate implementing the native methods of android.os.SystemClock
 * Delegate implementing the native methods of android.os.SystemClock
@@ -30,9 +31,6 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 *
 *
 */
 */
public class SystemClock_Delegate {
public class SystemClock_Delegate {
    private static long sBootTime = System.currentTimeMillis();
    private static long sBootTimeNano = System.nanoTime();

    /**
    /**
     * Returns milliseconds since boot, not counting time spent in deep sleep.
     * Returns milliseconds since boot, not counting time spent in deep sleep.
     * <b>Note:</b> This value may get reset occasionally (before it would
     * <b>Note:</b> This value may get reset occasionally (before it would
@@ -42,7 +40,7 @@ public class SystemClock_Delegate {
     */
     */
    @LayoutlibDelegate
    @LayoutlibDelegate
    /*package*/ static long uptimeMillis() {
    /*package*/ static long uptimeMillis() {
        return System.currentTimeMillis() - sBootTime;
        return System_Delegate.currentTimeMillis() - System_Delegate.bootTimeMillis();
    }
    }


    /**
    /**
@@ -52,7 +50,7 @@ public class SystemClock_Delegate {
     */
     */
    @LayoutlibDelegate
    @LayoutlibDelegate
    /*package*/ static long elapsedRealtime() {
    /*package*/ static long elapsedRealtime() {
        return System.currentTimeMillis() - sBootTime;
        return System_Delegate.currentTimeMillis() - System_Delegate.bootTimeMillis();
    }
    }


    /**
    /**
@@ -62,7 +60,7 @@ public class SystemClock_Delegate {
     */
     */
    @LayoutlibDelegate
    @LayoutlibDelegate
    /*package*/ static long elapsedRealtimeNanos() {
    /*package*/ static long elapsedRealtimeNanos() {
        return System.nanoTime() - sBootTimeNano;
        return System_Delegate.nanoTime() - System_Delegate.bootTime();
    }
    }


    /**
    /**
@@ -72,7 +70,7 @@ public class SystemClock_Delegate {
     */
     */
    @LayoutlibDelegate
    @LayoutlibDelegate
    /*package*/ static long currentThreadTimeMillis() {
    /*package*/ static long currentThreadTimeMillis() {
        return System.currentTimeMillis();
        return System_Delegate.currentTimeMillis();
    }
    }


    /**
    /**
@@ -84,7 +82,7 @@ public class SystemClock_Delegate {
     */
     */
    @LayoutlibDelegate
    @LayoutlibDelegate
    /*package*/ static long currentThreadTimeMicro() {
    /*package*/ static long currentThreadTimeMicro() {
        return System.currentTimeMillis() * 1000;
        return System_Delegate.currentTimeMillis() * 1000;
    }
    }


    /**
    /**
+34 −0
Original line number Original line Diff line number Diff line
@@ -17,6 +17,8 @@ package android.view;


import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;


import java.util.concurrent.atomic.AtomicReference;

/**
/**
 * Delegate used to provide new implementation of a select few methods of {@link Choreographer}
 * Delegate used to provide new implementation of a select few methods of {@link Choreographer}
 *
 *
@@ -25,9 +27,41 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 *
 *
 */
 */
public class Choreographer_Delegate {
public class Choreographer_Delegate {
    static final AtomicReference<Choreographer> mInstance = new AtomicReference<Choreographer>();

    @LayoutlibDelegate
    public static Choreographer getInstance() {
        if (mInstance.get() == null) {
            mInstance.compareAndSet(null, Choreographer.getInstance_Original());
        }

        return mInstance.get();
    }


    @LayoutlibDelegate
    @LayoutlibDelegate
    public static float getRefreshRate() {
    public static float getRefreshRate() {
        return 60.f;
        return 60.f;
    }
    }

    @LayoutlibDelegate
    static void scheduleVsyncLocked(Choreographer thisChoreographer) {
        // do nothing
    }

    public static void doFrame(long frameTimeNanos) {
        Choreographer thisChoreographer = Choreographer.getInstance();

        thisChoreographer.mLastFrameTimeNanos = frameTimeNanos;

        thisChoreographer.mFrameInfo.markInputHandlingStart();
        thisChoreographer.doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);

        thisChoreographer.mFrameInfo.markAnimationsStart();
        thisChoreographer.doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);

        thisChoreographer.mFrameInfo.markPerformTraversalsStart();
        thisChoreographer.doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);

        thisChoreographer.doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
    }
}
}
Loading