diff options
Diffstat (limited to 'android/animation/AnimationThread.java')
-rw-r--r-- | android/animation/AnimationThread.java | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/android/animation/AnimationThread.java b/android/animation/AnimationThread.java new file mode 100644 index 00000000..ce2aec79 --- /dev/null +++ b/android/animation/AnimationThread.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.animation; + +import com.android.ide.common.rendering.api.IAnimationListener; +import com.android.ide.common.rendering.api.RenderSession; +import com.android.ide.common.rendering.api.Result; +import com.android.ide.common.rendering.api.Result.Status; +import com.android.layoutlib.bridge.Bridge; +import com.android.layoutlib.bridge.impl.RenderSessionImpl; + +import android.os.Handler; +import android.os.Handler_Delegate; +import android.os.Message; + +import java.util.PriorityQueue; +import java.util.Queue; + +/** + * Abstract animation thread. + * <p/> + * This does not actually start an animation, instead it fakes a looper that will play whatever + * animation is sending messages to its own {@link Handler}. + * <p/> + * Classes should implement {@link #preAnimation()} and {@link #postAnimation()}. + * <p/> + * If {@link #preAnimation()} does not start an animation somehow then the thread doesn't do + * anything. + * + */ +public abstract class AnimationThread extends Thread { + + private static class MessageBundle implements Comparable<MessageBundle> { + final Handler mTarget; + final Message mMessage; + final long mUptimeMillis; + + MessageBundle(Handler target, Message message, long uptimeMillis) { + mTarget = target; + mMessage = message; + mUptimeMillis = uptimeMillis; + } + + @Override + public int compareTo(MessageBundle bundle) { + if (mUptimeMillis < bundle.mUptimeMillis) { + return -1; + } + return 1; + } + } + + private final RenderSessionImpl mSession; + + private Queue<MessageBundle> mQueue = new PriorityQueue<MessageBundle>(); + private final IAnimationListener mListener; + + public AnimationThread(RenderSessionImpl scene, String threadName, + IAnimationListener listener) { + super(threadName); + mSession = scene; + mListener = listener; + } + + public abstract Result preAnimation(); + public abstract void postAnimation(); + + @Override + public void run() { + Bridge.prepareThread(); + try { + /* FIXME: The ANIMATION_FRAME message no longer exists. Instead, the + * animation timing loop is completely based on a Choreographer objects + * that schedules animation and drawing frames. The animation handler is + * no longer even a handler; it is just a Runnable enqueued on the Choreographer. + Handler_Delegate.setCallback(new IHandlerCallback() { + @Override + public void sendMessageAtTime(Handler handler, Message msg, long uptimeMillis) { + if (msg.what == ValueAnimator.ANIMATION_START || + msg.what == ValueAnimator.ANIMATION_FRAME) { + mQueue.add(new MessageBundle(handler, msg, uptimeMillis)); + } else { + // just ignore. + } + } + }); + */ + + // call out to the pre-animation work, which should start an animation or more. + Result result = preAnimation(); + if (result.isSuccess() == false) { + mListener.done(result); + } + + // loop the animation + RenderSession session = mSession.getSession(); + do { + // check early. + if (mListener.isCanceled()) { + break; + } + + // get the next message. + MessageBundle bundle = mQueue.poll(); + if (bundle == null) { + break; + } + + // sleep enough for this bundle to be on time + long currentTime = System.currentTimeMillis(); + if (currentTime < bundle.mUptimeMillis) { + try { + sleep(bundle.mUptimeMillis - currentTime); + } catch (InterruptedException e) { + // FIXME log/do something/sleep again? + e.printStackTrace(); + } + } + + // check after sleeping. + if (mListener.isCanceled()) { + break; + } + + // ready to do the work, acquire the scene. + result = mSession.acquire(250); + if (result.isSuccess() == false) { + mListener.done(result); + return; + } + + // process the bundle. If the animation is not finished, this will enqueue + // the next message, so mQueue will have another one. + try { + // check after acquiring in case it took a while. + if (mListener.isCanceled()) { + break; + } + + bundle.mTarget.handleMessage(bundle.mMessage); + if (mSession.render(false /*freshRender*/).isSuccess()) { + mListener.onNewFrame(session); + } + } finally { + mSession.release(); + } + } while (mListener.isCanceled() == false && mQueue.size() > 0); + + mListener.done(Status.SUCCESS.createResult()); + + } catch (Throwable throwable) { + // can't use Bridge.getLog() as the exception might be thrown outside + // of an acquire/release block. + mListener.done(Status.ERROR_UNKNOWN.createResult("Error playing animation", throwable)); + + } finally { + postAnimation(); + Handler_Delegate.setCallback(null); + Bridge.cleanupThread(); + } + } +} |