blob: e1652111a64ebf0ba3143aa85dd5da6719757340 [file] [log] [blame]
Jorim Jaggi21c39a72017-10-20 15:47:51 +02001/*
2 * Copyright (C) 2017 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
17package com.android.server.wm;
18
19import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
20import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
21import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
22
23import android.annotation.NonNull;
24import android.annotation.Nullable;
25import android.util.Slog;
26import android.view.SurfaceControl;
27import android.view.SurfaceControl.Transaction;
28
29import java.io.PrintWriter;
30
31/**
32 * A class that can run animations on objects that have a set of child surfaces. We do this by
33 * reparenting all child surfaces of an object onto a new surface, called the "Leash". The Leash
34 * gets attached in the surface hierarchy where the the children were attached to. We then hand off
35 * the Leash to the component handling the animation, which is specified by the
36 * {@link AnimationAdapter}. When the animation is done animating, our callback to finish the
37 * animation will be invoked, at which we reparent the children back to the original parent.
38 */
39class SurfaceAnimator {
40
41 private static final String TAG = TAG_WITH_CLASS_NAME ? "SurfaceAnimator" : TAG_WM;
42 private final WindowManagerService mService;
43 private AnimationAdapter mAnimation;
44 private SurfaceControl mLeash;
45 private final Animatable mAnimatable;
46 private final OnAnimationFinishedCallback mInnerAnimationFinishedCallback;
47 private final Runnable mAnimationFinishedCallback;
48 private boolean mAnimationStartDelayed;
49
50 /**
51 * @param animatable The object to animate.
52 * @param animationFinishedCallback Callback to invoke when an animation has finished running.
53 */
54 SurfaceAnimator(Animatable animatable, Runnable animationFinishedCallback,
55 WindowManagerService service) {
56 mAnimatable = animatable;
57 mService = service;
58 mAnimationFinishedCallback = animationFinishedCallback;
59 mInnerAnimationFinishedCallback = getFinishedCallback(animationFinishedCallback);
60 }
61
62 private OnAnimationFinishedCallback getFinishedCallback(Runnable animationFinishedCallback) {
63 return anim -> {
64 synchronized (mService.mWindowMap) {
65 if (anim != mAnimation) {
66 // Callback was from another animation - ignore.
67 return;
68 }
69
70 final Transaction t = new Transaction();
71 SurfaceControl.openTransaction();
72 try {
73 reset(t);
74 animationFinishedCallback.run();
75 } finally {
76 // TODO: This should use pendingTransaction eventually, but right now things
77 // happening on the animation finished callback are happening on the global
78 // transaction.
79 SurfaceControl.mergeToGlobalTransaction(t);
80 SurfaceControl.closeTransaction();
81 }
82 }
83 };
84 }
85
86 /**
87 * Starts an animation.
88 *
89 * @param anim The object that bridges the controller, {@link SurfaceAnimator}, with the
90 * component responsible for running the animation. It runs the animation with
91 * {@link AnimationAdapter#startAnimation} once the hierarchy with
92 * the Leash has been set up.
93 * @param hidden Whether the container holding the child surfaces is currently visible or not.
94 * This is important as it will start with the leash hidden or visible before
95 * handing it to the component that is responsible to run the animation.
96 */
97 void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden) {
98 cancelAnimation(t, true /* restarting */);
99 mAnimation = anim;
Jorim Jaggia5e10572017-11-15 14:36:26 +0100100 final SurfaceControl surface = mAnimatable.getSurfaceControl();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200101 if (surface == null) {
102 Slog.w(TAG, "Unable to start animation, surface is null or no children.");
103 cancelAnimation();
104 return;
105 }
106 mLeash = createAnimationLeash(surface, t,
107 mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), hidden);
Jorim Jaggia5e10572017-11-15 14:36:26 +0100108 mAnimatable.onAnimationLeashCreated(t, mLeash);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200109 if (mAnimationStartDelayed) {
110 if (DEBUG_ANIM) Slog.i(TAG, "Animation start delayed");
111 return;
112 }
113 mAnimation.startAnimation(mLeash, t, mInnerAnimationFinishedCallback);
114 }
115
116 /**
117 * Begins with delaying all animations to start. Any subsequent call to {@link #startAnimation}
118 * will not start the animation until {@link #endDelayingAnimationStart} is called. When an
119 * animation start is being delayed, the animator is considered animating already.
120 */
121 void startDelayingAnimationStart() {
122
123 // We only allow delaying animation start we are not currently animating
124 if (!isAnimating()) {
125 mAnimationStartDelayed = true;
126 }
127 }
128
129 /**
130 * See {@link #startDelayingAnimationStart}.
131 */
132 void endDelayingAnimationStart() {
133 final boolean delayed = mAnimationStartDelayed;
134 mAnimationStartDelayed = false;
135 if (delayed && mAnimation != null) {
136 mAnimation.startAnimation(mLeash, mAnimatable.getPendingTransaction(),
137 mInnerAnimationFinishedCallback);
138 mAnimatable.commitPendingTransaction();
139 }
140 }
141
142 /**
143 * @return Whether we are currently running an animation, or we have a pending animation that
144 * is waiting to be started with {@link #endDelayingAnimationStart}
145 */
146 boolean isAnimating() {
147 return mAnimation != null;
148 }
149
150 /**
151 * @return The current animation spec if we are running an animation, or {@code null} otherwise.
152 */
153 AnimationAdapter getAnimation() {
154 return mAnimation;
155 }
156
157 /**
158 * Cancels any currently running animation.
159 */
160 void cancelAnimation() {
161 cancelAnimation(mAnimatable.getPendingTransaction(), false /* restarting */);
162 mAnimatable.commitPendingTransaction();
163 }
164
165 /**
166 * Sets the layer of the surface.
167 * <p>
168 * When the layer of the surface needs to be adjusted, we need to set it on the leash if the
169 * surface is reparented to the leash. This method takes care of that.
170 */
171 void setLayer(Transaction t, int layer) {
Jorim Jaggia5e10572017-11-15 14:36:26 +0100172 t.setLayer(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), layer);
173 }
174
175 /**
176 * Sets the surface to be relatively layered.
177 *
178 * @see #setLayer
179 */
180 void setRelativeLayer(Transaction t, SurfaceControl relativeTo, int layer) {
181 t.setRelativeLayer(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), relativeTo, layer);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200182 }
183
184 /**
185 * Reparents the surface.
186 *
187 * @see #setLayer
188 */
189 void reparent(Transaction t, SurfaceControl newParent) {
Jorim Jaggia5e10572017-11-15 14:36:26 +0100190 t.reparent(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), newParent.getHandle());
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200191 }
192
193 /**
194 * @return True if the surface is attached to the leash; false otherwise.
195 */
196 boolean hasLeash() {
197 return mLeash != null;
198 }
199
200 private void cancelAnimation(Transaction t, boolean restarting) {
201 if (DEBUG_ANIM) Slog.i(TAG, "Cancelling animation restarting=" + restarting);
202 final SurfaceControl leash = mLeash;
203 final AnimationAdapter animation = mAnimation;
204 reset(t);
205 if (animation != null) {
206 if (!mAnimationStartDelayed) {
207 animation.onAnimationCancelled(leash);
208 }
209 if (!restarting) {
210 mAnimationFinishedCallback.run();
211 }
212 }
213 if (!restarting) {
214 mAnimationStartDelayed = false;
215 }
216 }
217
218 private void reset(Transaction t) {
Jorim Jaggia5e10572017-11-15 14:36:26 +0100219 final SurfaceControl surface = mAnimatable.getSurfaceControl();
220 final SurfaceControl parent = mAnimatable.getParentSurfaceControl();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200221
222 // If the surface was destroyed, we don't care to reparent it back.
223 final boolean destroy = mLeash != null && surface != null && parent != null;
224 if (destroy) {
225 if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to original parent");
226 t.reparent(surface, parent.getHandle());
227 }
Jorim Jaggia5e10572017-11-15 14:36:26 +0100228 if (mLeash != null) {
229 mAnimatable.destroyAfterPendingTransaction(mLeash);
230 }
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200231 mLeash = null;
232 mAnimation = null;
233
234 // Make sure to inform the animatable after the leash was destroyed.
235 if (destroy) {
Jorim Jaggia5e10572017-11-15 14:36:26 +0100236 mAnimatable.onAnimationLeashDestroyed(t);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200237 }
238 }
239
240 private SurfaceControl createAnimationLeash(SurfaceControl surface, Transaction t, int width,
241 int height, boolean hidden) {
242 if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash");
Jorim Jaggia5e10572017-11-15 14:36:26 +0100243 final SurfaceControl.Builder builder = mAnimatable.makeAnimationLeash()
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200244 .setName(surface + " - animation-leash")
245 .setSize(width, height);
246 final SurfaceControl leash = builder.build();
247 if (!hidden) {
248 t.show(leash);
249 }
250 t.reparent(surface, leash.getHandle());
251 return leash;
252 }
253
254 void dump(PrintWriter pw, String prefix) {
255 pw.print(prefix); pw.print("mAnimation="); pw.print(mAnimation);
256 pw.print(" mLeash="); pw.println(mLeash);
257 }
258
259 /**
260 * Callback to be passed into {@link AnimationAdapter#startAnimation} to be invoked by the
261 * component that is running the animation when the animation is finished.
262 */
263 interface OnAnimationFinishedCallback {
264 void onAnimationFinished(AnimationAdapter anim);
265 }
266
267 /**
268 * Interface to be animated by {@link SurfaceAnimator}.
269 */
270 interface Animatable {
271
272 /**
273 * @return The pending transaction that will be committed in the next frame.
274 */
275 @NonNull Transaction getPendingTransaction();
276
277 /**
278 * Schedules a commit of the pending transaction.
279 */
280 void commitPendingTransaction();
281
282 /**
283 * Called when the was created.
284 *
285 * @param t The transaction to use to apply any necessary changes.
286 * @param leash The leash that was created.
287 */
Jorim Jaggia5e10572017-11-15 14:36:26 +0100288 void onAnimationLeashCreated(Transaction t, SurfaceControl leash);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200289
290 /**
291 * Called when the leash is being destroyed, and the surface was reparented back to the
292 * original parent.
293 *
294 * @param t The transaction to use to apply any necessary changes.
295 */
Jorim Jaggia5e10572017-11-15 14:36:26 +0100296 void onAnimationLeashDestroyed(Transaction t);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200297
298 /**
Jorim Jaggia5e10572017-11-15 14:36:26 +0100299 * Destroy a given surface after executing {@link #getPendingTransaction}.
300 *
301 * @see WindowContainer#destroyAfterPendingTransaction
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200302 */
Jorim Jaggia5e10572017-11-15 14:36:26 +0100303 void destroyAfterPendingTransaction(SurfaceControl surface);
304
305 /**
306 * @return A new surface to be used for the animation leash, inserted at the correct
307 * position in the hierarchy.
308 */
309 SurfaceControl.Builder makeAnimationLeash();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200310
311 /**
312 * @return The surface of the object to be animated.
313 */
Jorim Jaggia5e10572017-11-15 14:36:26 +0100314 @Nullable SurfaceControl getSurfaceControl();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200315
316 /**
317 * @return The parent of the surface object to be animated.
318 */
Jorim Jaggia5e10572017-11-15 14:36:26 +0100319 @Nullable SurfaceControl getParentSurfaceControl();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200320
321 /**
322 * @return The width of the surface to be animated.
323 */
324 int getSurfaceWidth();
325
326 /**
327 * @return The height of the surface to be animated.
328 */
329 int getSurfaceHeight();
330 }
331}