• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.animation;
18 
19 import com.android.ide.common.rendering.api.IAnimationListener;
20 import com.android.ide.common.rendering.api.RenderSession;
21 import com.android.ide.common.rendering.api.Result;
22 import com.android.ide.common.rendering.api.Result.Status;
23 import com.android.layoutlib.bridge.Bridge;
24 import com.android.layoutlib.bridge.impl.RenderSessionImpl;
25 
26 import android.os.Handler;
27 import android.os.Handler_Delegate;
28 import android.os.Handler_Delegate.IHandlerCallback;
29 import android.os.Message;
30 
31 import java.util.PriorityQueue;
32 import java.util.Queue;
33 
34 /**
35  * Abstract animation thread.
36  * <p/>
37  * This does not actually start an animation, instead it fakes a looper that will play whatever
38  * animation is sending messages to its own {@link Handler}.
39  * <p/>
40  * Classes should implement {@link #preAnimation()} and {@link #postAnimation()}.
41  * <p/>
42  * If {@link #preAnimation()} does not start an animation somehow then the thread doesn't do
43  * anything.
44  *
45  */
46 public abstract class AnimationThread extends Thread {
47 
48     private static class MessageBundle implements Comparable<MessageBundle> {
49         final Handler mTarget;
50         final Message mMessage;
51         final long mUptimeMillis;
52 
MessageBundle(Handler target, Message message, long uptimeMillis)53         MessageBundle(Handler target, Message message, long uptimeMillis) {
54             mTarget = target;
55             mMessage = message;
56             mUptimeMillis = uptimeMillis;
57         }
58 
59         @Override
compareTo(MessageBundle bundle)60         public int compareTo(MessageBundle bundle) {
61             if (mUptimeMillis < bundle.mUptimeMillis) {
62                 return -1;
63             }
64             return 1;
65         }
66     }
67 
68     private final RenderSessionImpl mSession;
69 
70     private Queue<MessageBundle> mQueue = new PriorityQueue<MessageBundle>();
71     private final IAnimationListener mListener;
72 
AnimationThread(RenderSessionImpl scene, String threadName, IAnimationListener listener)73     public AnimationThread(RenderSessionImpl scene, String threadName,
74             IAnimationListener listener) {
75         super(threadName);
76         mSession = scene;
77         mListener = listener;
78     }
79 
preAnimation()80     public abstract Result preAnimation();
postAnimation()81     public abstract void postAnimation();
82 
83     @Override
run()84     public void run() {
85         Bridge.prepareThread();
86         try {
87             /* FIXME: The ANIMATION_FRAME message no longer exists.  Instead, the
88              * animation timing loop is completely based on a Choreographer objects
89              * that schedules animation and drawing frames.  The animation handler is
90              * no longer even a handler; it is just a Runnable enqueued on the Choreographer.
91             Handler_Delegate.setCallback(new IHandlerCallback() {
92                 @Override
93                 public void sendMessageAtTime(Handler handler, Message msg, long uptimeMillis) {
94                     if (msg.what == ValueAnimator.ANIMATION_START ||
95                             msg.what == ValueAnimator.ANIMATION_FRAME) {
96                         mQueue.add(new MessageBundle(handler, msg, uptimeMillis));
97                     } else {
98                         // just ignore.
99                     }
100                 }
101             });
102             */
103 
104             // call out to the pre-animation work, which should start an animation or more.
105             Result result = preAnimation();
106             if (result.isSuccess() == false) {
107                 mListener.done(result);
108             }
109 
110             // loop the animation
111             RenderSession session = mSession.getSession();
112             do {
113                 // check early.
114                 if (mListener.isCanceled()) {
115                     break;
116                 }
117 
118                 // get the next message.
119                 MessageBundle bundle = mQueue.poll();
120                 if (bundle == null) {
121                     break;
122                 }
123 
124                 // sleep enough for this bundle to be on time
125                 long currentTime = System.currentTimeMillis();
126                 if (currentTime < bundle.mUptimeMillis) {
127                     try {
128                         sleep(bundle.mUptimeMillis - currentTime);
129                     } catch (InterruptedException e) {
130                         // FIXME log/do something/sleep again?
131                         e.printStackTrace();
132                     }
133                 }
134 
135                 // check after sleeping.
136                 if (mListener.isCanceled()) {
137                     break;
138                 }
139 
140                 // ready to do the work, acquire the scene.
141                 result = mSession.acquire(250);
142                 if (result.isSuccess() == false) {
143                     mListener.done(result);
144                     return;
145                 }
146 
147                 // process the bundle. If the animation is not finished, this will enqueue
148                 // the next message, so mQueue will have another one.
149                 try {
150                     // check after acquiring in case it took a while.
151                     if (mListener.isCanceled()) {
152                         break;
153                     }
154 
155                     bundle.mTarget.handleMessage(bundle.mMessage);
156                     if (mSession.render(false /*freshRender*/).isSuccess()) {
157                         mListener.onNewFrame(session);
158                     }
159                 } finally {
160                     mSession.release();
161                 }
162             } while (mListener.isCanceled() == false && mQueue.size() > 0);
163 
164             mListener.done(Status.SUCCESS.createResult());
165 
166         } catch (Throwable throwable) {
167             // can't use Bridge.getLog() as the exception might be thrown outside
168             // of an acquire/release block.
169             mListener.done(Status.ERROR_UNKNOWN.createResult("Error playing animation", throwable));
170 
171         } finally {
172             postAnimation();
173             Handler_Delegate.setCallback(null);
174             Bridge.cleanupThread();
175         }
176     }
177 }
178