Aurimas Liutikas | 93554f2 | 2022-04-19 16:51:35 -0700 | [diff] [blame] | 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.Message; |
| 29 | |
| 30 | import java.util.PriorityQueue; |
| 31 | import java.util.Queue; |
| 32 | |
| 33 | /** |
| 34 | * Abstract animation thread. |
| 35 | * <p/> |
| 36 | * This does not actually start an animation, instead it fakes a looper that will play whatever |
| 37 | * animation is sending messages to its own {@link Handler}. |
| 38 | * <p/> |
| 39 | * Classes should implement {@link #preAnimation()} and {@link #postAnimation()}. |
| 40 | * <p/> |
| 41 | * If {@link #preAnimation()} does not start an animation somehow then the thread doesn't do |
| 42 | * anything. |
| 43 | * |
| 44 | */ |
| 45 | public abstract class AnimationThread extends Thread { |
| 46 | |
| 47 | private static class MessageBundle implements Comparable<MessageBundle> { |
| 48 | final Handler mTarget; |
| 49 | final Message mMessage; |
| 50 | final long mUptimeMillis; |
| 51 | |
| 52 | MessageBundle(Handler target, Message message, long uptimeMillis) { |
| 53 | mTarget = target; |
| 54 | mMessage = message; |
| 55 | mUptimeMillis = uptimeMillis; |
| 56 | } |
| 57 | |
| 58 | @Override |
| 59 | public int compareTo(MessageBundle bundle) { |
| 60 | if (mUptimeMillis < bundle.mUptimeMillis) { |
| 61 | return -1; |
| 62 | } |
| 63 | return 1; |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | private final RenderSessionImpl mSession; |
| 68 | |
| 69 | private Queue<MessageBundle> mQueue = new PriorityQueue<MessageBundle>(); |
| 70 | private final IAnimationListener mListener; |
| 71 | |
| 72 | public AnimationThread(RenderSessionImpl scene, String threadName, |
| 73 | IAnimationListener listener) { |
| 74 | super(threadName); |
| 75 | mSession = scene; |
| 76 | mListener = listener; |
| 77 | } |
| 78 | |
| 79 | public abstract Result preAnimation(); |
| 80 | public abstract void postAnimation(); |
| 81 | |
| 82 | @Override |
| 83 | public void run() { |
| 84 | Bridge.prepareThread(); |
| 85 | try { |
| 86 | /* FIXME: The ANIMATION_FRAME message no longer exists. Instead, the |
| 87 | * animation timing loop is completely based on a Choreographer objects |
| 88 | * that schedules animation and drawing frames. The animation handler is |
| 89 | * no longer even a handler; it is just a Runnable enqueued on the Choreographer. |
| 90 | Handler_Delegate.setCallback(new IHandlerCallback() { |
| 91 | @Override |
| 92 | public void sendMessageAtTime(Handler handler, Message msg, long uptimeMillis) { |
| 93 | if (msg.what == ValueAnimator.ANIMATION_START || |
| 94 | msg.what == ValueAnimator.ANIMATION_FRAME) { |
| 95 | mQueue.add(new MessageBundle(handler, msg, uptimeMillis)); |
| 96 | } else { |
| 97 | // just ignore. |
| 98 | } |
| 99 | } |
| 100 | }); |
| 101 | */ |
| 102 | |
| 103 | // call out to the pre-animation work, which should start an animation or more. |
| 104 | Result result = preAnimation(); |
| 105 | if (result.isSuccess() == false) { |
| 106 | mListener.done(result); |
| 107 | } |
| 108 | |
| 109 | // loop the animation |
| 110 | RenderSession session = mSession.getSession(); |
| 111 | do { |
| 112 | // check early. |
| 113 | if (mListener.isCanceled()) { |
| 114 | break; |
| 115 | } |
| 116 | |
| 117 | // get the next message. |
| 118 | MessageBundle bundle = mQueue.poll(); |
| 119 | if (bundle == null) { |
| 120 | break; |
| 121 | } |
| 122 | |
| 123 | // sleep enough for this bundle to be on time |
| 124 | long currentTime = System.currentTimeMillis(); |
| 125 | if (currentTime < bundle.mUptimeMillis) { |
| 126 | try { |
| 127 | sleep(bundle.mUptimeMillis - currentTime); |
| 128 | } catch (InterruptedException e) { |
| 129 | // FIXME log/do something/sleep again? |
| 130 | e.printStackTrace(); |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | // check after sleeping. |
| 135 | if (mListener.isCanceled()) { |
| 136 | break; |
| 137 | } |
| 138 | |
| 139 | // ready to do the work, acquire the scene. |
| 140 | result = mSession.acquire(250); |
| 141 | if (result.isSuccess() == false) { |
| 142 | mListener.done(result); |
| 143 | return; |
| 144 | } |
| 145 | |
| 146 | // process the bundle. If the animation is not finished, this will enqueue |
| 147 | // the next message, so mQueue will have another one. |
| 148 | try { |
| 149 | // check after acquiring in case it took a while. |
| 150 | if (mListener.isCanceled()) { |
| 151 | break; |
| 152 | } |
| 153 | |
| 154 | bundle.mTarget.handleMessage(bundle.mMessage); |
| 155 | if (mSession.render(false /*freshRender*/).isSuccess()) { |
| 156 | mListener.onNewFrame(session); |
| 157 | } |
| 158 | } finally { |
| 159 | mSession.release(); |
| 160 | } |
| 161 | } while (mListener.isCanceled() == false && mQueue.size() > 0); |
| 162 | |
| 163 | mListener.done(Status.SUCCESS.createResult()); |
| 164 | |
| 165 | } catch (Throwable throwable) { |
| 166 | // can't use Bridge.getLog() as the exception might be thrown outside |
| 167 | // of an acquire/release block. |
| 168 | mListener.done(Status.ERROR_UNKNOWN.createResult("Error playing animation", throwable)); |
| 169 | |
| 170 | } finally { |
| 171 | postAnimation(); |
| 172 | Handler_Delegate.setCallback(null); |
| 173 | Bridge.cleanupThread(); |
| 174 | } |
| 175 | } |
| 176 | } |