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