AnimationThread.java revision 5a82d8c58bf91c357c37a82b9f5e5c26f676d847
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
31479101a9b4142d21d42351684bc9bc65ec3471beXavier Ducrohetimport java.util.PriorityQueue;
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
48479101a9b4142d21d42351684bc9bc65ec3471beXavier Ducrohet    private static class MessageBundle implements Comparable<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        }
58479101a9b4142d21d42351684bc9bc65ec3471beXavier Ducrohet
59479101a9b4142d21d42351684bc9bc65ec3471beXavier Ducrohet        public int compareTo(MessageBundle bundle) {
60479101a9b4142d21d42351684bc9bc65ec3471beXavier Ducrohet            if (mUptimeMillis < bundle.mUptimeMillis) {
61479101a9b4142d21d42351684bc9bc65ec3471beXavier Ducrohet                return -1;
62479101a9b4142d21d42351684bc9bc65ec3471beXavier Ducrohet            }
63479101a9b4142d21d42351684bc9bc65ec3471beXavier Ducrohet            return 1;
64479101a9b4142d21d42351684bc9bc65ec3471beXavier Ducrohet        }
652eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet    }
662eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet
6719a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohet    private final RenderSessionImpl mSession;
682eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet
69479101a9b4142d21d42351684bc9bc65ec3471beXavier Ducrohet    private Queue<MessageBundle> mQueue = new PriorityQueue<MessageBundle>();
702eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet    private final IAnimationListener mListener;
712eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet
7219a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohet    public AnimationThread(RenderSessionImpl scene, String threadName,
7319a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohet            IAnimationListener listener) {
74e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet        super(threadName);
7519a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohet        mSession = scene;
762eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet        mListener = listener;
772eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet    }
782eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet
7919a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohet    public abstract Result preAnimation();
80e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet    public abstract void postAnimation();
81e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet
822eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet    @Override
832eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet    public void run() {
849eb6d412af6859b6c0bb969c76bbfc48eec8fd4bXavier Ducrohet        Bridge.prepareThread();
852eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet        try {
862eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet            Handler_Delegate.setCallback(new IHandlerCallback() {
872eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                public void sendMessageAtTime(Handler handler, Message msg, long uptimeMillis) {
882eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                    if (msg.what == ValueAnimator.ANIMATION_START ||
892eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                            msg.what == ValueAnimator.ANIMATION_FRAME) {
902eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                        mQueue.add(new MessageBundle(handler, msg, uptimeMillis));
912eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                    } else {
922eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                        // just ignore.
932eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                    }
942eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                }
952eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet            });
962eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet
97e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet            // call out to the pre-animation work, which should start an animation or more.
9819a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohet            Result result = preAnimation();
99e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet            if (result.isSuccess() == false) {
100e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet                mListener.done(result);
101e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet            }
1022eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet
1032eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet            // loop the animation
10419a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohet            RenderSession session = mSession.getSession();
1052eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet            do {
106e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet                // check early.
107e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet                if (mListener.isCanceled()) {
108e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet                    break;
109e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet                }
110e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet
1112eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                // get the next message.
1122eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                MessageBundle bundle = mQueue.poll();
1132eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                if (bundle == null) {
1142eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                    break;
1152eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                }
1162eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet
1172eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                // sleep enough for this bundle to be on time
1182eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                long currentTime = System.currentTimeMillis();
1192eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                if (currentTime < bundle.mUptimeMillis) {
1202eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                    try {
1212eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                        sleep(bundle.mUptimeMillis - currentTime);
1222eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                    } catch (InterruptedException e) {
123c8aec064f9f304c88f61c8c1aa3d60521b92177aXavier Ducrohet                        // FIXME log/do something/sleep again?
1242eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                        e.printStackTrace();
1252eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                    }
1262eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                }
1272eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet
128e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet                // check after sleeping.
129e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet                if (mListener.isCanceled()) {
130e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet                    break;
131e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet                }
132e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet
1332eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                // ready to do the work, acquire the scene.
13419a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohet                result = mSession.acquire(250);
135c8aec064f9f304c88f61c8c1aa3d60521b92177aXavier Ducrohet                if (result.isSuccess() == false) {
1362eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                    mListener.done(result);
1372eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                    return;
1382eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                }
1392eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet
1402eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                // process the bundle. If the animation is not finished, this will enqueue
1412eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                // the next message, so mQueue will have another one.
1422eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                try {
143e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet                    // check after acquiring in case it took a while.
144e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet                    if (mListener.isCanceled()) {
145e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet                        break;
146e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet                    }
147e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet
1482eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                    bundle.mTarget.handleMessage(bundle.mMessage);
1495a82d8c58bf91c357c37a82b9f5e5c26f676d847Xavier Ducrohet                    if (mSession.render(false /*freshRender*/).isSuccess()) {
15019a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohet                        mListener.onNewFrame(session);
1512eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                    }
1522eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                } finally {
15319a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohet                    mSession.release();
1542eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet                }
155e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet            } while (mListener.isCanceled() == false && mQueue.size() > 0);
1562eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet
15719a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohet            mListener.done(Status.SUCCESS.createResult());
1582b9c38ab62abc8d5b2f956e961087f259caf25ffXavier Ducrohet
1592b9c38ab62abc8d5b2f956e961087f259caf25ffXavier Ducrohet        } catch (Throwable throwable) {
160d7cc1244d7f0901688545c8535fa531fbb68669aXavier Ducrohet            // can't use Bridge.getLog() as the exception might be thrown outside
161d7cc1244d7f0901688545c8535fa531fbb68669aXavier Ducrohet            // of an acquire/release block.
162d7cc1244d7f0901688545c8535fa531fbb68669aXavier Ducrohet            mListener.done(Status.ERROR_UNKNOWN.createResult("Error playing animation", throwable));
1632b9c38ab62abc8d5b2f956e961087f259caf25ffXavier Ducrohet
1642eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet        } finally {
165e1179ea065392485a6e3bf1e28a1242179cd48b0Xavier Ducrohet            postAnimation();
1662eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet            Handler_Delegate.setCallback(null);
1679eb6d412af6859b6c0bb969c76bbfc48eec8fd4bXavier Ducrohet            Bridge.cleanupThread();
1682eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet        }
1692eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet    }
1702eea6fab1cbb0a5c8f913491c2d622c904759893Xavier Ducrohet}
171