AnimationThread.java revision d7cc1244d7f0901688545c8535fa531fbb68669a
12eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet/*
22eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet * Copyright (C) 2010 The Android Open Source Project
32eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet *
42eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet * Licensed under the Apache License, Version 2.0 (the "License");
52eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet * you may not use this file except in compliance with the License.
62eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet * You may obtain a copy of the License at
72eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet *
82eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet *      http://www.apache.org/licenses/LICENSE-2.0
92eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet *
102eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet * Unless required by applicable law or agreed to in writing, software
112eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet * distributed under the License is distributed on an "AS IS" BASIS,
122eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet * See the License for the specific language governing permissions and
142eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet * limitations under the License.
152eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet */
162eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet
172eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohetpackage com.android.layoutlib.bridge.impl;
182eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet
1919a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohetimport com.android.ide.common.rendering.api.IAnimationListener;
2019a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohetimport com.android.ide.common.rendering.api.RenderSession;
2119a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohetimport com.android.ide.common.rendering.api.Result;
2219a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohetimport com.android.ide.common.rendering.api.Result.Status;
239eb6d412af6859b6c0bb969c76bbfc48eec8fd4bXavier Ducrohetimport com.android.layoutlib.bridge.Bridge;
242eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet
252eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohetimport android.animation.ValueAnimator;
262eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohetimport android.os.Handler;
272eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohetimport android.os.Handler_Delegate;
282eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohetimport android.os.Message;
292eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohetimport android.os.Handler_Delegate.IHandlerCallback;
302eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet
312eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohetimport java.util.LinkedList;
322eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohetimport java.util.Queue;
332eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet
34e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet/**
35e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet * Abstract animation thread.
36e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet * <p/>
37e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet * This does not actually start an animation, instead it fakes a looper that will play whatever
38e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet * animation is sending messages to its own {@link Handler}.
39e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet * <p/>
40e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet * Classes should implement {@link #preAnimation()} and {@link #postAnimation()}.
41e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet * <p/>
422b9c38ab62abc8d5b2f956e961087f259caf25ffXavier Ducrohet * If {@link #preAnimation()} does not start an animation somehow then the thread doesn't do
43e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet * anything.
44e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet *
45e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet */
46e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohetpublic abstract class AnimationThread extends Thread {
472eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet
482eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet    private static class MessageBundle {
492eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet        final Handler mTarget;
502eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet        final Message mMessage;
512eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet        final long mUptimeMillis;
522eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet
532eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet        MessageBundle(Handler target, Message message, long uptimeMillis) {
542eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet            mTarget = target;
552eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet            mMessage = message;
562eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet            mUptimeMillis = uptimeMillis;
572eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet        }
582eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet    }
592eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet
6019a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohet    private final RenderSessionImpl mSession;
612eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet
622b9c38ab62abc8d5b2f956e961087f259caf25ffXavier Ducrohet    private Queue<MessageBundle> mQueue = new LinkedList<MessageBundle>();
632eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet    private final IAnimationListener mListener;
642eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet
6519a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohet    public AnimationThread(RenderSessionImpl scene, String threadName,
6619a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohet            IAnimationListener listener) {
67e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet        super(threadName);
6819a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohet        mSession = scene;
692eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet        mListener = listener;
702eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet    }
712eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet
7219a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohet    public abstract Result preAnimation();
73e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet    public abstract void postAnimation();
74e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet
752eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet    @Override
762eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet    public void run() {
779eb6d412af6859b6c0bb969c76bbfc48eec8fd4bXavier Ducrohet        Bridge.prepareThread();
782eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet        try {
792eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet            Handler_Delegate.setCallback(new IHandlerCallback() {
802eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                public void sendMessageAtTime(Handler handler, Message msg, long uptimeMillis) {
812eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                    if (msg.what == ValueAnimator.ANIMATION_START ||
822eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                            msg.what == ValueAnimator.ANIMATION_FRAME) {
832eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                        mQueue.add(new MessageBundle(handler, msg, uptimeMillis));
842eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                    } else {
852eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                        // just ignore.
862eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                    }
872eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                }
882eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet            });
892eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet
90e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet            // call out to the pre-animation work, which should start an animation or more.
9119a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohet            Result result = preAnimation();
92e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet            if (result.isSuccess() == false) {
93e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet                mListener.done(result);
94e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet            }
952eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet
962eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet            // loop the animation
9719a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohet            RenderSession session = mSession.getSession();
982eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet            do {
99e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet                // check early.
100e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet                if (mListener.isCanceled()) {
101e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet                    break;
102e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet                }
103e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet
1042eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                // get the next message.
1052eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                MessageBundle bundle = mQueue.poll();
1062eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                if (bundle == null) {
1072eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                    break;
1082eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                }
1092eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet
1102eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                // sleep enough for this bundle to be on time
1112eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                long currentTime = System.currentTimeMillis();
1122eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                if (currentTime < bundle.mUptimeMillis) {
1132eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                    try {
1142eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                        sleep(bundle.mUptimeMillis - currentTime);
1152eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                    } catch (InterruptedException e) {
116c8aec064f9f304c88f61c8c1aa3d60521b92177aXavier Ducrohet                        // FIXME log/do something/sleep again?
1172eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                        e.printStackTrace();
1182eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                    }
1192eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                }
1202eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet
121e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet                // check after sleeping.
122e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet                if (mListener.isCanceled()) {
123e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet                    break;
124e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet                }
125e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet
1262eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                // ready to do the work, acquire the scene.
12719a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohet                result = mSession.acquire(250);
128c8aec064f9f304c88f61c8c1aa3d60521b92177aXavier Ducrohet                if (result.isSuccess() == false) {
1292eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                    mListener.done(result);
1302eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                    return;
1312eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                }
1322eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet
1332eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                // process the bundle. If the animation is not finished, this will enqueue
1342eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                // the next message, so mQueue will have another one.
1352eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                try {
136e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet                    // check after acquiring in case it took a while.
137e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet                    if (mListener.isCanceled()) {
138e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet                        break;
139e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet                    }
140e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet
1412eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                    bundle.mTarget.handleMessage(bundle.mMessage);
14219a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohet                    if (mSession.render().isSuccess()) {
14319a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohet                        mListener.onNewFrame(session);
1442eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                    }
1452eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                } finally {
14619a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohet                    mSession.release();
1472eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                }
148e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet            } while (mListener.isCanceled() == false && mQueue.size() > 0);
1492eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet
15019a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohet            mListener.done(Status.SUCCESS.createResult());
1512b9c38ab62abc8d5b2f956e961087f259caf25ffXavier Ducrohet
1522b9c38ab62abc8d5b2f956e961087f259caf25ffXavier Ducrohet        } catch (Throwable throwable) {
153d7cc1244d7f0901688545c8535fa531fbb68669aXavier Ducrohet            // can't use Bridge.getLog() as the exception might be thrown outside
154d7cc1244d7f0901688545c8535fa531fbb68669aXavier Ducrohet            // of an acquire/release block.
155d7cc1244d7f0901688545c8535fa531fbb68669aXavier Ducrohet            mSession.getLog().error(null, "Error playing animation", throwable);
156d7cc1244d7f0901688545c8535fa531fbb68669aXavier Ducrohet            mListener.done(Status.ERROR_UNKNOWN.createResult("Error playing animation", throwable));
1572b9c38ab62abc8d5b2f956e961087f259caf25ffXavier Ducrohet
1582eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet        } finally {
159e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet            postAnimation();
1602eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet            Handler_Delegate.setCallback(null);
1619eb6d412af6859b6c0bb969c76bbfc48eec8fd4bXavier Ducrohet            Bridge.cleanupThread();
1622eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet        }
1632eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet    }
1642eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet}
165