Home | History | Annotate | Download | only in wm
      1 /*
      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 
     17 package com.android.server.wm;
     18 
     19 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
     20 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
     21 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
     22 import static com.android.server.wm.SurfaceAnimatorProto.ANIMATION_ADAPTER;
     23 import static com.android.server.wm.SurfaceAnimatorProto.ANIMATION_START_DELAYED;
     24 import static com.android.server.wm.SurfaceAnimatorProto.LEASH;
     25 
     26 import android.annotation.NonNull;
     27 import android.annotation.Nullable;
     28 import android.util.Slog;
     29 import android.util.proto.ProtoOutputStream;
     30 import android.view.SurfaceControl;
     31 import android.view.SurfaceControl.Transaction;
     32 
     33 import com.android.internal.annotations.VisibleForTesting;
     34 
     35 import 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  */
     45 class SurfaceAnimator {
     46 
     47     private static final String TAG = TAG_WITH_CLASS_NAME ? "SurfaceAnimator" : TAG_WM;
     48     private final WindowManagerService mService;
     49     private AnimationAdapter mAnimation;
     50 
     51     @VisibleForTesting
     52     SurfaceControl mLeash;
     53     private final Animatable mAnimatable;
     54     private final OnAnimationFinishedCallback mInnerAnimationFinishedCallback;
     55     @VisibleForTesting
     56     final Runnable mAnimationFinishedCallback;
     57     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      */
     63     SurfaceAnimator(Animatable animatable, @Nullable Runnable animationFinishedCallback,
     64             WindowManagerService service) {
     65         mAnimatable = animatable;
     66         mService = service;
     67         mAnimationFinishedCallback = animationFinishedCallback;
     68         mInnerAnimationFinishedCallback = getFinishedCallback(animationFinishedCallback);
     69     }
     70 
     71     private OnAnimationFinishedCallback getFinishedCallback(
     72             @Nullable Runnable animationFinishedCallback) {
     73         return anim -> {
     74             synchronized (mService.mWindowMap) {
     75                 final SurfaceAnimator target = mService.mAnimationTransferMap.remove(anim);
     76                 if (target != null) {
     77                     target.mInnerAnimationFinishedCallback.onAnimationFinished(anim);
     78                     return;
     79                 }
     80 
     81                 if (anim != mAnimation) {
     82                     return;
     83                 }
     84                 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();
     92                 }
     93             }
     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) {
    109         cancelAnimation(t, true /* restarting */, true /* forwardCancel */);
    110         mAnimation = anim;
    111         final SurfaceControl surface = mAnimatable.getSurfaceControl();
    112         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);
    119         mAnimatable.onAnimationLeashCreated(t, mLeash);
    120         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() {
    172         cancelAnimation(mAnimatable.getPendingTransaction(), false /* restarting */,
    173                 true /* forwardCancel */);
    174         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) {
    184         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);
    194     }
    195 
    196     /**
    197      * Reparents the surface.
    198      *
    199      * @see #setLayer
    200      */
    201     void reparent(Transaction t, SurfaceControl newParent) {
    202         t.reparent(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), newParent.getHandle());
    203     }
    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 
    212     void transferAnimation(SurfaceAnimator from) {
    213         if (from.mLeash == null) {
    214             return;
    215         }
    216         final SurfaceControl surface = mAnimatable.getSurfaceControl();
    217         final SurfaceControl parent = mAnimatable.getAnimationLeashParent();
    218         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 
    237     boolean isAnimationStartDelayed() {
    238         return mAnimationStartDelayed;
    239     }
    240 
    241     /**
    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) {
    251         if (DEBUG_ANIM) Slog.i(TAG, "Cancelling animation restarting=" + restarting);
    252         final SurfaceControl leash = mLeash;
    253         final AnimationAdapter animation = mAnimation;
    254         reset(t, forwardCancel);
    255         if (animation != null) {
    256             if (!mAnimationStartDelayed && forwardCancel) {
    257                 animation.onAnimationCancelled(leash);
    258             }
    259             if (!restarting) {
    260                 mAnimationFinishedCallback.run();
    261             }
    262         }
    263         if (!restarting) {
    264             mAnimationStartDelayed = false;
    265         }
    266     }
    267 
    268     private void reset(Transaction t, boolean destroyLeash) {
    269         final SurfaceControl surface = mAnimatable.getSurfaceControl();
    270         final SurfaceControl parent = mAnimatable.getParentSurfaceControl();
    271 
    272         boolean scheduleAnim = false;
    273 
    274         // 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());
    279             scheduleAnim = true;
    280         }
    281         mService.mAnimationTransferMap.remove(mAnimation);
    282         if (mLeash != null && destroyLeash) {
    283             t.destroy(mLeash);
    284             scheduleAnim = true;
    285         }
    286         mLeash = null;
    287         mAnimation = null;
    288 
    289         // Make sure to inform the animatable after the leash was destroyed.
    290         if (destroy) {
    291             mAnimatable.onAnimationLeashDestroyed(t);
    292             scheduleAnim = true;
    293         }
    294 
    295         if (scheduleAnim) {
    296             mService.scheduleAnimationLocked();
    297         }
    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");
    303         final SurfaceControl.Builder builder = mAnimatable.makeAnimationLeash()
    304                 .setParent(mAnimatable.getAnimationLeashParent())
    305                 .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 
    315     /**
    316      * Write to a protocol buffer output stream. Protocol buffer message definition is at {@link
    317      * com.android.server.wm.SurfaceAnimatorProto}.
    318      *
    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);
    325         if (mAnimation != null) {
    326             mAnimation.writeToProto(proto, ANIMATION_ADAPTER);
    327         }
    328         if (mLeash != null){
    329             mLeash.writeToProto(proto, LEASH);
    330         }
    331         proto.write(ANIMATION_START_DELAYED, mAnimationStartDelayed);
    332         proto.end(token);
    333     }
    334 
    335     void dump(PrintWriter pw, String prefix) {
    336         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         }
    348     }
    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          */
    379         void onAnimationLeashCreated(Transaction t, SurfaceControl leash);
    380 
    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          */
    387         void onAnimationLeashDestroyed(Transaction t);
    388 
    389         /**
    390          * @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();
    394 
    395         /**
    396          * @return The parent that should be used for the animation leash.
    397          */
    398         @Nullable SurfaceControl getAnimationLeashParent();
    399 
    400         /**
    401          * @return The surface of the object to be animated.
    402          */
    403         @Nullable SurfaceControl getSurfaceControl();
    404 
    405         /**
    406          * @return The parent of the surface object to be animated.
    407          */
    408         @Nullable SurfaceControl getParentSurfaceControl();
    409 
    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();
    419 
    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         }
    431     }
    432 }
    433