blob: 35b8641371be31fef75d532f57dcf4385e8b8c04 [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
Yi Jin6c6e9ca2018-03-20 16:53:35 -070019import static com.android.server.wm.SurfaceAnimatorProto.ANIMATION_ADAPTER;
20import static com.android.server.wm.SurfaceAnimatorProto.ANIMATION_START_DELAYED;
21import static com.android.server.wm.SurfaceAnimatorProto.LEASH;
Vishnu Naird454442d2018-11-13 13:51:01 -080022import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
23import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
24import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
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 -> {
Wale Ogunwaledb485de2018-10-29 09:47:07 -070074 synchronized (mService.mGlobalLock) {
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) {
Robert Carr10584fa2019-01-14 15:55:19 -0800202 t.reparent(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), newParent);
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 */);
Robert Carr10584fa2019-01-14 15:55:19 -0800231 t.reparent(surface, mLeash);
232 t.reparent(mLeash, parent);
Jorim Jaggi980c9de2017-11-17 01:41:37 +0100233 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;
Robert Carr4a2d3742019-02-11 12:15:38 -0800254 reset(t, false);
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 }
Robert Carr4a2d3742019-02-11 12:15:38 -0800263
264 if (forwardCancel && leash != null) {
265 t.remove(leash);
266 mService.scheduleAnimationLocked();
267 }
268
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200269 if (!restarting) {
270 mAnimationStartDelayed = false;
271 }
272 }
273
Jorim Jaggi980c9de2017-11-17 01:41:37 +0100274 private void reset(Transaction t, boolean destroyLeash) {
Jorim Jaggia5e10572017-11-15 14:36:26 +0100275 final SurfaceControl surface = mAnimatable.getSurfaceControl();
276 final SurfaceControl parent = mAnimatable.getParentSurfaceControl();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200277
Chavi Weingartenb736e322018-02-23 00:27:54 +0000278 boolean scheduleAnim = false;
279
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200280 // If the surface was destroyed, we don't care to reparent it back.
281 final boolean destroy = mLeash != null && surface != null && parent != null;
282 if (destroy) {
283 if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to original parent");
Robert Carr10584fa2019-01-14 15:55:19 -0800284 t.reparent(surface, parent);
Chavi Weingartenb736e322018-02-23 00:27:54 +0000285 scheduleAnim = true;
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200286 }
Jorim Jaggi980c9de2017-11-17 01:41:37 +0100287 mService.mAnimationTransferMap.remove(mAnimation);
288 if (mLeash != null && destroyLeash) {
Robert Carr71200f22019-02-05 09:44:53 -0800289 t.remove(mLeash);
Chavi Weingartenb736e322018-02-23 00:27:54 +0000290 scheduleAnim = true;
Jorim Jaggia5e10572017-11-15 14:36:26 +0100291 }
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200292 mLeash = null;
293 mAnimation = null;
294
295 // Make sure to inform the animatable after the leash was destroyed.
296 if (destroy) {
Jorim Jaggia5e10572017-11-15 14:36:26 +0100297 mAnimatable.onAnimationLeashDestroyed(t);
Chavi Weingartenb736e322018-02-23 00:27:54 +0000298 scheduleAnim = true;
299 }
300
301 if (scheduleAnim) {
302 mService.scheduleAnimationLocked();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200303 }
304 }
305
306 private SurfaceControl createAnimationLeash(SurfaceControl surface, Transaction t, int width,
307 int height, boolean hidden) {
308 if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash");
Jorim Jaggia5e10572017-11-15 14:36:26 +0100309 final SurfaceControl.Builder builder = mAnimatable.makeAnimationLeash()
Jorim Jaggi596a1992017-12-29 14:48:02 +0100310 .setParent(mAnimatable.getAnimationLeashParent())
Vishnu Naire86bd982018-11-28 13:23:17 -0800311 .setName(surface + " - animation-leash");
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200312 final SurfaceControl leash = builder.build();
Louis Changb92aa022018-11-16 15:46:50 +0800313 t.setWindowCrop(leash, width, height);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200314 if (!hidden) {
315 t.show(leash);
316 }
Robert Carr10584fa2019-01-14 15:55:19 -0800317 t.reparent(surface, leash);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200318 return leash;
319 }
320
Vishnu Nair04ab4392018-01-10 11:00:06 -0800321 /**
322 * Write to a protocol buffer output stream. Protocol buffer message definition is at {@link
Yi Jin6c6e9ca2018-03-20 16:53:35 -0700323 * com.android.server.wm.SurfaceAnimatorProto}.
Vishnu Nair04ab4392018-01-10 11:00:06 -0800324 *
325 * @param proto Stream to write the SurfaceAnimator object to.
326 * @param fieldId Field Id of the SurfaceAnimator as defined in the parent message.
327 * @hide
328 */
329 void writeToProto(ProtoOutputStream proto, long fieldId) {
330 final long token = proto.start(fieldId);
Jorim Jaggif75d1612018-02-27 15:05:21 +0100331 if (mAnimation != null) {
332 mAnimation.writeToProto(proto, ANIMATION_ADAPTER);
333 }
Nataniel Borges023ecb52019-01-16 14:15:43 -0800334 if (mLeash != null) {
Vishnu Nair04ab4392018-01-10 11:00:06 -0800335 mLeash.writeToProto(proto, LEASH);
336 }
337 proto.write(ANIMATION_START_DELAYED, mAnimationStartDelayed);
338 proto.end(token);
339 }
340
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200341 void dump(PrintWriter pw, String prefix) {
Jorim Jaggif75d1612018-02-27 15:05:21 +0100342 pw.print(prefix); pw.print("mLeash="); pw.print(mLeash);
343 if (mAnimationStartDelayed) {
344 pw.print(" mAnimationStartDelayed="); pw.println(mAnimationStartDelayed);
345 } else {
346 pw.println();
347 }
348 pw.print(prefix); pw.println("Animation:");
349 if (mAnimation != null) {
350 mAnimation.dump(pw, prefix + " ");
351 } else {
352 pw.print(prefix); pw.println("null");
353 }
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200354 }
355
356 /**
357 * Callback to be passed into {@link AnimationAdapter#startAnimation} to be invoked by the
358 * component that is running the animation when the animation is finished.
359 */
360 interface OnAnimationFinishedCallback {
361 void onAnimationFinished(AnimationAdapter anim);
362 }
363
364 /**
365 * Interface to be animated by {@link SurfaceAnimator}.
366 */
367 interface Animatable {
368
369 /**
370 * @return The pending transaction that will be committed in the next frame.
371 */
372 @NonNull Transaction getPendingTransaction();
373
374 /**
375 * Schedules a commit of the pending transaction.
376 */
377 void commitPendingTransaction();
378
379 /**
380 * Called when the was created.
381 *
382 * @param t The transaction to use to apply any necessary changes.
383 * @param leash The leash that was created.
384 */
Jorim Jaggia5e10572017-11-15 14:36:26 +0100385 void onAnimationLeashCreated(Transaction t, SurfaceControl leash);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200386
387 /**
388 * Called when the leash is being destroyed, and the surface was reparented back to the
389 * original parent.
390 *
391 * @param t The transaction to use to apply any necessary changes.
392 */
Jorim Jaggia5e10572017-11-15 14:36:26 +0100393 void onAnimationLeashDestroyed(Transaction t);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200394
395 /**
Jorim Jaggia5e10572017-11-15 14:36:26 +0100396 * @return A new surface to be used for the animation leash, inserted at the correct
397 * position in the hierarchy.
398 */
399 SurfaceControl.Builder makeAnimationLeash();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200400
401 /**
Jorim Jaggi596a1992017-12-29 14:48:02 +0100402 * @return The parent that should be used for the animation leash.
403 */
404 @Nullable SurfaceControl getAnimationLeashParent();
405
406 /**
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200407 * @return The surface of the object to be animated.
408 */
Jorim Jaggia5e10572017-11-15 14:36:26 +0100409 @Nullable SurfaceControl getSurfaceControl();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200410
411 /**
412 * @return The parent of the surface object to be animated.
413 */
Jorim Jaggia5e10572017-11-15 14:36:26 +0100414 @Nullable SurfaceControl getParentSurfaceControl();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200415
416 /**
417 * @return The width of the surface to be animated.
418 */
419 int getSurfaceWidth();
420
421 /**
422 * @return The height of the surface to be animated.
423 */
424 int getSurfaceHeight();
Jorim Jaggi6de61012018-03-19 14:53:23 +0100425
426 /**
427 * Gets called when the animation is about to finish and gives the client the opportunity to
428 * defer finishing the animation, i.e. it keeps the leash around until the client calls
429 * {@link #cancelAnimation}.
430 *
431 * @param endDeferFinishCallback The callback to call when defer finishing should be ended.
432 * @return Whether the client would like to defer the animation finish.
433 */
434 default boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
435 return false;
436 }
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200437 }
438}