blob: 2b78d7bccca0a0955d9e5959211ab9535318061a [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 -> {
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();
Vishnu Naird454442d2018-11-13 13:51:01 -0800308 t.setWindowCrop(surface, width, height);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200309 if (!hidden) {
310 t.show(leash);
311 }
312 t.reparent(surface, leash.getHandle());
313 return leash;
314 }
315
Vishnu Nair04ab4392018-01-10 11:00:06 -0800316 /**
317 * Write to a protocol buffer output stream. Protocol buffer message definition is at {@link
Yi Jin6c6e9ca2018-03-20 16:53:35 -0700318 * com.android.server.wm.SurfaceAnimatorProto}.
Vishnu Nair04ab4392018-01-10 11:00:06 -0800319 *
320 * @param proto Stream to write the SurfaceAnimator object to.
321 * @param fieldId Field Id of the SurfaceAnimator as defined in the parent message.
322 * @hide
323 */
324 void writeToProto(ProtoOutputStream proto, long fieldId) {
325 final long token = proto.start(fieldId);
Jorim Jaggif75d1612018-02-27 15:05:21 +0100326 if (mAnimation != null) {
327 mAnimation.writeToProto(proto, ANIMATION_ADAPTER);
328 }
Vishnu Nair04ab4392018-01-10 11:00:06 -0800329 if (mLeash != null){
330 mLeash.writeToProto(proto, LEASH);
331 }
332 proto.write(ANIMATION_START_DELAYED, mAnimationStartDelayed);
333 proto.end(token);
334 }
335
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200336 void dump(PrintWriter pw, String prefix) {
Jorim Jaggif75d1612018-02-27 15:05:21 +0100337 pw.print(prefix); pw.print("mLeash="); pw.print(mLeash);
338 if (mAnimationStartDelayed) {
339 pw.print(" mAnimationStartDelayed="); pw.println(mAnimationStartDelayed);
340 } else {
341 pw.println();
342 }
343 pw.print(prefix); pw.println("Animation:");
344 if (mAnimation != null) {
345 mAnimation.dump(pw, prefix + " ");
346 } else {
347 pw.print(prefix); pw.println("null");
348 }
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200349 }
350
351 /**
352 * Callback to be passed into {@link AnimationAdapter#startAnimation} to be invoked by the
353 * component that is running the animation when the animation is finished.
354 */
355 interface OnAnimationFinishedCallback {
356 void onAnimationFinished(AnimationAdapter anim);
357 }
358
359 /**
360 * Interface to be animated by {@link SurfaceAnimator}.
361 */
362 interface Animatable {
363
364 /**
365 * @return The pending transaction that will be committed in the next frame.
366 */
367 @NonNull Transaction getPendingTransaction();
368
369 /**
370 * Schedules a commit of the pending transaction.
371 */
372 void commitPendingTransaction();
373
374 /**
375 * Called when the was created.
376 *
377 * @param t The transaction to use to apply any necessary changes.
378 * @param leash The leash that was created.
379 */
Jorim Jaggia5e10572017-11-15 14:36:26 +0100380 void onAnimationLeashCreated(Transaction t, SurfaceControl leash);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200381
382 /**
383 * Called when the leash is being destroyed, and the surface was reparented back to the
384 * original parent.
385 *
386 * @param t The transaction to use to apply any necessary changes.
387 */
Jorim Jaggia5e10572017-11-15 14:36:26 +0100388 void onAnimationLeashDestroyed(Transaction t);
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200389
390 /**
Jorim Jaggia5e10572017-11-15 14:36:26 +0100391 * @return A new surface to be used for the animation leash, inserted at the correct
392 * position in the hierarchy.
393 */
394 SurfaceControl.Builder makeAnimationLeash();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200395
396 /**
Jorim Jaggi596a1992017-12-29 14:48:02 +0100397 * @return The parent that should be used for the animation leash.
398 */
399 @Nullable SurfaceControl getAnimationLeashParent();
400
401 /**
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200402 * @return The surface of the object to be animated.
403 */
Jorim Jaggia5e10572017-11-15 14:36:26 +0100404 @Nullable SurfaceControl getSurfaceControl();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200405
406 /**
407 * @return The parent of the surface object to be animated.
408 */
Jorim Jaggia5e10572017-11-15 14:36:26 +0100409 @Nullable SurfaceControl getParentSurfaceControl();
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200410
411 /**
412 * @return The width of the surface to be animated.
413 */
414 int getSurfaceWidth();
415
416 /**
417 * @return The height of the surface to be animated.
418 */
419 int getSurfaceHeight();
Jorim Jaggi6de61012018-03-19 14:53:23 +0100420
421 /**
422 * Gets called when the animation is about to finish and gives the client the opportunity to
423 * defer finishing the animation, i.e. it keeps the leash around until the client calls
424 * {@link #cancelAnimation}.
425 *
426 * @param endDeferFinishCallback The callback to call when defer finishing should be ended.
427 * @return Whether the client would like to defer the animation finish.
428 */
429 default boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
430 return false;
431 }
Jorim Jaggi21c39a72017-10-20 15:47:51 +0200432 }
433}