blob: ba3d091f4a088569161a73972774ff18760a7138 [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;
Yi Jin6c6e9ca2018-03-20 16:53:35 -070022import static com.android.server.wm.SurfaceAnimatorProto.ANIMATION_ADAPTER;
23import static com.android.server.wm.SurfaceAnimatorProto.ANIMATION_START_DELAYED;
24import static com.android.server.wm.SurfaceAnimatorProto.LEASH;
Jorim Jaggi21c39a72017-10-20 15:47:51 +020025
26import android.annotation.NonNull;
27import android.annotation.Nullable;
28import android.util.Slog;
Vishnu Nair04ab4392018-01-10 11:00:06 -080029import android.util.proto.ProtoOutputStream;
Jorim Jaggi21c39a72017-10-20 15:47:51 +020030import android.view.SurfaceControl;
31import android.view.SurfaceControl.Transaction;
32
Jorim Jaggi980c9de2017-11-17 01:41:37 +010033import com.android.internal.annotations.VisibleForTesting;
34
Jorim Jaggi21c39a72017-10-20 15:47:51 +020035import java.io.PrintWriter;
36
37/**
38 * A class that can run animations on objects that have a set of child surfaces. We do this by
39 * reparenting all child surfaces of an object onto a new surface, called the "Leash". The Leash
40 * gets attached in the surface hierarchy where the the children were attached to. We then hand off
41 * the Leash to the component handling the animation, which is specified by the
42 * {@link AnimationAdapter}. When the animation is done animating, our callback to finish the
43 * animation will be invoked, at which we reparent the children back to the original parent.
44 */
45class SurfaceAnimator {
46
47 private static final String TAG = TAG_WITH_CLASS_NAME ? "SurfaceAnimator" : TAG_WM;
48 private final WindowManagerService mService;
49 private AnimationAdapter mAnimation;
Jorim Jaggi980c9de2017-11-17 01:41:37 +010050
51 @VisibleForTesting
52 SurfaceControl mLeash;
Jorim Jaggi21c39a72017-10-20 15:47:51 +020053 private final Animatable mAnimatable;
54 private final OnAnimationFinishedCallback mInnerAnimationFinishedCallback;
chaviwf20bd222018-02-01 16:06:52 -080055 @VisibleForTesting
56 final Runnable mAnimationFinishedCallback;
Jorim Jaggi21c39a72017-10-20 15:47:51 +020057 private boolean mAnimationStartDelayed;
58
59 /**
60 * @param animatable The object to animate.
61 * @param animationFinishedCallback Callback to invoke when an animation has finished running.
62 */
Winson Chunge2d72172018-01-25 17:46:20 +000063 SurfaceAnimator(Animatable animatable, @Nullable Runnable animationFinishedCallback,
Chavi Weingartenb736e322018-02-23 00:27:54 +000064 WindowManagerService service) {
Jorim Jaggi21c39a72017-10-20 15:47:51 +020065 mAnimatable = animatable;
66 mService = service;
67 mAnimationFinishedCallback = animationFinishedCallback;
Chavi Weingartenb736e322018-02-23 00:27:54 +000068 mInnerAnimationFinishedCallback = getFinishedCallback(animationFinishedCallback);
Jorim Jaggi21c39a72017-10-20 15:47:51 +020069 }
70
Winson Chunge2d72172018-01-25 17:46:20 +000071 private OnAnimationFinishedCallback getFinishedCallback(
Chavi Weingartenb736e322018-02-23 00:27:54 +000072 @Nullable Runnable animationFinishedCallback) {
Jorim Jaggi21c39a72017-10-20 15:47:51 +020073 return anim -> {
74 synchronized (mService.mWindowMap) {
Jorim Jaggi980c9de2017-11-17 01:41:37 +010075 final SurfaceAnimator target = mService.mAnimationTransferMap.remove(anim);
76 if (target != null) {
77 target.mInnerAnimationFinishedCallback.onAnimationFinished(anim);
78 return;
79 }
Jorim Jaggi21c39a72017-10-20 15:47:51 +020080
Chavi Weingartenb736e322018-02-23 00:27:54 +000081 if (anim != mAnimation) {
82 return;
83 }
Jorim Jaggi6de61012018-03-19 14:53:23 +010084 final Runnable resetAndInvokeFinish = () -> {
85 reset(mAnimatable.getPendingTransaction(), true /* destroyLeash */);
86 if (animationFinishedCallback != null) {
87 animationFinishedCallback.run();
88 }
89 };
90 if (!mAnimatable.shouldDeferAnimationFinish(resetAndInvokeFinish)) {
91 resetAndInvokeFinish.run();
Chavi Weingartenb736e322018-02-23 00:27:54 +000092 }
Jorim Jaggi21c39a72017-10-20 15:47:51 +020093 }
94 };
95 }
96
97 /**
98 * Starts an animation.
99 *
100 * @param anim The object that bridges the controller, {@link SurfaceAnimator}, with the
101 * component responsible for running the animation. It runs the animation with
102 * {@link AnimationAdapter#startAnimation} once the hierarchy with
103 * the Leash has been set up.
104 * @param hidden Whether the container holding the child surfaces is currently visible or not.
105 * This is important as it will start with the leash hidden or visible before
106 * handing it to the component that is responsible to run the animation.
107 */
108 void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden) {
Jorim Jaggi980c9de2017-11-17 01:41:37 +0100109 cancelAnimation(t, true /* restarting */, true /* forwardCancel */);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200110 mAnimation = anim;
Jorim Jaggia5e10572017-11-15 14:36:26 +0100111 final SurfaceControl surface = mAnimatable.getSurfaceControl();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200112 if (surface == null) {
113 Slog.w(TAG, "Unable to start animation, surface is null or no children.");
114 cancelAnimation();
115 return;
116 }
117 mLeash = createAnimationLeash(surface, t,
118 mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), hidden);
Jorim Jaggia5e10572017-11-15 14:36:26 +0100119 mAnimatable.onAnimationLeashCreated(t, mLeash);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200120 if (mAnimationStartDelayed) {
121 if (DEBUG_ANIM) Slog.i(TAG, "Animation start delayed");
122 return;
123 }
124 mAnimation.startAnimation(mLeash, t, mInnerAnimationFinishedCallback);
125 }
126
127 /**
128 * Begins with delaying all animations to start. Any subsequent call to {@link #startAnimation}
129 * will not start the animation until {@link #endDelayingAnimationStart} is called. When an
130 * animation start is being delayed, the animator is considered animating already.
131 */
132 void startDelayingAnimationStart() {
133
134 // We only allow delaying animation start we are not currently animating
135 if (!isAnimating()) {
136 mAnimationStartDelayed = true;
137 }
138 }
139
140 /**
141 * See {@link #startDelayingAnimationStart}.
142 */
143 void endDelayingAnimationStart() {
144 final boolean delayed = mAnimationStartDelayed;
145 mAnimationStartDelayed = false;
146 if (delayed && mAnimation != null) {
147 mAnimation.startAnimation(mLeash, mAnimatable.getPendingTransaction(),
148 mInnerAnimationFinishedCallback);
149 mAnimatable.commitPendingTransaction();
150 }
151 }
152
153 /**
154 * @return Whether we are currently running an animation, or we have a pending animation that
155 * is waiting to be started with {@link #endDelayingAnimationStart}
156 */
157 boolean isAnimating() {
158 return mAnimation != null;
159 }
160
161 /**
162 * @return The current animation spec if we are running an animation, or {@code null} otherwise.
163 */
164 AnimationAdapter getAnimation() {
165 return mAnimation;
166 }
167
168 /**
169 * Cancels any currently running animation.
170 */
171 void cancelAnimation() {
Jorim Jaggi980c9de2017-11-17 01:41:37 +0100172 cancelAnimation(mAnimatable.getPendingTransaction(), false /* restarting */,
173 true /* forwardCancel */);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200174 mAnimatable.commitPendingTransaction();
175 }
176
177 /**
178 * Sets the layer of the surface.
179 * <p>
180 * When the layer of the surface needs to be adjusted, we need to set it on the leash if the
181 * surface is reparented to the leash. This method takes care of that.
182 */
183 void setLayer(Transaction t, int layer) {
Jorim Jaggia5e10572017-11-15 14:36:26 +0100184 t.setLayer(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), layer);
185 }
186
187 /**
188 * Sets the surface to be relatively layered.
189 *
190 * @see #setLayer
191 */
192 void setRelativeLayer(Transaction t, SurfaceControl relativeTo, int layer) {
193 t.setRelativeLayer(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), relativeTo, layer);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200194 }
195
196 /**
197 * Reparents the surface.
198 *
199 * @see #setLayer
200 */
201 void reparent(Transaction t, SurfaceControl newParent) {
Jorim Jaggia5e10572017-11-15 14:36:26 +0100202 t.reparent(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), newParent.getHandle());
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200203 }
204
205 /**
206 * @return True if the surface is attached to the leash; false otherwise.
207 */
208 boolean hasLeash() {
209 return mLeash != null;
210 }
211
Jorim Jaggi980c9de2017-11-17 01:41:37 +0100212 void transferAnimation(SurfaceAnimator from) {
213 if (from.mLeash == null) {
214 return;
215 }
216 final SurfaceControl surface = mAnimatable.getSurfaceControl();
Jorim Jaggi596a1992017-12-29 14:48:02 +0100217 final SurfaceControl parent = mAnimatable.getAnimationLeashParent();
Jorim Jaggi980c9de2017-11-17 01:41:37 +0100218 if (surface == null || parent == null) {
219 Slog.w(TAG, "Unable to transfer animation, surface or parent is null");
220 cancelAnimation();
221 return;
222 }
223 endDelayingAnimationStart();
224 final Transaction t = mAnimatable.getPendingTransaction();
225 cancelAnimation(t, true /* restarting */, true /* forwardCancel */);
226 mLeash = from.mLeash;
227 mAnimation = from.mAnimation;
228
229 // Cancel source animation, but don't let animation runner cancel the animation.
230 from.cancelAnimation(t, false /* restarting */, false /* forwardCancel */);
231 t.reparent(surface, mLeash.getHandle());
232 t.reparent(mLeash, parent.getHandle());
233 mAnimatable.onAnimationLeashCreated(t, mLeash);
234 mService.mAnimationTransferMap.put(mAnimation, this);
235 }
236
Jorim Jaggic4d29f22018-03-22 16:30:56 +0100237 boolean isAnimationStartDelayed() {
238 return mAnimationStartDelayed;
239 }
240
Jorim Jaggi980c9de2017-11-17 01:41:37 +0100241 /**
242 * Cancels the animation, and resets the leash.
243 *
244 * @param t The transaction to use for all cancelling surface operations.
245 * @param restarting Whether we are restarting the animation.
246 * @param forwardCancel Whether to forward the cancel signal to the adapter executing the
247 * animation. This will be set to false when just transferring an animation
248 * to another animator.
249 */
250 private void cancelAnimation(Transaction t, boolean restarting, boolean forwardCancel) {
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200251 if (DEBUG_ANIM) Slog.i(TAG, "Cancelling animation restarting=" + restarting);
252 final SurfaceControl leash = mLeash;
253 final AnimationAdapter animation = mAnimation;
Jorim Jaggi980c9de2017-11-17 01:41:37 +0100254 reset(t, forwardCancel);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200255 if (animation != null) {
Jorim Jaggi980c9de2017-11-17 01:41:37 +0100256 if (!mAnimationStartDelayed && forwardCancel) {
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200257 animation.onAnimationCancelled(leash);
258 }
259 if (!restarting) {
260 mAnimationFinishedCallback.run();
261 }
262 }
263 if (!restarting) {
264 mAnimationStartDelayed = false;
265 }
266 }
267
Jorim Jaggi980c9de2017-11-17 01:41:37 +0100268 private void reset(Transaction t, boolean destroyLeash) {
Jorim Jaggia5e10572017-11-15 14:36:26 +0100269 final SurfaceControl surface = mAnimatable.getSurfaceControl();
270 final SurfaceControl parent = mAnimatable.getParentSurfaceControl();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200271
Chavi Weingartenb736e322018-02-23 00:27:54 +0000272 boolean scheduleAnim = false;
273
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200274 // If the surface was destroyed, we don't care to reparent it back.
275 final boolean destroy = mLeash != null && surface != null && parent != null;
276 if (destroy) {
277 if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to original parent");
278 t.reparent(surface, parent.getHandle());
Chavi Weingartenb736e322018-02-23 00:27:54 +0000279 scheduleAnim = true;
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200280 }
Jorim Jaggi980c9de2017-11-17 01:41:37 +0100281 mService.mAnimationTransferMap.remove(mAnimation);
282 if (mLeash != null && destroyLeash) {
Chavi Weingartenb736e322018-02-23 00:27:54 +0000283 t.destroy(mLeash);
284 scheduleAnim = true;
Jorim Jaggia5e10572017-11-15 14:36:26 +0100285 }
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200286 mLeash = null;
287 mAnimation = null;
288
289 // Make sure to inform the animatable after the leash was destroyed.
290 if (destroy) {
Jorim Jaggia5e10572017-11-15 14:36:26 +0100291 mAnimatable.onAnimationLeashDestroyed(t);
Chavi Weingartenb736e322018-02-23 00:27:54 +0000292 scheduleAnim = true;
293 }
294
295 if (scheduleAnim) {
296 mService.scheduleAnimationLocked();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200297 }
298 }
299
300 private SurfaceControl createAnimationLeash(SurfaceControl surface, Transaction t, int width,
301 int height, boolean hidden) {
302 if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash");
Jorim Jaggia5e10572017-11-15 14:36:26 +0100303 final SurfaceControl.Builder builder = mAnimatable.makeAnimationLeash()
Jorim Jaggi596a1992017-12-29 14:48:02 +0100304 .setParent(mAnimatable.getAnimationLeashParent())
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200305 .setName(surface + " - animation-leash")
306 .setSize(width, height);
307 final SurfaceControl leash = builder.build();
308 if (!hidden) {
309 t.show(leash);
310 }
311 t.reparent(surface, leash.getHandle());
312 return leash;
313 }
314
Vishnu Nair04ab4392018-01-10 11:00:06 -0800315 /**
316 * Write to a protocol buffer output stream. Protocol buffer message definition is at {@link
Yi Jin6c6e9ca2018-03-20 16:53:35 -0700317 * com.android.server.wm.SurfaceAnimatorProto}.
Vishnu Nair04ab4392018-01-10 11:00:06 -0800318 *
319 * @param proto Stream to write the SurfaceAnimator object to.
320 * @param fieldId Field Id of the SurfaceAnimator as defined in the parent message.
321 * @hide
322 */
323 void writeToProto(ProtoOutputStream proto, long fieldId) {
324 final long token = proto.start(fieldId);
Jorim Jaggif75d1612018-02-27 15:05:21 +0100325 if (mAnimation != null) {
326 mAnimation.writeToProto(proto, ANIMATION_ADAPTER);
327 }
Vishnu Nair04ab4392018-01-10 11:00:06 -0800328 if (mLeash != null){
329 mLeash.writeToProto(proto, LEASH);
330 }
331 proto.write(ANIMATION_START_DELAYED, mAnimationStartDelayed);
332 proto.end(token);
333 }
334
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200335 void dump(PrintWriter pw, String prefix) {
Jorim Jaggif75d1612018-02-27 15:05:21 +0100336 pw.print(prefix); pw.print("mLeash="); pw.print(mLeash);
337 if (mAnimationStartDelayed) {
338 pw.print(" mAnimationStartDelayed="); pw.println(mAnimationStartDelayed);
339 } else {
340 pw.println();
341 }
342 pw.print(prefix); pw.println("Animation:");
343 if (mAnimation != null) {
344 mAnimation.dump(pw, prefix + " ");
345 } else {
346 pw.print(prefix); pw.println("null");
347 }
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200348 }
349
350 /**
351 * Callback to be passed into {@link AnimationAdapter#startAnimation} to be invoked by the
352 * component that is running the animation when the animation is finished.
353 */
354 interface OnAnimationFinishedCallback {
355 void onAnimationFinished(AnimationAdapter anim);
356 }
357
358 /**
359 * Interface to be animated by {@link SurfaceAnimator}.
360 */
361 interface Animatable {
362
363 /**
364 * @return The pending transaction that will be committed in the next frame.
365 */
366 @NonNull Transaction getPendingTransaction();
367
368 /**
369 * Schedules a commit of the pending transaction.
370 */
371 void commitPendingTransaction();
372
373 /**
374 * Called when the was created.
375 *
376 * @param t The transaction to use to apply any necessary changes.
377 * @param leash The leash that was created.
378 */
Jorim Jaggia5e10572017-11-15 14:36:26 +0100379 void onAnimationLeashCreated(Transaction t, SurfaceControl leash);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200380
381 /**
382 * Called when the leash is being destroyed, and the surface was reparented back to the
383 * original parent.
384 *
385 * @param t The transaction to use to apply any necessary changes.
386 */
Jorim Jaggia5e10572017-11-15 14:36:26 +0100387 void onAnimationLeashDestroyed(Transaction t);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200388
389 /**
Jorim Jaggia5e10572017-11-15 14:36:26 +0100390 * @return A new surface to be used for the animation leash, inserted at the correct
391 * position in the hierarchy.
392 */
393 SurfaceControl.Builder makeAnimationLeash();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200394
395 /**
Jorim Jaggi596a1992017-12-29 14:48:02 +0100396 * @return The parent that should be used for the animation leash.
397 */
398 @Nullable SurfaceControl getAnimationLeashParent();
399
400 /**
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200401 * @return The surface of the object to be animated.
402 */
Jorim Jaggia5e10572017-11-15 14:36:26 +0100403 @Nullable SurfaceControl getSurfaceControl();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200404
405 /**
406 * @return The parent of the surface object to be animated.
407 */
Jorim Jaggia5e10572017-11-15 14:36:26 +0100408 @Nullable SurfaceControl getParentSurfaceControl();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200409
410 /**
411 * @return The width of the surface to be animated.
412 */
413 int getSurfaceWidth();
414
415 /**
416 * @return The height of the surface to be animated.
417 */
418 int getSurfaceHeight();
Jorim Jaggi6de61012018-03-19 14:53:23 +0100419
420 /**
421 * Gets called when the animation is about to finish and gives the client the opportunity to
422 * defer finishing the animation, i.e. it keeps the leash around until the client calls
423 * {@link #cancelAnimation}.
424 *
425 * @param endDeferFinishCallback The callback to call when defer finishing should be ended.
426 * @return Whether the client would like to defer the animation finish.
427 */
428 default boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
429 return false;
430 }
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200431 }
432}