Choreographer.java revision 968588573cf516fd8cf33c133d06f06c67ca1785
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/**
298bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn * Coordinates animations and drawing for UI on a particular thread.
3087d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown *
3187d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown * This object is thread-safe.  Other threads can add and remove listeners
3287d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown * or schedule work to occur at a later time on the UI thread.
3387d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown *
3458aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown * Ensuring thread-safety is a little tricky because the {@link DisplayEventReceiver}
3558aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown * can only be accessed from the UI thread so operations that touch the event receiver
3658aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown * are posted to the UI thread if needed.
3758aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown *
3896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown * @hide
3996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown */
40968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brownpublic final class Choreographer {
4196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private static final String TAG = "Choreographer";
4296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private static final boolean DEBUG = false;
4396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
4458aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown    // Amount of time in ms to wait before actually disposing of the display event
4558aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown    // receiver after all listeners have been removed.
4658aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown    private static final long DISPOSE_RECEIVER_DELAY = 200;
4758aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown
4896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // The default amount of time in ms between animation frames.
4996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // When vsync is not enabled, we want to have some idea of how long we should
5096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // wait before posting the next animation message.  It is important that the
5196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // default value be less than the true inter-frame delay on all devices to avoid
5296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // situations where we might skip frames by waiting too long (we must compensate
5396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // for jitter and hardware variations).  Regardless of this value, the animation
5496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // and display loop is ultimately rate-limited by how fast new graphics buffers can
5596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // be dequeued.
5696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private static final long DEFAULT_FRAME_DELAY = 10;
5796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
5896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // The number of milliseconds between animation frames.
5987d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown    private static volatile long sFrameDelay = DEFAULT_FRAME_DELAY;
6096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
6196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // Thread local storage for the choreographer.
6296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private static final ThreadLocal<Choreographer> sThreadInstance =
6396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            new ThreadLocal<Choreographer>() {
6496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        @Override
6596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        protected Choreographer initialValue() {
6696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            Looper looper = Looper.myLooper();
6796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            if (looper == null) {
6896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                throw new IllegalStateException("The current thread must have a looper!");
6996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            }
7096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            return new Choreographer(looper);
7196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
7296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    };
7396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
7496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // System property to enable/disable vsync for animations and drawing.
7596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // Enabled by default.
7696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private static final boolean USE_VSYNC = SystemProperties.getBoolean(
7796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            "debug.choreographer.vsync", true);
7896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
7996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // System property to enable/disable the use of the vsync / animation timer
8096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    // for drawing rather than drawing immediately.
8190a3c5f51ddf0f98769a258ed224f2ec5b645d0eJeff Brown    // Temporarily disabled by default because postponing performTraversals() violates
8290a3c5f51ddf0f98769a258ed224f2ec5b645d0eJeff Brown    // assumptions about traversals happening in-order relative to other posted messages.
8390a3c5f51ddf0f98769a258ed224f2ec5b645d0eJeff Brown    // Bug: 5721047
8496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private static final boolean USE_ANIMATION_TIMER_FOR_DRAW = SystemProperties.getBoolean(
8590a3c5f51ddf0f98769a258ed224f2ec5b645d0eJeff Brown            "debug.choreographer.animdraw", false);
8696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
8796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private static final int MSG_DO_ANIMATION = 0;
8896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private static final int MSG_DO_DRAW = 1;
8958aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown    private static final int MSG_DO_SCHEDULE_VSYNC = 2;
9058aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown    private static final int MSG_DO_DISPOSE_RECEIVER = 3;
9196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
9287d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown    private final Object mLock = new Object();
9387d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown
9496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private final Looper mLooper;
95968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    private final FrameHandler mHandler;
96968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
97968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    private Callback mCallbackPool;
9896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
9996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private OnAnimateListener[] mOnAnimateListeners;
10096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private OnDrawListener[] mOnDrawListeners;
10196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
102968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    private Callback mOnAnimateCallbacks;
103968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    private Callback mOnDrawCallbacks;
104968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
10596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private boolean mAnimationScheduled;
10696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private boolean mDrawScheduled;
10758aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown    private boolean mFrameDisplayEventReceiverNeeded;
10896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private FrameDisplayEventReceiver mFrameDisplayEventReceiver;
10996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private long mLastAnimationTime;
11096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private long mLastDrawTime;
11196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
11296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private Choreographer(Looper looper) {
11396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        mLooper = looper;
114968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        mHandler = new FrameHandler(looper);
11596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        mLastAnimationTime = Long.MIN_VALUE;
11696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        mLastDrawTime = Long.MIN_VALUE;
11796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
11896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
11996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    /**
1208bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn     * Gets the choreographer for the calling thread.  Must be called from
1218bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn     * a thread that already has a {@link android.os.Looper} associated with it.
12296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     *
12396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * @return The choreographer for this thread.
12496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * @throws IllegalStateException if the thread does not have a looper.
12596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     */
12696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    public static Choreographer getInstance() {
12796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        return sThreadInstance.get();
12896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
12996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
13096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    /**
13196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * The amount of time, in milliseconds, between each frame of the animation. This is a
13296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * requested time that the animation will attempt to honor, but the actual delay between
13396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * frames may be different, depending on system load and capabilities. This is a static
13496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * function because the same delay will be applied to all animations, since they are all
13596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * run off of a single timing loop.
13696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     *
13796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * The frame delay may be ignored when the animation system uses an external timing
13896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * source, such as the display refresh rate (vsync), to govern animations.
13996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     *
14096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * @return the requested time between frames, in milliseconds
14196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     */
14296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    public static long getFrameDelay() {
14396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        return sFrameDelay;
14496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
14596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
14696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    /**
14796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * The amount of time, in milliseconds, between each frame of the animation. This is a
14896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * requested time that the animation will attempt to honor, but the actual delay between
14996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * frames may be different, depending on system load and capabilities. This is a static
15096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * function because the same delay will be applied to all animations, since they are all
15196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * run off of a single timing loop.
15296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     *
15396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * The frame delay may be ignored when the animation system uses an external timing
15496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * source, such as the display refresh rate (vsync), to govern animations.
15596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     *
15696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * @param frameDelay the requested time between frames, in milliseconds
15796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     */
15896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    public static void setFrameDelay(long frameDelay) {
15996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        sFrameDelay = frameDelay;
16096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
16196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
16296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    /**
16396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * Schedules animation (and drawing) to occur on the next frame synchronization boundary.
16496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     */
16596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    public void scheduleAnimation() {
16687d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown        synchronized (mLock) {
167968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            scheduleAnimationLocked(false);
16887d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown        }
16987d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown    }
17087d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown
171968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    private void scheduleAnimationLocked(boolean force) {
172968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        if (!mAnimationScheduled
173968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                && (force || mOnAnimateListeners != null || mOnAnimateCallbacks != null)) {
17496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            mAnimationScheduled = true;
17596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            if (USE_VSYNC) {
17696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                if (DEBUG) {
17796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                    Log.d(TAG, "Scheduling vsync for animation.");
17896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                }
17958aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown
18058aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown                // If running on the Looper thread, then schedule the vsync immediately,
18158aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown                // otherwise post a message to schedule the vsync from the UI thread
18258aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown                // as soon as possible.
18358aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown                if (!mFrameDisplayEventReceiverNeeded) {
18458aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown                    mFrameDisplayEventReceiverNeeded = true;
18558aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown                    if (mFrameDisplayEventReceiver != null) {
186968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                        mHandler.removeMessages(MSG_DO_DISPOSE_RECEIVER);
18758aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown                    }
18858aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown                }
18958aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown                if (isRunningOnLooperThreadLocked()) {
19058aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown                    doScheduleVsyncLocked();
19158aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown                } else {
192968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                    mHandler.sendMessageAtFrontOfQueue(
193968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                            mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC));
19496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                }
19596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            } else {
19696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                final long now = SystemClock.uptimeMillis();
19796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                final long nextAnimationTime = Math.max(mLastAnimationTime + sFrameDelay, now);
19896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                if (DEBUG) {
19996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                    Log.d(TAG, "Scheduling animation in " + (nextAnimationTime - now) + " ms.");
20096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                }
201968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                mHandler.sendEmptyMessageAtTime(MSG_DO_ANIMATION, nextAnimationTime);
20296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            }
20396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
20496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
20596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
20696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    /**
20787d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown     * Returns true if {@link #scheduleAnimation()} has been called but
2088bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn     * {@link OnAnimateListener#onAnimate() OnAnimateListener.onAnimate()} has
2098bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn     * not yet been called.
2108bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn     */
2118bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn    public boolean isAnimationScheduled() {
21287d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown        synchronized (mLock) {
21387d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown            return mAnimationScheduled;
21487d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown        }
2158bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn    }
2168bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn
2178bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn    /**
21896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * Schedules drawing to occur on the next frame synchronization boundary.
21996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * Must be called on the UI thread.
22096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     */
22196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    public void scheduleDraw() {
22287d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown        synchronized (mLock) {
223968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            scheduleDrawLocked();
224968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
225968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    }
226968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
227968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    private void scheduleDrawLocked() {
228968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        if (!mDrawScheduled
229968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                && (mOnDrawListeners != null || mOnDrawCallbacks != null)) {
230968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            mDrawScheduled = true;
231968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            if (USE_ANIMATION_TIMER_FOR_DRAW) {
232968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                scheduleAnimationLocked(true);
233968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            } else {
234968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                if (DEBUG) {
235968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                    Log.d(TAG, "Scheduling draw immediately.");
23696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                }
237968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                mHandler.sendEmptyMessage(MSG_DO_DRAW);
23896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            }
23996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
24096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
24196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
2428bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn    /**
24387d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown     * Returns true if {@link #scheduleDraw()} has been called but
2448bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn     * {@link OnDrawListener#onDraw() OnDrawListener.onDraw()} has
2458bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn     * not yet been called.
2468bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn     */
2478bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn    public boolean isDrawScheduled() {
24887d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown        synchronized (mLock) {
24987d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown            return mDrawScheduled;
25087d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown        }
2518bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn    }
2528bcd54b98ad5d98d47364ff14e06910deadf9302Dianne Hackborn
253968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    /**
254968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     * Adds an animation listener.
255968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     *
256968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     * @param listener The listener to add.
257968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     */
258968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    public void addOnAnimateListener(OnAnimateListener listener) {
259968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        if (listener == null) {
260968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            throw new IllegalArgumentException("listener must not be null");
261968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
262968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
263968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        if (DEBUG) {
264968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            Log.d(TAG, "Adding onAnimate listener: " + listener);
265968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
266968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
267968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        synchronized (mLock) {
268968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            mOnAnimateListeners = ArrayUtils.appendElement(OnAnimateListener.class,
269968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                    mOnAnimateListeners, listener);
270968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
271968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    }
272968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
273968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    /**
274968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     * Removes an animation listener.
275968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     *
276968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     * @param listener The listener to remove.
277968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     */
278968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    public void removeOnAnimateListener(OnAnimateListener listener) {
279968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        if (listener == null) {
280968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            throw new IllegalArgumentException("listener must not be null");
281968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
282968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
283968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        if (DEBUG) {
284968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            Log.d(TAG, "Removing onAnimate listener: " + listener);
285968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
286968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
287968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        synchronized (mLock) {
288968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            mOnAnimateListeners = ArrayUtils.removeElement(OnAnimateListener.class,
289968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                    mOnAnimateListeners, listener);
290968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            stopTimingLoopIfNoListenersOrCallbacksLocked();
291968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
292968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    }
293968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
294968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    /**
295968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     * Adds a draw listener.
296968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     *
297968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     * @param listener The listener to add.
298968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     */
299968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    public void addOnDrawListener(OnDrawListener listener) {
300968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        if (listener == null) {
301968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            throw new IllegalArgumentException("listener must not be null");
302968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
303968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
304968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        if (DEBUG) {
305968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            Log.d(TAG, "Adding onDraw listener: " + listener);
306968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
307968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
308968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        synchronized (mLock) {
309968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            mOnDrawListeners = ArrayUtils.appendElement(OnDrawListener.class,
310968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                    mOnDrawListeners, listener);
311968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
312968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    }
313968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
314968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    /**
315968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     * Removes a draw listener.
316968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     * Must be called on the UI thread.
317968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     *
318968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     * @param listener The listener to remove.
319968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     */
320968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    public void removeOnDrawListener(OnDrawListener listener) {
321968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        if (listener == null) {
322968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            throw new IllegalArgumentException("listener must not be null");
323968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
324968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
325968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        if (DEBUG) {
326968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            Log.d(TAG, "Removing onDraw listener: " + listener);
327968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
328968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
329968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        synchronized (mLock) {
330968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            mOnDrawListeners = ArrayUtils.removeElement(OnDrawListener.class,
331968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                    mOnDrawListeners, listener);
332968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            stopTimingLoopIfNoListenersOrCallbacksLocked();
333968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
334968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    }
335968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
336968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
337968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    /**
338968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     * Posts a callback to run on the next animation cycle and schedules an animation cycle.
339968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     * The callback only runs once and then is automatically removed.
340968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     *
341968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     * @param runnable The callback to run during the next animation cycle.
342968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     *
343968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     * @see #removeOnAnimateCallback
344968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     */
345968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    public void postOnAnimateCallback(Runnable runnable) {
346968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        if (runnable == null) {
347968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            throw new IllegalArgumentException("runnable must not be null");
348968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
349968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        synchronized (mLock) {
350968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            mOnAnimateCallbacks = addCallbackLocked(mOnAnimateCallbacks, runnable);
351968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            scheduleAnimationLocked(false);
352968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
353968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    }
354968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
355968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    /**
356968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     * Removes an animation callback.
357968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     * Does nothing if the specified animation callback has not been posted or has already
358968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     * been removed.
359968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     *
360968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     * @param runnable The animation callback to remove.
361968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     *
362968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     * @see #postOnAnimateCallback
363968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     */
364968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    public void removeOnAnimateCallback(Runnable runnable) {
365968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        if (runnable == null) {
366968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            throw new IllegalArgumentException("runnable must not be null");
367968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
368968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        synchronized (mLock) {
369968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            mOnAnimateCallbacks = removeCallbackLocked(mOnAnimateCallbacks, runnable);
370968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            stopTimingLoopIfNoListenersOrCallbacksLocked();
371968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
372968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    }
373968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
374968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    /**
375968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     * Posts a callback to run on the next draw cycle and schedules a draw cycle.
376968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     * The callback only runs once and then is automatically removed.
377968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     *
378968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     * @param runnable The callback to run during the next draw cycle.
379968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     *
380968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     * @see #removeOnDrawCallback
381968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     */
382968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    public void postOnDrawCallback(Runnable runnable) {
383968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        if (runnable == null) {
384968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            throw new IllegalArgumentException("runnable must not be null");
385968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
386968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        synchronized (mLock) {
387968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            mOnDrawCallbacks = addCallbackLocked(mOnDrawCallbacks, runnable);
388968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            scheduleDrawLocked();
389968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
390968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    }
391968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
392968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    /**
393968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     * Removes a draw callback.
394968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     * Does nothing if the specified draw callback has not been posted or has already
395968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     * been removed.
396968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     *
397968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     * @param runnable The draw callback to remove.
398968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     *
399968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     * @see #postOnDrawCallback
400968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown     */
401968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    public void removeOnDrawCallback(Runnable runnable) {
402968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        if (runnable == null) {
403968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            throw new IllegalArgumentException("runnable must not be null");
404968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
405968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        synchronized (mLock) {
406968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            mOnDrawCallbacks = removeCallbackLocked(mOnDrawCallbacks, runnable);
407968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            stopTimingLoopIfNoListenersOrCallbacksLocked();
40896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
40996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
41096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
411968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    void doAnimation() {
41287d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown        doAnimationInner();
41387d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown
41487d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown        if (USE_ANIMATION_TIMER_FOR_DRAW) {
41587d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown            doDraw();
41687d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown        }
41787d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown    }
41887d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown
419968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    void doAnimationInner() {
42087d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown        final long start;
42187d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown        final OnAnimateListener[] listeners;
422968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        final Callback callbacks;
42387d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown        synchronized (mLock) {
42487d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown            if (!mAnimationScheduled) {
42587d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown                return; // no work to do
42687d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown            }
42796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            mAnimationScheduled = false;
42896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
42987d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown            start = SystemClock.uptimeMillis();
43096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            if (DEBUG) {
43196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                Log.d(TAG, "Performing animation: " + Math.max(0, start - mLastAnimationTime)
43296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                        + " ms have elapsed since previous animation.");
43396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            }
43496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            mLastAnimationTime = start;
43596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
43687d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown            listeners = mOnAnimateListeners;
437968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            callbacks = mOnAnimateCallbacks;
438968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            mOnAnimateCallbacks = null;
43987d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown        }
44096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
44187d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown        if (listeners != null) {
44287d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown            for (int i = 0; i < listeners.length; i++) {
44387d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown                listeners[i].onAnimate();
44496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            }
44596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
44696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
447968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        if (callbacks != null) {
448968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            runCallbacks(callbacks);
449968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            synchronized (mLock) {
450968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                recycleCallbacksLocked(callbacks);
451968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            }
452968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
453968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
45487d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown        if (DEBUG) {
45587d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown            Log.d(TAG, "Animation took " + (SystemClock.uptimeMillis() - start) + " ms.");
45696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
45796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
45896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
459968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    void doDraw() {
46087d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown        final long start;
46187d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown        final OnDrawListener[] listeners;
462968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        final Callback callbacks;
46387d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown        synchronized (mLock) {
46487d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown            if (!mDrawScheduled) {
46587d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown                return; // no work to do
46687d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown            }
46796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            mDrawScheduled = false;
46896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
46987d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown            start = SystemClock.uptimeMillis();
47096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            if (DEBUG) {
47196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                Log.d(TAG, "Performing draw: " + Math.max(0, start - mLastDrawTime)
47296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                        + " ms have elapsed since previous draw.");
47396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            }
47496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            mLastDrawTime = start;
47596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
47687d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown            listeners = mOnDrawListeners;
477968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            callbacks = mOnDrawCallbacks;
478968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            mOnDrawCallbacks = null;
47987d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown        }
48096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
48187d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown        if (listeners != null) {
48287d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown            for (int i = 0; i < listeners.length; i++) {
48387d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown                listeners[i].onDraw();
48496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            }
48596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
48687d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown
487968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        if (callbacks != null) {
488968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            runCallbacks(callbacks);
489968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            synchronized (mLock) {
490968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                recycleCallbacksLocked(callbacks);
491968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            }
492968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
493968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
49487d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown        if (DEBUG) {
49587d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown            Log.d(TAG, "Draw took " + (SystemClock.uptimeMillis() - start) + " ms.");
49687d0b03f1f16590ff261ae30441e838ae5446e84Jeff Brown        }
49796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
49896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
499968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    void doScheduleVsync() {
50058aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown        synchronized (mLock) {
50158aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown            doScheduleVsyncLocked();
50258aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown        }
50358aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown    }
50458aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown
50558aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown    private void doScheduleVsyncLocked() {
50658aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown        if (mFrameDisplayEventReceiverNeeded && mAnimationScheduled) {
50758aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown            if (mFrameDisplayEventReceiver == null) {
50858aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown                mFrameDisplayEventReceiver = new FrameDisplayEventReceiver(mLooper);
50958aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown            }
51058aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown            mFrameDisplayEventReceiver.scheduleVsync();
51158aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown        }
51258aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown    }
51358aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown
514968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    void doDisposeReceiver() {
51558aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown        synchronized (mLock) {
51658aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown            if (!mFrameDisplayEventReceiverNeeded && mFrameDisplayEventReceiver != null) {
51758aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown                mFrameDisplayEventReceiver.dispose();
51858aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown                mFrameDisplayEventReceiver = null;
51958aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown            }
52058aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown        }
52158aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown    }
52258aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown
523968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    private void stopTimingLoopIfNoListenersOrCallbacksLocked() {
524968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        if (mOnAnimateListeners == null && mOnDrawListeners == null
525968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                && mOnAnimateCallbacks == null && mOnDrawCallbacks == null) {
52696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            if (DEBUG) {
52796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                Log.d(TAG, "Stopping timing loop.");
52896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            }
52996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
53096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            if (mAnimationScheduled) {
53196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                mAnimationScheduled = false;
53258aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown                if (USE_VSYNC) {
533968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                    mHandler.removeMessages(MSG_DO_SCHEDULE_VSYNC);
53458aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown                } else {
535968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                    mHandler.removeMessages(MSG_DO_ANIMATION);
53696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                }
53796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            }
53896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
53996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            if (mDrawScheduled) {
54096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                mDrawScheduled = false;
54196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                if (!USE_ANIMATION_TIMER_FOR_DRAW) {
542968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                    mHandler.removeMessages(MSG_DO_DRAW);
54396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown                }
54496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            }
54596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
54658aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown            // Post a message to dispose the display event receiver if we haven't needed
54758aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown            // it again after a certain amount of time has elapsed.  Another reason to
54858aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown            // defer disposal is that it is possible for use to attempt to dispose the
54958aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown            // receiver while handling a vsync event that it dispatched, which might
55058aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown            // cause a few problems...
55158aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown            if (mFrameDisplayEventReceiverNeeded) {
55258aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown                mFrameDisplayEventReceiverNeeded = false;
55358aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown                if (mFrameDisplayEventReceiver != null) {
554968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                    mHandler.sendEmptyMessageDelayed(MSG_DO_DISPOSE_RECEIVER,
555968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                            DISPOSE_RECEIVER_DELAY);
55658aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown                }
55796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            }
55896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
55996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
56096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
56158aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown    private boolean isRunningOnLooperThreadLocked() {
56258aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown        return Looper.myLooper() == mLooper;
56358aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown    }
56458aedbc9bea13415e2d42cf7c9fe8a7efd243e66Jeff Brown
565968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    private Callback addCallbackLocked(Callback head, Runnable runnable) {
566968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        Callback callback = obtainCallbackLocked(runnable);
567968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        if (head == null) {
568968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            return callback;
569968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
570968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        Callback tail = head;
571968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        while (tail.next != null) {
572968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            tail = tail.next;
573968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
574968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        tail.next = callback;
575968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        return head;
576968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    }
577968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
578968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    private Callback removeCallbackLocked(Callback head, Runnable runnable) {
579968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        Callback predecessor = null;
580968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        for (Callback callback = head; callback != null;) {
581968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            final Callback next = callback.next;
582968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            if (callback.runnable == runnable) {
583968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                if (predecessor != null) {
584968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                    predecessor.next = next;
585968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                } else {
586968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                    head = next;
587968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                }
588968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                recycleCallbackLocked(callback);
589968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            } else {
590968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                predecessor = callback;
591968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            }
592968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            callback = next;
593968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
594968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        return head;
595968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    }
596968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
597968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    private void runCallbacks(Callback head) {
598968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        while (head != null) {
599968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            head.runnable.run();
600968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            head = head.next;
601968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
602968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    }
603968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
604968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    private void recycleCallbacksLocked(Callback head) {
605968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        while (head != null) {
606968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            final Callback next = head.next;
607968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            recycleCallbackLocked(head);
608968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            head = next;
609968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
610968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    }
611968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
612968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    private Callback obtainCallbackLocked(Runnable runnable) {
613968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        Callback callback = mCallbackPool;
614968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        if (callback == null) {
615968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            callback = new Callback();
616968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        } else {
617968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            mCallbackPool = callback.next;
618968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            callback.next = null;
619968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
620968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        callback.runnable = runnable;
621968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        return callback;
622968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    }
623968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
624968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    private void recycleCallbackLocked(Callback callback) {
625968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        callback.runnable = null;
626968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        callback.next = mCallbackPool;
627968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        mCallbackPool = callback;
628968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    }
629968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
63096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    /**
63196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * Listens for animation frame timing events.
63296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     */
63396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    public static interface OnAnimateListener {
63496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        /**
63596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown         * Called to animate properties before drawing the frame.
63696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown         */
63796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        public void onAnimate();
63896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
63996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
64096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    /**
64196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     * Listens for draw frame timing events.
64296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown     */
64396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    public static interface OnDrawListener {
64496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        /**
64596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown         * Called to draw the frame.
64696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown         */
64796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        public void onDraw();
64896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
64996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
650968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    private final class FrameHandler extends Handler {
651968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        public FrameHandler(Looper looper) {
652968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            super(looper);
653968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
654968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
655968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        @Override
656968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        public void handleMessage(Message msg) {
657968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            switch (msg.what) {
658968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                case MSG_DO_ANIMATION:
659968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                    doAnimation();
660968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                    break;
661968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                case MSG_DO_DRAW:
662968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                    doDraw();
663968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                    break;
664968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                case MSG_DO_SCHEDULE_VSYNC:
665968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                    doScheduleVsync();
666968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                    break;
667968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                case MSG_DO_DISPOSE_RECEIVER:
668968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                    doDisposeReceiver();
669968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown                    break;
670968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown            }
671968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        }
672968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    }
673968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
67496e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    private final class FrameDisplayEventReceiver extends DisplayEventReceiver {
67596e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        public FrameDisplayEventReceiver(Looper looper) {
67696e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            super(looper);
67796e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
67896e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown
67996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        @Override
68096e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        public void onVsync(long timestampNanos, int frame) {
68196e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown            doAnimation();
68296e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown        }
68396e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown    }
684968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown
685968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    private static final class Callback {
686968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        public Callback next;
687968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown        public Runnable runnable;
688968588573cf516fd8cf33c133d06f06c67ca1785Jeff Brown    }
68996e942dabeeaaa9ab6df3a870668c6fe53d930daJeff Brown}
690