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