1282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski/*
2282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Copyright (C) 2010 The Android Open Source Project
3282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
4282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Licensed under the Apache License, Version 2.0 (the "License");
5282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * you may not use this file except in compliance with the License.
6282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * You may obtain a copy of the License at
7282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
8282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *      http://www.apache.org/licenses/LICENSE-2.0
9282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
10282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Unless required by applicable law or agreed to in writing, software
11282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * distributed under the License is distributed on an "AS IS" BASIS,
12282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * See the License for the specific language governing permissions and
14282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * limitations under the License.
15282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */
16282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
17282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskipackage android.animation;
18282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
19282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.ide.common.rendering.api.IAnimationListener;
20282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.ide.common.rendering.api.RenderSession;
21282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.ide.common.rendering.api.Result;
22282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.ide.common.rendering.api.Result.Status;
23282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.layoutlib.bridge.Bridge;
24282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.layoutlib.bridge.impl.RenderSessionImpl;
25282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
26282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.os.Handler;
27282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.os.Handler_Delegate;
28282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.os.Handler_Delegate.IHandlerCallback;
29282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.os.Message;
30282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
31282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.util.PriorityQueue;
32282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.util.Queue;
33282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
34282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski/**
35282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Abstract animation thread.
36282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * <p/>
37282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * This does not actually start an animation, instead it fakes a looper that will play whatever
38282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * animation is sending messages to its own {@link Handler}.
39282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * <p/>
40282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Classes should implement {@link #preAnimation()} and {@link #postAnimation()}.
41282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * <p/>
42282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * If {@link #preAnimation()} does not start an animation somehow then the thread doesn't do
43282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * anything.
44282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
45282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */
46282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskipublic abstract class AnimationThread extends Thread {
47282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
48282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private static class MessageBundle implements Comparable<MessageBundle> {
49282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        final Handler mTarget;
50282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        final Message mMessage;
51282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        final long mUptimeMillis;
52282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
53282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        MessageBundle(Handler target, Message message, long uptimeMillis) {
54282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mTarget = target;
55282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mMessage = message;
56282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mUptimeMillis = uptimeMillis;
57282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
58282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
59282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        @Override
60282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        public int compareTo(MessageBundle bundle) {
61282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (mUptimeMillis < bundle.mUptimeMillis) {
62282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                return -1;
63282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
64282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return 1;
65282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
66282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
67282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
68282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private final RenderSessionImpl mSession;
69282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
70282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private Queue<MessageBundle> mQueue = new PriorityQueue<MessageBundle>();
71282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private final IAnimationListener mListener;
72282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
73282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public AnimationThread(RenderSessionImpl scene, String threadName,
74282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            IAnimationListener listener) {
75282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        super(threadName);
76282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        mSession = scene;
77282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        mListener = listener;
78282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
79282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
80282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public abstract Result preAnimation();
81282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public abstract void postAnimation();
82282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
83282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    @Override
84282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public void run() {
85282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        Bridge.prepareThread();
86282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        try {
87282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            /* FIXME: The ANIMATION_FRAME message no longer exists.  Instead, the
88282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski             * animation timing loop is completely based on a Choreographer objects
89282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski             * that schedules animation and drawing frames.  The animation handler is
90282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski             * no longer even a handler; it is just a Runnable enqueued on the Choreographer.
91282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            Handler_Delegate.setCallback(new IHandlerCallback() {
92282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                @Override
93282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                public void sendMessageAtTime(Handler handler, Message msg, long uptimeMillis) {
94282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    if (msg.what == ValueAnimator.ANIMATION_START ||
95282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            msg.what == ValueAnimator.ANIMATION_FRAME) {
96282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        mQueue.add(new MessageBundle(handler, msg, uptimeMillis));
97282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    } else {
98282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        // just ignore.
99282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    }
100282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
101282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            });
102282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            */
103282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
104282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // call out to the pre-animation work, which should start an animation or more.
105282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            Result result = preAnimation();
106282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (result.isSuccess() == false) {
107282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                mListener.done(result);
108282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
109282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
110282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // loop the animation
111282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            RenderSession session = mSession.getSession();
112282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            do {
113282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // check early.
114282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (mListener.isCanceled()) {
115282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    break;
116282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
117282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
118282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // get the next message.
119282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                MessageBundle bundle = mQueue.poll();
120282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (bundle == null) {
121282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    break;
122282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
123282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
124282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // sleep enough for this bundle to be on time
125282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                long currentTime = System.currentTimeMillis();
126282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (currentTime < bundle.mUptimeMillis) {
127282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    try {
128282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        sleep(bundle.mUptimeMillis - currentTime);
129282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    } catch (InterruptedException e) {
130282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        // FIXME log/do something/sleep again?
131282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        e.printStackTrace();
132282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    }
133282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
134282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
135282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // check after sleeping.
136282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (mListener.isCanceled()) {
137282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    break;
138282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
139282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
140282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // ready to do the work, acquire the scene.
141282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                result = mSession.acquire(250);
142282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (result.isSuccess() == false) {
143282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    mListener.done(result);
144282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    return;
145282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
146282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
147282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // process the bundle. If the animation is not finished, this will enqueue
148282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // the next message, so mQueue will have another one.
149282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                try {
150282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    // check after acquiring in case it took a while.
151282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    if (mListener.isCanceled()) {
152282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        break;
153282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    }
154282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
155282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    bundle.mTarget.handleMessage(bundle.mMessage);
156282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    if (mSession.render(false /*freshRender*/).isSuccess()) {
157282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        mListener.onNewFrame(session);
158282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    }
159282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                } finally {
160282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    mSession.release();
161282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
162282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } while (mListener.isCanceled() == false && mQueue.size() > 0);
163282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
164282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mListener.done(Status.SUCCESS.createResult());
165282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
166282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } catch (Throwable throwable) {
167282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // can't use Bridge.getLog() as the exception might be thrown outside
168282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // of an acquire/release block.
169282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mListener.done(Status.ERROR_UNKNOWN.createResult("Error playing animation", throwable));
170282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
171282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } finally {
172282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            postAnimation();
173282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            Handler_Delegate.setCallback(null);
174282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            Bridge.cleanupThread();
175282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
176282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
177282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
178