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