Choreographer.java revision 96e942dabeeaaa9ab6df3a870668c6fe53d930da
196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown/*
296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * Copyright (C) 2011 The Android Open Source Project
396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown *
496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * Licensed under the Apache License, Version 2.0 (the "License");
596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * you may not use this file except in compliance with the License.
696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * You may obtain a copy of the License at
796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown *
896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown *      http://www.apache.org/licenses/LICENSE-2.0
996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown *
1096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * Unless required by applicable law or agreed to in writing, software
1196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * distributed under the License is distributed on an "AS IS" BASIS,
1296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * See the License for the specific language governing permissions and
1496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * limitations under the License.
1596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown */
1696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
1796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brownpackage android.view;
1896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
1996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brownimport com.android.internal.util.ArrayUtils;
2096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
2196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brownimport android.os.Handler;
2296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brownimport android.os.Looper;
2396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brownimport android.os.Message;
2496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brownimport android.os.SystemClock;
2596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brownimport android.os.SystemProperties;
2696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brownimport android.util.Log;
2796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
2896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown/**
2996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * Coodinates animations and drawing for UI on a particular thread.
3096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * @hide
3196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown */
3296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brownpublic final class Choreographer extends Handler {
3396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private static final String TAG = "Choreographer";
3496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private static final boolean DEBUG = false;
3596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
3696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // The default amount of time in ms between animation frames.
3796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // When vsync is not enabled, we want to have some idea of how long we should
3896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // wait before posting the next animation message.  It is important that the
3996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // default value be less than the true inter-frame delay on all devices to avoid
4096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // situations where we might skip frames by waiting too long (we must compensate
4196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // for jitter and hardware variations).  Regardless of this value, the animation
4296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // and display loop is ultimately rate-limited by how fast new graphics buffers can
4396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // be dequeued.
4496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private static final long DEFAULT_FRAME_DELAY = 10;
4596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
4696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // The number of milliseconds between animation frames.
4796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private static long sFrameDelay = DEFAULT_FRAME_DELAY;
4896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
4996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // Thread local storage for the choreographer.
5096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private static final ThreadLocal<Choreographer> sThreadInstance =
5196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            new ThreadLocal<Choreographer>() {
5296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        @Override
5396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        protected Choreographer initialValue() {
5496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            Looper looper = Looper.myLooper();
5596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            if (looper == null) {
5696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                throw new IllegalStateException("The current thread must have a looper!");
5796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            }
5896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            return new Choreographer(looper);
5996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
6096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    };
6196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
6296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // System property to enable/disable vsync for animations and drawing.
6396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // Enabled by default.
6496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private static final boolean USE_VSYNC = SystemProperties.getBoolean(
6596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            "debug.choreographer.vsync", true);
6696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
6796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // System property to enable/disable the use of the vsync / animation timer
6896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // for drawing rather than drawing immediately.
6996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // Enabled by default.
7096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private static final boolean USE_ANIMATION_TIMER_FOR_DRAW = SystemProperties.getBoolean(
7196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            "debug.choreographer.animdraw", true);
7296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
7396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private static final int MSG_DO_ANIMATION = 0;
7496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private static final int MSG_DO_DRAW = 1;
7596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
7696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private final Looper mLooper;
7796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
7896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private OnAnimateListener[] mOnAnimateListeners;
7996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private OnDrawListener[] mOnDrawListeners;
8096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
8196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private boolean mAnimationScheduled;
8296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private boolean mDrawScheduled;
8396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private FrameDisplayEventReceiver mFrameDisplayEventReceiver;
8496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private long mLastAnimationTime;
8596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private long mLastDrawTime;
8696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
8796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private Choreographer(Looper looper) {
8896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        super(looper);
8996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        mLooper = looper;
9096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        mLastAnimationTime = Long.MIN_VALUE;
9196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        mLastDrawTime = Long.MIN_VALUE;
9296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
9396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
9496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    /**
9596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * Gets the choreographer for this thread.
9696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * Must be called on the UI thread.
9796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     *
9896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * @return The choreographer for this thread.
9996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * @throws IllegalStateException if the thread does not have a looper.
10096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     */
10196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    public static Choreographer getInstance() {
10296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        return sThreadInstance.get();
10396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
10496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
10596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    /**
10696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * The amount of time, in milliseconds, between each frame of the animation. This is a
10796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * requested time that the animation will attempt to honor, but the actual delay between
10896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * frames may be different, depending on system load and capabilities. This is a static
10996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * function because the same delay will be applied to all animations, since they are all
11096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * run off of a single timing loop.
11196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     *
11296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * The frame delay may be ignored when the animation system uses an external timing
11396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * source, such as the display refresh rate (vsync), to govern animations.
11496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     *
11596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * @return the requested time between frames, in milliseconds
11696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     */
11796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    public static long getFrameDelay() {
11896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        return sFrameDelay;
11996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
12096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
12196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    /**
12296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * The amount of time, in milliseconds, between each frame of the animation. This is a
12396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * requested time that the animation will attempt to honor, but the actual delay between
12496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * frames may be different, depending on system load and capabilities. This is a static
12596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * function because the same delay will be applied to all animations, since they are all
12696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * run off of a single timing loop.
12796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     *
12896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * The frame delay may be ignored when the animation system uses an external timing
12996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * source, such as the display refresh rate (vsync), to govern animations.
13096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     *
13196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * @param frameDelay the requested time between frames, in milliseconds
13296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     */
13396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    public static void setFrameDelay(long frameDelay) {
13496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        sFrameDelay = frameDelay;
13596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
13696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
13796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    /**
13896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * Schedules animation (and drawing) to occur on the next frame synchronization boundary.
13996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * Must be called on the UI thread.
14096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     */
14196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    public void scheduleAnimation() {
14296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        if (!mAnimationScheduled) {
14396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            mAnimationScheduled = true;
14496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            if (USE_VSYNC) {
14596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                if (DEBUG) {
14696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                    Log.d(TAG, "Scheduling vsync for animation.");
14796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                }
14896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                if (mFrameDisplayEventReceiver == null) {
14996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                    mFrameDisplayEventReceiver = new FrameDisplayEventReceiver(mLooper);
15096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                }
15196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                mFrameDisplayEventReceiver.scheduleVsync();
15296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            } else {
15396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                final long now = SystemClock.uptimeMillis();
15496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                final long nextAnimationTime = Math.max(mLastAnimationTime + sFrameDelay, now);
15596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                if (DEBUG) {
15696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                    Log.d(TAG, "Scheduling animation in " + (nextAnimationTime - now) + " ms.");
15796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                }
15896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                sendEmptyMessageAtTime(MSG_DO_ANIMATION, nextAnimationTime);
15996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            }
16096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
16196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
16296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
16396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    /**
16496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * Schedules drawing to occur on the next frame synchronization boundary.
16596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * Must be called on the UI thread.
16696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     */
16796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    public void scheduleDraw() {
16896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        if (!mDrawScheduled) {
16996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            mDrawScheduled = true;
17096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            if (USE_ANIMATION_TIMER_FOR_DRAW) {
17196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                scheduleAnimation();
17296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            } else {
17396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                if (DEBUG) {
17496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                    Log.d(TAG, "Scheduling draw immediately.");
17596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                }
17696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                sendEmptyMessage(MSG_DO_DRAW);
17796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            }
17896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
17996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
18096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
18196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    @Override
18296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    public void handleMessage(Message msg) {
18396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        switch (msg.what) {
18496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            case MSG_DO_ANIMATION:
18596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                doAnimation();
18696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                break;
18796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            case MSG_DO_DRAW:
18896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                doDraw();
18996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                break;
19096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
19196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
19296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
19396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private void doAnimation() {
19496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        if (mAnimationScheduled) {
19596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            mAnimationScheduled = false;
19696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
19796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            final long start = SystemClock.uptimeMillis();
19896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            if (DEBUG) {
19996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                Log.d(TAG, "Performing animation: " + Math.max(0, start - mLastAnimationTime)
20096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                        + " ms have elapsed since previous animation.");
20196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            }
20296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            mLastAnimationTime = start;
20396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
20496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            final OnAnimateListener[] listeners = mOnAnimateListeners;
20596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            if (listeners != null) {
20696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                for (int i = 0; i < listeners.length; i++) {
20796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                    listeners[i].onAnimate();
20896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                }
20996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            }
21096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
21196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            if (DEBUG) {
21296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                Log.d(TAG, "Animation took " + (SystemClock.uptimeMillis() - start) + " ms.");
21396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            }
21496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
21596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
21696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        if (USE_ANIMATION_TIMER_FOR_DRAW) {
21796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            doDraw();
21896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
21996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
22096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
22196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private void doDraw() {
22296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        if (mDrawScheduled) {
22396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            mDrawScheduled = false;
22496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
22596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            final long start = SystemClock.uptimeMillis();
22696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            if (DEBUG) {
22796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                Log.d(TAG, "Performing draw: " + Math.max(0, start - mLastDrawTime)
22896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                        + " ms have elapsed since previous draw.");
22996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            }
23096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            mLastDrawTime = start;
23196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
23296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            final OnDrawListener[] listeners = mOnDrawListeners;
23396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            if (listeners != null) {
23496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                for (int i = 0; i < listeners.length; i++) {
23596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                    listeners[i].onDraw();
23696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                }
23796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            }
23896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
23996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            if (DEBUG) {
24096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                Log.d(TAG, "Draw took " + (SystemClock.uptimeMillis() - start) + " ms.");
24196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            }
24296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
24396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
24496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
24596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    /**
24696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * Adds an animation listener.
24796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * Must be called on the UI thread.
24896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     *
24996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * @param listener The listener to add.
25096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     */
25196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    public void addOnAnimateListener(OnAnimateListener listener) {
25296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        if (listener == null) {
25396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            throw new IllegalArgumentException("listener must not be null");
25496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
25596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
25696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        if (DEBUG) {
25796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            Log.d(TAG, "Adding onAnimate listener: " + listener);
25896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
25996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
26096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        mOnAnimateListeners = ArrayUtils.appendElement(OnAnimateListener.class,
26196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                mOnAnimateListeners, listener);
26296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
26396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
26496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    /**
26596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * Removes an animation listener.
26696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * Must be called on the UI thread.
26796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     *
26896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * @param listener The listener to remove.
26996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     */
27096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    public void removeOnAnimateListener(OnAnimateListener listener) {
27196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        if (listener == null) {
27296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            throw new IllegalArgumentException("listener must not be null");
27396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
27496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
27596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        if (DEBUG) {
27696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            Log.d(TAG, "Removing onAnimate listener: " + listener);
27796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
27896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
27996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        mOnAnimateListeners = ArrayUtils.removeElement(OnAnimateListener.class,
28096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                mOnAnimateListeners, listener);
28196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        stopTimingLoopIfNoListeners();
28296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
28396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
28496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    /**
28596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * Adds a draw listener.
28696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * Must be called on the UI thread.
28796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     *
28896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * @param listener The listener to add.
28996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     */
29096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    public void addOnDrawListener(OnDrawListener listener) {
29196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        if (listener == null) {
29296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            throw new IllegalArgumentException("listener must not be null");
29396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
29496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
29596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        if (DEBUG) {
29696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            Log.d(TAG, "Adding onDraw listener: " + listener);
29796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
29896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
29996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        mOnDrawListeners = ArrayUtils.appendElement(OnDrawListener.class,
30096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                mOnDrawListeners, listener);
30196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
30296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
30396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    /**
30496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * Removes a draw listener.
30596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * Must be called on the UI thread.
30696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     *
30796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * @param listener The listener to remove.
30896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     */
30996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    public void removeOnDrawListener(OnDrawListener listener) {
31096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        if (listener == null) {
31196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            throw new IllegalArgumentException("listener must not be null");
31296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
31396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
31496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        if (DEBUG) {
31596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            Log.d(TAG, "Removing onDraw listener: " + listener);
31696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
31796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
31896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        mOnDrawListeners = ArrayUtils.removeElement(OnDrawListener.class,
31996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                mOnDrawListeners, listener);
32096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        stopTimingLoopIfNoListeners();
32196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
32296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
32396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private void stopTimingLoopIfNoListeners() {
32496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        if (mOnDrawListeners == null && mOnAnimateListeners == null) {
32596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            if (DEBUG) {
32696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                Log.d(TAG, "Stopping timing loop.");
32796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            }
32896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
32996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            if (mAnimationScheduled) {
33096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                mAnimationScheduled = false;
33196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                if (!USE_VSYNC) {
33296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                    removeMessages(MSG_DO_ANIMATION);
33396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                }
33496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            }
33596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
33696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            if (mDrawScheduled) {
33796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                mDrawScheduled = false;
33896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                if (!USE_ANIMATION_TIMER_FOR_DRAW) {
33996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                    removeMessages(MSG_DO_DRAW);
34096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                }
34196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            }
34296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
34396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            if (mFrameDisplayEventReceiver != null) {
34496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                mFrameDisplayEventReceiver.dispose();
34596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                mFrameDisplayEventReceiver = null;
34696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            }
34796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
34896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
34996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
35096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    /**
35196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * Listens for animation frame timing events.
35296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     */
35396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    public static interface OnAnimateListener {
35496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        /**
35596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown         * Called to animate properties before drawing the frame.
35696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown         */
35796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        public void onAnimate();
35896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
35996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
36096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    /**
36196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * Listens for draw frame timing events.
36296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     */
36396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    public static interface OnDrawListener {
36496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        /**
36596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown         * Called to draw the frame.
36696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown         */
36796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        public void onDraw();
36896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
36996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
37096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private final class FrameDisplayEventReceiver extends DisplayEventReceiver {
37196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        public FrameDisplayEventReceiver(Looper looper) {
37296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            super(looper);
37396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
37496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
37596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        @Override
37696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        public void onVsync(long timestampNanos, int frame) {
37796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            doAnimation();
37896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
37996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
38096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown}
381