blob: a32e711df53429cc197c9ba4a74868e4ebbd107a [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;
Jorim Jaggi980c9de2017-11-17 01:41:37 +010025import android.util.ArrayMap;
Jorim Jaggi21c39a72017-10-20 15:47:51 +020026import android.util.Slog;
27import android.view.SurfaceControl;
28import android.view.SurfaceControl.Transaction;
29
Jorim Jaggi980c9de2017-11-17 01:41:37 +010030import com.android.internal.annotations.VisibleForTesting;
31
Jorim Jaggi21c39a72017-10-20 15:47:51 +020032import java.io.PrintWriter;
33
34/**
35 * A class that can run animations on objects that have a set of child surfaces. We do this by
36 * reparenting all child surfaces of an object onto a new surface, called the "Leash". The Leash
37 * gets attached in the surface hierarchy where the the children were attached to. We then hand off
38 * the Leash to the component handling the animation, which is specified by the
39 * {@link AnimationAdapter}. When the animation is done animating, our callback to finish the
40 * animation will be invoked, at which we reparent the children back to the original parent.
41 */
42class SurfaceAnimator {
43
44 private static final String TAG = TAG_WITH_CLASS_NAME ? "SurfaceAnimator" : TAG_WM;
45 private final WindowManagerService mService;
46 private AnimationAdapter mAnimation;
Jorim Jaggi980c9de2017-11-17 01:41:37 +010047
48 @VisibleForTesting
49 SurfaceControl mLeash;
Jorim Jaggi21c39a72017-10-20 15:47:51 +020050 private final Animatable mAnimatable;
51 private final OnAnimationFinishedCallback mInnerAnimationFinishedCallback;
52 private final Runnable mAnimationFinishedCallback;
53 private boolean mAnimationStartDelayed;
54
55 /**
56 * @param animatable The object to animate.
57 * @param animationFinishedCallback Callback to invoke when an animation has finished running.
58 */
59 SurfaceAnimator(Animatable animatable, Runnable animationFinishedCallback,
60 WindowManagerService service) {
61 mAnimatable = animatable;
62 mService = service;
63 mAnimationFinishedCallback = animationFinishedCallback;
64 mInnerAnimationFinishedCallback = getFinishedCallback(animationFinishedCallback);
65 }
66
67 private OnAnimationFinishedCallback getFinishedCallback(Runnable animationFinishedCallback) {
68 return anim -> {
69 synchronized (mService.mWindowMap) {
Jorim Jaggi980c9de2017-11-17 01:41:37 +010070 final SurfaceAnimator target = mService.mAnimationTransferMap.remove(anim);
71 if (target != null) {
72 target.mInnerAnimationFinishedCallback.onAnimationFinished(anim);
73 return;
74 }
Jorim Jaggi21c39a72017-10-20 15:47:51 +020075
Jorim Jaggia3844032018-01-03 15:54:43 +010076 // 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 // For now we need to run this after it's guaranteed that the transaction that
80 // reparents the surface onto the leash is executed already. Otherwise this may be
81 // executed first, leading to surface loss, as the reparent operations wouldn't
82 // be in order.
83 mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
84 if (anim != mAnimation) {
85 // Callback was from another animation - ignore.
86 return;
87 }
88 final Transaction t = new Transaction();
89 SurfaceControl.openTransaction();
90 try {
91 reset(t, true /* destroyLeash */);
92 animationFinishedCallback.run();
93 } finally {
94 SurfaceControl.mergeToGlobalTransaction(t);
95 SurfaceControl.closeTransaction();
96 }
97 });
Jorim Jaggi21c39a72017-10-20 15:47:51 +020098 }
99 };
100 }
101
102 /**
103 * Starts an animation.
104 *
105 * @param anim The object that bridges the controller, {@link SurfaceAnimator}, with the
106 * component responsible for running the animation. It runs the animation with
107 * {@link AnimationAdapter#startAnimation} once the hierarchy with
108 * the Leash has been set up.
109 * @param hidden Whether the container holding the child surfaces is currently visible or not.
110 * This is important as it will start with the leash hidden or visible before
111 * handing it to the component that is responsible to run the animation.
112 */
113 void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden) {
Jorim Jaggi980c9de2017-11-17 01:41:37 +0100114 cancelAnimation(t, true /* restarting */, true /* forwardCancel */);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200115 mAnimation = anim;
Jorim Jaggia5e10572017-11-15 14:36:26 +0100116 final SurfaceControl surface = mAnimatable.getSurfaceControl();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200117 if (surface == null) {
118 Slog.w(TAG, "Unable to start animation, surface is null or no children.");
119 cancelAnimation();
120 return;
121 }
122 mLeash = createAnimationLeash(surface, t,
123 mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), hidden);
Jorim Jaggia5e10572017-11-15 14:36:26 +0100124 mAnimatable.onAnimationLeashCreated(t, mLeash);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200125 if (mAnimationStartDelayed) {
126 if (DEBUG_ANIM) Slog.i(TAG, "Animation start delayed");
127 return;
128 }
129 mAnimation.startAnimation(mLeash, t, mInnerAnimationFinishedCallback);
130 }
131
132 /**
133 * Begins with delaying all animations to start. Any subsequent call to {@link #startAnimation}
134 * will not start the animation until {@link #endDelayingAnimationStart} is called. When an
135 * animation start is being delayed, the animator is considered animating already.
136 */
137 void startDelayingAnimationStart() {
138
139 // We only allow delaying animation start we are not currently animating
140 if (!isAnimating()) {
141 mAnimationStartDelayed = true;
142 }
143 }
144
145 /**
146 * See {@link #startDelayingAnimationStart}.
147 */
148 void endDelayingAnimationStart() {
149 final boolean delayed = mAnimationStartDelayed;
150 mAnimationStartDelayed = false;
151 if (delayed && mAnimation != null) {
152 mAnimation.startAnimation(mLeash, mAnimatable.getPendingTransaction(),
153 mInnerAnimationFinishedCallback);
154 mAnimatable.commitPendingTransaction();
155 }
156 }
157
158 /**
159 * @return Whether we are currently running an animation, or we have a pending animation that
160 * is waiting to be started with {@link #endDelayingAnimationStart}
161 */
162 boolean isAnimating() {
163 return mAnimation != null;
164 }
165
166 /**
167 * @return The current animation spec if we are running an animation, or {@code null} otherwise.
168 */
169 AnimationAdapter getAnimation() {
170 return mAnimation;
171 }
172
173 /**
174 * Cancels any currently running animation.
175 */
176 void cancelAnimation() {
Jorim Jaggi980c9de2017-11-17 01:41:37 +0100177 cancelAnimation(mAnimatable.getPendingTransaction(), false /* restarting */,
178 true /* forwardCancel */);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200179 mAnimatable.commitPendingTransaction();
180 }
181
182 /**
183 * Sets the layer of the surface.
184 * <p>
185 * When the layer of the surface needs to be adjusted, we need to set it on the leash if the
186 * surface is reparented to the leash. This method takes care of that.
187 */
188 void setLayer(Transaction t, int layer) {
Jorim Jaggia5e10572017-11-15 14:36:26 +0100189 t.setLayer(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), layer);
190 }
191
192 /**
193 * Sets the surface to be relatively layered.
194 *
195 * @see #setLayer
196 */
197 void setRelativeLayer(Transaction t, SurfaceControl relativeTo, int layer) {
198 t.setRelativeLayer(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), relativeTo, layer);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200199 }
200
201 /**
202 * Reparents the surface.
203 *
204 * @see #setLayer
205 */
206 void reparent(Transaction t, SurfaceControl newParent) {
Jorim Jaggia5e10572017-11-15 14:36:26 +0100207 t.reparent(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), newParent.getHandle());
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200208 }
209
210 /**
211 * @return True if the surface is attached to the leash; false otherwise.
212 */
213 boolean hasLeash() {
214 return mLeash != null;
215 }
216
Jorim Jaggi980c9de2017-11-17 01:41:37 +0100217 void transferAnimation(SurfaceAnimator from) {
218 if (from.mLeash == null) {
219 return;
220 }
221 final SurfaceControl surface = mAnimatable.getSurfaceControl();
Jorim Jaggi596a1992017-12-29 14:48:02 +0100222 final SurfaceControl parent = mAnimatable.getAnimationLeashParent();
Jorim Jaggi980c9de2017-11-17 01:41:37 +0100223 if (surface == null || parent == null) {
224 Slog.w(TAG, "Unable to transfer animation, surface or parent is null");
225 cancelAnimation();
226 return;
227 }
228 endDelayingAnimationStart();
229 final Transaction t = mAnimatable.getPendingTransaction();
230 cancelAnimation(t, true /* restarting */, true /* forwardCancel */);
231 mLeash = from.mLeash;
232 mAnimation = from.mAnimation;
233
234 // Cancel source animation, but don't let animation runner cancel the animation.
235 from.cancelAnimation(t, false /* restarting */, false /* forwardCancel */);
236 t.reparent(surface, mLeash.getHandle());
237 t.reparent(mLeash, parent.getHandle());
238 mAnimatable.onAnimationLeashCreated(t, mLeash);
239 mService.mAnimationTransferMap.put(mAnimation, this);
240 }
241
242 /**
243 * Cancels the animation, and resets the leash.
244 *
245 * @param t The transaction to use for all cancelling surface operations.
246 * @param restarting Whether we are restarting the animation.
247 * @param forwardCancel Whether to forward the cancel signal to the adapter executing the
248 * animation. This will be set to false when just transferring an animation
249 * to another animator.
250 */
251 private void cancelAnimation(Transaction t, boolean restarting, boolean forwardCancel) {
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200252 if (DEBUG_ANIM) Slog.i(TAG, "Cancelling animation restarting=" + restarting);
253 final SurfaceControl leash = mLeash;
254 final AnimationAdapter animation = mAnimation;
Jorim Jaggi980c9de2017-11-17 01:41:37 +0100255 reset(t, forwardCancel);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200256 if (animation != null) {
Jorim Jaggi980c9de2017-11-17 01:41:37 +0100257 if (!mAnimationStartDelayed && forwardCancel) {
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200258 animation.onAnimationCancelled(leash);
259 }
260 if (!restarting) {
261 mAnimationFinishedCallback.run();
262 }
263 }
264 if (!restarting) {
265 mAnimationStartDelayed = false;
266 }
267 }
268
Jorim Jaggi980c9de2017-11-17 01:41:37 +0100269 private void reset(Transaction t, boolean destroyLeash) {
Jorim Jaggia5e10572017-11-15 14:36:26 +0100270 final SurfaceControl surface = mAnimatable.getSurfaceControl();
271 final SurfaceControl parent = mAnimatable.getParentSurfaceControl();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200272
273 // If the surface was destroyed, we don't care to reparent it back.
274 final boolean destroy = mLeash != null && surface != null && parent != null;
275 if (destroy) {
276 if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to original parent");
277 t.reparent(surface, parent.getHandle());
278 }
Jorim Jaggi980c9de2017-11-17 01:41:37 +0100279 mService.mAnimationTransferMap.remove(mAnimation);
280 if (mLeash != null && destroyLeash) {
Jorim Jaggia5e10572017-11-15 14:36:26 +0100281 mAnimatable.destroyAfterPendingTransaction(mLeash);
282 }
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200283 mLeash = null;
284 mAnimation = null;
285
286 // Make sure to inform the animatable after the leash was destroyed.
287 if (destroy) {
Jorim Jaggia5e10572017-11-15 14:36:26 +0100288 mAnimatable.onAnimationLeashDestroyed(t);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200289 }
290 }
291
292 private SurfaceControl createAnimationLeash(SurfaceControl surface, Transaction t, int width,
293 int height, boolean hidden) {
294 if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash");
Jorim Jaggia5e10572017-11-15 14:36:26 +0100295 final SurfaceControl.Builder builder = mAnimatable.makeAnimationLeash()
Jorim Jaggi596a1992017-12-29 14:48:02 +0100296 .setParent(mAnimatable.getAnimationLeashParent())
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200297 .setName(surface + " - animation-leash")
298 .setSize(width, height);
299 final SurfaceControl leash = builder.build();
300 if (!hidden) {
301 t.show(leash);
302 }
303 t.reparent(surface, leash.getHandle());
304 return leash;
305 }
306
307 void dump(PrintWriter pw, String prefix) {
308 pw.print(prefix); pw.print("mAnimation="); pw.print(mAnimation);
309 pw.print(" mLeash="); pw.println(mLeash);
310 }
311
312 /**
313 * Callback to be passed into {@link AnimationAdapter#startAnimation} to be invoked by the
314 * component that is running the animation when the animation is finished.
315 */
316 interface OnAnimationFinishedCallback {
317 void onAnimationFinished(AnimationAdapter anim);
318 }
319
320 /**
321 * Interface to be animated by {@link SurfaceAnimator}.
322 */
323 interface Animatable {
324
325 /**
326 * @return The pending transaction that will be committed in the next frame.
327 */
328 @NonNull Transaction getPendingTransaction();
329
330 /**
331 * Schedules a commit of the pending transaction.
332 */
333 void commitPendingTransaction();
334
335 /**
336 * Called when the was created.
337 *
338 * @param t The transaction to use to apply any necessary changes.
339 * @param leash The leash that was created.
340 */
Jorim Jaggia5e10572017-11-15 14:36:26 +0100341 void onAnimationLeashCreated(Transaction t, SurfaceControl leash);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200342
343 /**
344 * Called when the leash is being destroyed, and the surface was reparented back to the
345 * original parent.
346 *
347 * @param t The transaction to use to apply any necessary changes.
348 */
Jorim Jaggia5e10572017-11-15 14:36:26 +0100349 void onAnimationLeashDestroyed(Transaction t);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200350
351 /**
Jorim Jaggia5e10572017-11-15 14:36:26 +0100352 * Destroy a given surface after executing {@link #getPendingTransaction}.
353 *
354 * @see WindowContainer#destroyAfterPendingTransaction
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200355 */
Jorim Jaggia5e10572017-11-15 14:36:26 +0100356 void destroyAfterPendingTransaction(SurfaceControl surface);
357
358 /**
359 * @return A new surface to be used for the animation leash, inserted at the correct
360 * position in the hierarchy.
361 */
362 SurfaceControl.Builder makeAnimationLeash();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200363
364 /**
Jorim Jaggi596a1992017-12-29 14:48:02 +0100365 * @return The parent that should be used for the animation leash.
366 */
367 @Nullable SurfaceControl getAnimationLeashParent();
368
369 /**
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200370 * @return The surface of the object to be animated.
371 */
Jorim Jaggia5e10572017-11-15 14:36:26 +0100372 @Nullable SurfaceControl getSurfaceControl();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200373
374 /**
375 * @return The parent of the surface object to be animated.
376 */
Jorim Jaggia5e10572017-11-15 14:36:26 +0100377 @Nullable SurfaceControl getParentSurfaceControl();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200378
379 /**
380 * @return The width of the surface to be animated.
381 */
382 int getSurfaceWidth();
383
384 /**
385 * @return The height of the surface to be animated.
386 */
387 int getSurfaceHeight();
388 }
389}