Home | History | Annotate | Download | only in wm
      1 /*
      2  * Copyright (C) 2014 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 android.view.Display.INVALID_DISPLAY;
     20 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
     21 import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
     22 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
     23 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
     24 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
     25 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
     26 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
     27 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
     28 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
     29 
     30 import android.graphics.Matrix;
     31 import android.util.Slog;
     32 import android.util.TimeUtils;
     33 import android.view.Choreographer;
     34 import android.view.Display;
     35 import android.view.SurfaceControl;
     36 import android.view.animation.Animation;
     37 import android.view.animation.Transformation;
     38 
     39 import java.io.PrintWriter;
     40 import java.util.ArrayList;
     41 
     42 public class AppWindowAnimator {
     43     static final String TAG = TAG_WITH_CLASS_NAME ? "AppWindowAnimator" : TAG_WM;
     44 
     45     private static final int PROLONG_ANIMATION_DISABLED = 0;
     46     static final int PROLONG_ANIMATION_AT_END = 1;
     47     static final int PROLONG_ANIMATION_AT_START = 2;
     48 
     49     final AppWindowToken mAppToken;
     50     final WindowManagerService mService;
     51     final WindowAnimator mAnimator;
     52 
     53     boolean animating;
     54     boolean wasAnimating;
     55     Animation animation;
     56     boolean hasTransformation;
     57     final Transformation transformation = new Transformation();
     58 
     59     // Have we been asked to have this token keep the screen frozen?
     60     // Protect with mAnimator.
     61     boolean freezingScreen;
     62 
     63     /**
     64      * How long we last kept the screen frozen.
     65      */
     66     int lastFreezeDuration;
     67 
     68     // Offset to the window of all layers in the token, for use by
     69     // AppWindowToken animations.
     70     int animLayerAdjustment;
     71 
     72     // Propagated from AppWindowToken.allDrawn, to determine when
     73     // the state changes.
     74     boolean allDrawn;
     75 
     76     // Special surface for thumbnail animation.  If deferThumbnailDestruction is enabled, then we
     77     // will make sure that the thumbnail is destroyed after the other surface is completed.  This
     78     // requires that the duration of the two animations are the same.
     79     SurfaceControl thumbnail;
     80     int thumbnailTransactionSeq;
     81     // TODO(b/62029108): combine both members into a private one. Create a member function to set
     82     // the thumbnail layer to +1 to the highest layer position and replace all setter instances
     83     // with this function. Remove all unnecessary calls to both variables in other classes.
     84     int thumbnailLayer;
     85     int thumbnailForceAboveLayer;
     86     Animation thumbnailAnimation;
     87     final Transformation thumbnailTransformation = new Transformation();
     88     // This flag indicates that the destruction of the thumbnail surface is synchronized with
     89     // another animation, so defer the destruction of this thumbnail surface for a single frame
     90     // after the secondary animation completes.
     91     boolean deferThumbnailDestruction;
     92     // This flag is set if the animator has deferThumbnailDestruction set and has reached the final
     93     // frame of animation.  It will extend the animation by one frame and then clean up afterwards.
     94     boolean deferFinalFrameCleanup;
     95     // If true when the animation hits the last frame, it will keep running on that last frame.
     96     // This is used to synchronize animation with Recents and we wait for Recents to tell us to
     97     // finish or for a new animation be set as fail-safe mechanism.
     98     private int mProlongAnimation;
     99     // Whether the prolong animation can be removed when animation is set. The purpose of this is
    100     // that if recents doesn't tell us to remove the prolonged animation, we will get rid of it
    101     // when new animation is set.
    102     private boolean mClearProlongedAnimation;
    103     private int mTransit;
    104     private int mTransitFlags;
    105 
    106     /** WindowStateAnimator from mAppAnimator.allAppWindows as of last performLayout */
    107     ArrayList<WindowStateAnimator> mAllAppWinAnimators = new ArrayList<>();
    108 
    109     /** True if the current animation was transferred from another AppWindowAnimator.
    110      *  See {@link #transferCurrentAnimation}*/
    111     boolean usingTransferredAnimation = false;
    112 
    113     private boolean mSkipFirstFrame = false;
    114     private int mStackClip = STACK_CLIP_BEFORE_ANIM;
    115 
    116     static final Animation sDummyAnimation = new DummyAnimation();
    117 
    118     public AppWindowAnimator(final AppWindowToken atoken, WindowManagerService service) {
    119         mAppToken = atoken;
    120         mService = service;
    121         mAnimator = mService.mAnimator;
    122     }
    123 
    124     public void setAnimation(Animation anim, int width, int height, int parentWidth,
    125             int parentHeight, boolean skipFirstFrame, int stackClip, int transit,
    126             int transitFlags) {
    127         if (WindowManagerService.localLOGV) Slog.v(TAG, "Setting animation in " + mAppToken
    128                 + ": " + anim + " wxh=" + width + "x" + height
    129                 + " hasContentToDisplay=" + mAppToken.hasContentToDisplay());
    130         animation = anim;
    131         animating = false;
    132         if (!anim.isInitialized()) {
    133             anim.initialize(width, height, parentWidth, parentHeight);
    134         }
    135         anim.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION);
    136         anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked());
    137         int zorder = anim.getZAdjustment();
    138         int adj = 0;
    139         if (zorder == Animation.ZORDER_TOP) {
    140             adj = TYPE_LAYER_OFFSET;
    141         } else if (zorder == Animation.ZORDER_BOTTOM) {
    142             adj = -TYPE_LAYER_OFFSET;
    143         }
    144 
    145         if (animLayerAdjustment != adj) {
    146             animLayerAdjustment = adj;
    147             updateLayers();
    148         }
    149         // Start out animation gone if window is gone, or visible if window is visible.
    150         transformation.clear();
    151         transformation.setAlpha(mAppToken.isVisible() ? 1 : 0);
    152         hasTransformation = true;
    153         mStackClip = stackClip;
    154 
    155         mSkipFirstFrame = skipFirstFrame;
    156         mTransit = transit;
    157         mTransitFlags = transitFlags;
    158 
    159         if (!mAppToken.fillsParent()) {
    160             anim.setBackgroundColor(0);
    161         }
    162         if (mClearProlongedAnimation) {
    163             mProlongAnimation = PROLONG_ANIMATION_DISABLED;
    164         } else {
    165             mClearProlongedAnimation = true;
    166         }
    167     }
    168 
    169     public void setDummyAnimation() {
    170         if (WindowManagerService.localLOGV) Slog.v(TAG, "Setting dummy animation in " + mAppToken
    171                 + " hasContentToDisplay=" + mAppToken.hasContentToDisplay());
    172         animation = sDummyAnimation;
    173         hasTransformation = true;
    174         transformation.clear();
    175         transformation.setAlpha(mAppToken.isVisible() ? 1 : 0);
    176     }
    177 
    178     void setNullAnimation() {
    179         animation = null;
    180         usingTransferredAnimation = false;
    181     }
    182 
    183     public void clearAnimation() {
    184         if (animation != null) {
    185             animating = true;
    186         }
    187         clearThumbnail();
    188         setNullAnimation();
    189         if (mAppToken.deferClearAllDrawn) {
    190             mAppToken.clearAllDrawn();
    191         }
    192         mStackClip = STACK_CLIP_BEFORE_ANIM;
    193         mTransit = TRANSIT_UNSET;
    194         mTransitFlags = 0;
    195     }
    196 
    197     public boolean isAnimating() {
    198         return animation != null || mAppToken.inPendingTransaction;
    199     }
    200 
    201     /**
    202      * @return whether an animation is about to start, i.e. the animation is set already but we
    203      *         haven't processed the first frame yet.
    204      */
    205     boolean isAnimationStarting() {
    206         return animation != null && !animating;
    207     }
    208 
    209     public int getTransit() {
    210         return mTransit;
    211     }
    212 
    213     int getTransitFlags() {
    214         return mTransitFlags;
    215     }
    216 
    217     public void clearThumbnail() {
    218         if (thumbnail != null) {
    219             thumbnail.hide();
    220             mService.mWindowPlacerLocked.destroyAfterTransaction(thumbnail);
    221             thumbnail = null;
    222         }
    223         deferThumbnailDestruction = false;
    224     }
    225 
    226     int getStackClip() {
    227         return mStackClip;
    228     }
    229 
    230     void transferCurrentAnimation(
    231             AppWindowAnimator toAppAnimator, WindowStateAnimator transferWinAnimator) {
    232 
    233         if (animation != null) {
    234             toAppAnimator.animation = animation;
    235             toAppAnimator.animating = animating;
    236             toAppAnimator.animLayerAdjustment = animLayerAdjustment;
    237             setNullAnimation();
    238             animLayerAdjustment = 0;
    239             toAppAnimator.updateLayers();
    240             updateLayers();
    241             toAppAnimator.usingTransferredAnimation = true;
    242             toAppAnimator.mTransit = mTransit;
    243         }
    244         if (transferWinAnimator != null) {
    245             mAllAppWinAnimators.remove(transferWinAnimator);
    246             toAppAnimator.mAllAppWinAnimators.add(transferWinAnimator);
    247             toAppAnimator.hasTransformation = transferWinAnimator.mAppAnimator.hasTransformation;
    248             if (toAppAnimator.hasTransformation) {
    249                 toAppAnimator.transformation.set(transferWinAnimator.mAppAnimator.transformation);
    250             } else {
    251                 toAppAnimator.transformation.clear();
    252             }
    253             transferWinAnimator.mAppAnimator = toAppAnimator;
    254         }
    255     }
    256 
    257     private void updateLayers() {
    258         mAppToken.getDisplayContent().assignWindowLayers(false /* relayoutNeeded */);
    259         thumbnailLayer = mAppToken.getHighestAnimLayer();
    260     }
    261 
    262     private void stepThumbnailAnimation(long currentTime) {
    263         thumbnailTransformation.clear();
    264         final long animationFrameTime = getAnimationFrameTime(thumbnailAnimation, currentTime);
    265         thumbnailAnimation.getTransformation(animationFrameTime, thumbnailTransformation);
    266 
    267         ScreenRotationAnimation screenRotationAnimation =
    268                 mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY);
    269         final boolean screenAnimation = screenRotationAnimation != null
    270                 && screenRotationAnimation.isAnimating();
    271         if (screenAnimation) {
    272             thumbnailTransformation.postCompose(screenRotationAnimation.getEnterTransformation());
    273         }
    274         // cache often used attributes locally
    275         final float tmpFloats[] = mService.mTmpFloats;
    276         thumbnailTransformation.getMatrix().getValues(tmpFloats);
    277         if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
    278                 "thumbnail", "POS " + tmpFloats[Matrix.MTRANS_X]
    279                 + ", " + tmpFloats[Matrix.MTRANS_Y]);
    280         thumbnail.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]);
    281         if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
    282                 "thumbnail", "alpha=" + thumbnailTransformation.getAlpha()
    283                 + " layer=" + thumbnailLayer
    284                 + " matrix=[" + tmpFloats[Matrix.MSCALE_X]
    285                 + "," + tmpFloats[Matrix.MSKEW_Y]
    286                 + "][" + tmpFloats[Matrix.MSKEW_X]
    287                 + "," + tmpFloats[Matrix.MSCALE_Y] + "]");
    288         thumbnail.setAlpha(thumbnailTransformation.getAlpha());
    289         if (thumbnailForceAboveLayer > 0) {
    290             thumbnail.setLayer(thumbnailForceAboveLayer + 1);
    291         } else {
    292             // The thumbnail is layered below the window immediately above this
    293             // token's anim layer.
    294             thumbnail.setLayer(thumbnailLayer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
    295                     - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
    296         }
    297         thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
    298                 tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
    299         thumbnail.setWindowCrop(thumbnailTransformation.getClipRect());
    300     }
    301 
    302     /**
    303      * Sometimes we need to synchronize the first frame of animation with some external event, e.g.
    304      * Recents hiding some of its content. To achieve this, we prolong the start of the animaiton
    305      * and keep producing the first frame of the animation.
    306      */
    307     private long getAnimationFrameTime(Animation animation, long currentTime) {
    308         if (mProlongAnimation == PROLONG_ANIMATION_AT_START) {
    309             animation.setStartTime(currentTime);
    310             return currentTime + 1;
    311         }
    312         return currentTime;
    313     }
    314 
    315     private boolean stepAnimation(long currentTime) {
    316         if (animation == null) {
    317             return false;
    318         }
    319         transformation.clear();
    320         final long animationFrameTime = getAnimationFrameTime(animation, currentTime);
    321         boolean hasMoreFrames = animation.getTransformation(animationFrameTime, transformation);
    322         if (!hasMoreFrames) {
    323             if (deferThumbnailDestruction && !deferFinalFrameCleanup) {
    324                 // We are deferring the thumbnail destruction, so extend the animation for one more
    325                 // (dummy) frame before we clean up
    326                 deferFinalFrameCleanup = true;
    327                 hasMoreFrames = true;
    328             } else {
    329                 if (false && DEBUG_ANIM) Slog.v(TAG,
    330                         "Stepped animation in " + mAppToken + ": more=" + hasMoreFrames +
    331                         ", xform=" + transformation + ", mProlongAnimation=" + mProlongAnimation);
    332                 deferFinalFrameCleanup = false;
    333                 if (mProlongAnimation == PROLONG_ANIMATION_AT_END) {
    334                     hasMoreFrames = true;
    335                 } else {
    336                     setNullAnimation();
    337                     clearThumbnail();
    338                     if (DEBUG_ANIM) Slog.v(TAG, "Finished animation in " + mAppToken + " @ "
    339                             + currentTime);
    340                 }
    341             }
    342         }
    343         hasTransformation = hasMoreFrames;
    344         return hasMoreFrames;
    345     }
    346 
    347     private long getStartTimeCorrection() {
    348         if (mSkipFirstFrame) {
    349 
    350             // If the transition is an animation in which the first frame doesn't change the screen
    351             // contents at all, we can just skip it and start at the second frame. So we shift the
    352             // start time of the animation forward by minus the frame duration.
    353             return -Choreographer.getInstance().getFrameIntervalNanos() / TimeUtils.NANOS_PER_MS;
    354         } else {
    355             return 0;
    356         }
    357     }
    358 
    359     // This must be called while inside a transaction.
    360     boolean stepAnimationLocked(long currentTime) {
    361         if (mAppToken.okToAnimate()) {
    362             // We will run animations as long as the display isn't frozen.
    363 
    364             if (animation == sDummyAnimation) {
    365                 // This guy is going to animate, but not yet.  For now count
    366                 // it as not animating for purposes of scheduling transactions;
    367                 // when it is really time to animate, this will be set to
    368                 // a real animation and the next call will execute normally.
    369                 return false;
    370             }
    371 
    372             if ((mAppToken.allDrawn || animating || mAppToken.startingDisplayed)
    373                     && animation != null) {
    374                 if (!animating) {
    375                     if (DEBUG_ANIM) Slog.v(TAG,
    376                         "Starting animation in " + mAppToken +
    377                         " @ " + currentTime + " scale="
    378                         + mService.getTransitionAnimationScaleLocked()
    379                         + " allDrawn=" + mAppToken.allDrawn + " animating=" + animating);
    380                     long correction = getStartTimeCorrection();
    381                     animation.setStartTime(currentTime + correction);
    382                     animating = true;
    383                     if (thumbnail != null) {
    384                         thumbnail.show();
    385                         thumbnailAnimation.setStartTime(currentTime + correction);
    386                     }
    387                     mSkipFirstFrame = false;
    388                 }
    389                 if (stepAnimation(currentTime)) {
    390                     // animation isn't over, step any thumbnail and that's
    391                     // it for now.
    392                     if (thumbnail != null) {
    393                         stepThumbnailAnimation(currentTime);
    394                     }
    395                     return true;
    396                 }
    397             }
    398         } else if (animation != null) {
    399             // If the display is frozen, and there is a pending animation,
    400             // clear it and make sure we run the cleanup code.
    401             animating = true;
    402             animation = null;
    403         }
    404 
    405         hasTransformation = false;
    406 
    407         if (!animating && animation == null) {
    408             return false;
    409         }
    410 
    411         mAppToken.setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM, "AppWindowToken");
    412 
    413         clearAnimation();
    414         animating = false;
    415         if (animLayerAdjustment != 0) {
    416             animLayerAdjustment = 0;
    417             updateLayers();
    418         }
    419         if (mService.mInputMethodTarget != null
    420                 && mService.mInputMethodTarget.mAppToken == mAppToken) {
    421             mAppToken.getDisplayContent().computeImeTarget(true /* updateImeTarget */);
    422         }
    423 
    424         if (DEBUG_ANIM) Slog.v(TAG, "Animation done in " + mAppToken
    425                 + ": reportedVisible=" + mAppToken.reportedVisible
    426                 + " okToDisplay=" + mAppToken.okToDisplay()
    427                 + " okToAnimate=" + mAppToken.okToAnimate()
    428                 + " startingDisplayed=" + mAppToken.startingDisplayed);
    429 
    430         transformation.clear();
    431 
    432         final int numAllAppWinAnimators = mAllAppWinAnimators.size();
    433         for (int i = 0; i < numAllAppWinAnimators; i++) {
    434             mAllAppWinAnimators.get(i).mWin.onExitAnimationDone();
    435         }
    436         mService.mAppTransition.notifyAppTransitionFinishedLocked(mAppToken.token);
    437         return false;
    438     }
    439 
    440     // This must be called while inside a transaction.
    441     boolean showAllWindowsLocked() {
    442         boolean isAnimating = false;
    443         final int NW = mAllAppWinAnimators.size();
    444         for (int i=0; i<NW; i++) {
    445             WindowStateAnimator winAnimator = mAllAppWinAnimators.get(i);
    446             if (DEBUG_VISIBILITY) Slog.v(TAG, "performing show on: " + winAnimator);
    447             winAnimator.mWin.performShowLocked();
    448             isAnimating |= winAnimator.isAnimationSet();
    449         }
    450         return isAnimating;
    451     }
    452 
    453     void dump(PrintWriter pw, String prefix) {
    454         pw.print(prefix); pw.print("mAppToken="); pw.println(mAppToken);
    455         pw.print(prefix); pw.print("mAnimator="); pw.println(mAnimator);
    456         pw.print(prefix); pw.print("freezingScreen="); pw.print(freezingScreen);
    457                 pw.print(" allDrawn="); pw.print(allDrawn);
    458                 pw.print(" animLayerAdjustment="); pw.println(animLayerAdjustment);
    459         if (lastFreezeDuration != 0) {
    460             pw.print(prefix); pw.print("lastFreezeDuration=");
    461                     TimeUtils.formatDuration(lastFreezeDuration, pw); pw.println();
    462         }
    463         if (animating || animation != null) {
    464             pw.print(prefix); pw.print("animating="); pw.println(animating);
    465             pw.print(prefix); pw.print("animation="); pw.println(animation);
    466             pw.print(prefix); pw.print("mTransit="); pw.println(mTransit);
    467             pw.print(prefix); pw.print("mTransitFlags="); pw.println(mTransitFlags);
    468         }
    469         if (hasTransformation) {
    470             pw.print(prefix); pw.print("XForm: ");
    471                     transformation.printShortString(pw);
    472                     pw.println();
    473         }
    474         if (thumbnail != null) {
    475             pw.print(prefix); pw.print("thumbnail="); pw.print(thumbnail);
    476                     pw.print(" layer="); pw.println(thumbnailLayer);
    477             pw.print(prefix); pw.print("thumbnailAnimation="); pw.println(thumbnailAnimation);
    478             pw.print(prefix); pw.print("thumbnailTransformation=");
    479                     pw.println(thumbnailTransformation.toShortString());
    480         }
    481         for (int i=0; i<mAllAppWinAnimators.size(); i++) {
    482             WindowStateAnimator wanim = mAllAppWinAnimators.get(i);
    483             pw.print(prefix); pw.print("App Win Anim #"); pw.print(i);
    484                     pw.print(": "); pw.println(wanim);
    485         }
    486     }
    487 
    488     void startProlongAnimation(int prolongType) {
    489         mProlongAnimation = prolongType;
    490         mClearProlongedAnimation = false;
    491     }
    492 
    493     void endProlongedAnimation() {
    494         mProlongAnimation = PROLONG_ANIMATION_DISABLED;
    495     }
    496 
    497     // This is an animation that does nothing: it just immediately finishes
    498     // itself every time it is called.  It is used as a stub animation in cases
    499     // where we want to synchronize multiple things that may be animating.
    500     static final class DummyAnimation extends Animation {
    501         @Override
    502         public boolean getTransformation(long currentTime, Transformation outTransformation) {
    503             return false;
    504         }
    505     }
    506 
    507 }
    508