AppWindowAnimator.java revision 8e0626572d44413e77ab73bd859b9d76c16c0d6b
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;
26import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
27
28import android.graphics.Matrix;
29import android.util.Slog;
30import android.util.TimeUtils;
31import android.view.Choreographer;
32import android.view.Display;
33import android.view.SurfaceControl;
34import android.view.WindowManagerPolicy;
35import android.view.animation.Animation;
36import android.view.animation.Transformation;
37
38import java.io.PrintWriter;
39import java.util.ArrayList;
40
41public 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.allDrawn = false;
189            mAppToken.deferClearAllDrawn = false;
190        }
191        mStackClip = STACK_CLIP_BEFORE_ANIM;
192    }
193
194    public boolean isAnimating() {
195        return animation != null || mAppToken.inPendingTransaction;
196    }
197
198    public void clearThumbnail() {
199        if (thumbnail != null) {
200            thumbnail.hide();
201            mService.mWindowPlacerLocked.destroyAfterTransaction(thumbnail);
202            thumbnail = null;
203        }
204        deferThumbnailDestruction = false;
205    }
206
207    int getStackClip() {
208        return mStackClip;
209    }
210
211    void transferCurrentAnimation(
212            AppWindowAnimator toAppAnimator, WindowStateAnimator transferWinAnimator) {
213
214        if (animation != null) {
215            toAppAnimator.animation = animation;
216            toAppAnimator.animating = animating;
217            toAppAnimator.animLayerAdjustment = animLayerAdjustment;
218            setNullAnimation();
219            animLayerAdjustment = 0;
220            toAppAnimator.updateLayers();
221            updateLayers();
222            toAppAnimator.usingTransferredAnimation = true;
223        }
224        if (transferWinAnimator != null) {
225            mAllAppWinAnimators.remove(transferWinAnimator);
226            toAppAnimator.mAllAppWinAnimators.add(transferWinAnimator);
227            toAppAnimator.hasTransformation = transferWinAnimator.mAppAnimator.hasTransformation;
228            if (toAppAnimator.hasTransformation) {
229                toAppAnimator.transformation.set(transferWinAnimator.mAppAnimator.transformation);
230            } else {
231                toAppAnimator.transformation.clear();
232            }
233            transferWinAnimator.mAppAnimator = toAppAnimator;
234        }
235    }
236
237    void updateLayers() {
238        final int windowCount = mAppToken.allAppWindows.size();
239        final int adj = animLayerAdjustment;
240        thumbnailLayer = -1;
241        final WallpaperController wallpaperController = mService.mWallpaperControllerLocked;
242        for (int i = 0; i < windowCount; i++) {
243            final WindowState w = mAppToken.allAppWindows.get(i);
244            final WindowStateAnimator winAnimator = w.mWinAnimator;
245            winAnimator.mAnimLayer = w.mLayer + adj;
246            if (winAnimator.mAnimLayer > thumbnailLayer) {
247                thumbnailLayer = winAnimator.mAnimLayer;
248            }
249            if (DEBUG_LAYERS) Slog.v(TAG, "Updating layer " + w + ": " + winAnimator.mAnimLayer);
250            if (w == mService.mInputMethodTarget && !mService.mInputMethodTargetWaitingAnim) {
251                mService.mLayersController.setInputMethodAnimLayerAdjustment(adj);
252            }
253            wallpaperController.setAnimLayerAdjustment(w, adj);
254        }
255    }
256
257    private void stepThumbnailAnimation(long currentTime) {
258        thumbnailTransformation.clear();
259        final long animationFrameTime = getAnimationFrameTime(thumbnailAnimation, currentTime);
260        thumbnailAnimation.getTransformation(animationFrameTime, thumbnailTransformation);
261
262        ScreenRotationAnimation screenRotationAnimation =
263                mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY);
264        final boolean screenAnimation = screenRotationAnimation != null
265                && screenRotationAnimation.isAnimating();
266        if (screenAnimation) {
267            thumbnailTransformation.postCompose(screenRotationAnimation.getEnterTransformation());
268        }
269        // cache often used attributes locally
270        final float tmpFloats[] = mService.mTmpFloats;
271        thumbnailTransformation.getMatrix().getValues(tmpFloats);
272        if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
273                "thumbnail", "POS " + tmpFloats[Matrix.MTRANS_X]
274                + ", " + tmpFloats[Matrix.MTRANS_Y]);
275        thumbnail.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]);
276        if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
277                "thumbnail", "alpha=" + thumbnailTransformation.getAlpha()
278                + " layer=" + thumbnailLayer
279                + " matrix=[" + tmpFloats[Matrix.MSCALE_X]
280                + "," + tmpFloats[Matrix.MSKEW_Y]
281                + "][" + tmpFloats[Matrix.MSKEW_X]
282                + "," + tmpFloats[Matrix.MSCALE_Y] + "]");
283        thumbnail.setAlpha(thumbnailTransformation.getAlpha());
284        if (thumbnailForceAboveLayer > 0) {
285            thumbnail.setLayer(thumbnailForceAboveLayer + 1);
286        } else {
287            // The thumbnail is layered below the window immediately above this
288            // token's anim layer.
289            thumbnail.setLayer(thumbnailLayer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
290                    - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
291        }
292        thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
293                tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
294        thumbnail.setWindowCrop(thumbnailTransformation.getClipRect());
295    }
296
297    /**
298     * Sometimes we need to synchronize the first frame of animation with some external event, e.g.
299     * Recents hiding some of its content. To achieve this, we prolong the start of the animaiton
300     * and keep producing the first frame of the animation.
301     */
302    private long getAnimationFrameTime(Animation animation, long currentTime) {
303        if (mProlongAnimation == PROLONG_ANIMATION_AT_START) {
304            animation.setStartTime(currentTime);
305            return currentTime + 1;
306        }
307        return currentTime;
308    }
309
310    private boolean stepAnimation(long currentTime) {
311        if (animation == null) {
312            return false;
313        }
314        transformation.clear();
315        final long animationFrameTime = getAnimationFrameTime(animation, currentTime);
316        boolean hasMoreFrames = animation.getTransformation(animationFrameTime, transformation);
317        if (!hasMoreFrames) {
318            if (deferThumbnailDestruction && !deferFinalFrameCleanup) {
319                // We are deferring the thumbnail destruction, so extend the animation for one more
320                // (dummy) frame before we clean up
321                deferFinalFrameCleanup = true;
322                hasMoreFrames = true;
323            } else {
324                if (false && DEBUG_ANIM) Slog.v(TAG,
325                        "Stepped animation in " + mAppToken + ": more=" + hasMoreFrames +
326                        ", xform=" + transformation + ", mProlongAnimation=" + mProlongAnimation);
327                deferFinalFrameCleanup = false;
328                if (mProlongAnimation == PROLONG_ANIMATION_AT_END) {
329                    hasMoreFrames = true;
330                } else {
331                    setNullAnimation();
332                    clearThumbnail();
333                    if (DEBUG_ANIM) Slog.v(TAG, "Finished animation in " + mAppToken + " @ "
334                            + currentTime);
335                }
336            }
337        }
338        hasTransformation = hasMoreFrames;
339        return hasMoreFrames;
340    }
341
342    private long getStartTimeCorrection() {
343        if (mSkipFirstFrame) {
344
345            // If the transition is an animation in which the first frame doesn't change the screen
346            // contents at all, we can just skip it and start at the second frame. So we shift the
347            // start time of the animation forward by minus the frame duration.
348            return -Choreographer.getInstance().getFrameIntervalNanos() / TimeUtils.NANOS_PER_MS;
349        } else {
350            return 0;
351        }
352    }
353
354    // This must be called while inside a transaction.
355    boolean stepAnimationLocked(long currentTime, final int displayId) {
356        if (mService.okToDisplay()) {
357            // We will run animations as long as the display isn't frozen.
358
359            if (animation == sDummyAnimation) {
360                // This guy is going to animate, but not yet.  For now count
361                // it as not animating for purposes of scheduling transactions;
362                // when it is really time to animate, this will be set to
363                // a real animation and the next call will execute normally.
364                return false;
365            }
366
367            if ((mAppToken.allDrawn || animating || mAppToken.startingDisplayed)
368                    && animation != null) {
369                if (!animating) {
370                    if (DEBUG_ANIM) Slog.v(TAG,
371                        "Starting animation in " + mAppToken +
372                        " @ " + currentTime + " scale="
373                        + mService.getTransitionAnimationScaleLocked()
374                        + " allDrawn=" + mAppToken.allDrawn + " animating=" + animating);
375                    long correction = getStartTimeCorrection();
376                    animation.setStartTime(currentTime + correction);
377                    animating = true;
378                    if (thumbnail != null) {
379                        thumbnail.show();
380                        thumbnailAnimation.setStartTime(currentTime + correction);
381                    }
382                    mSkipFirstFrame = false;
383                }
384                if (stepAnimation(currentTime)) {
385                    // animation isn't over, step any thumbnail and that's
386                    // it for now.
387                    if (thumbnail != null) {
388                        stepThumbnailAnimation(currentTime);
389                    }
390                    return true;
391                }
392            }
393        } else if (animation != null) {
394            // If the display is frozen, and there is a pending animation,
395            // clear it and make sure we run the cleanup code.
396            animating = true;
397            animation = null;
398        }
399
400        hasTransformation = false;
401
402        if (!animating && animation == null) {
403            return false;
404        }
405
406        mAnimator.setAppLayoutChanges(this, WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM,
407                "AppWindowToken", displayId);
408
409        clearAnimation();
410        animating = false;
411        if (animLayerAdjustment != 0) {
412            animLayerAdjustment = 0;
413            updateLayers();
414        }
415        if (mService.mInputMethodTarget != null
416                && mService.mInputMethodTarget.mAppToken == mAppToken) {
417            mService.moveInputMethodWindowsIfNeededLocked(true);
418        }
419
420        if (DEBUG_ANIM) Slog.v(TAG,
421                "Animation done in " + mAppToken
422                + ": reportedVisible=" + mAppToken.reportedVisible);
423
424        transformation.clear();
425
426        final int numAllAppWinAnimators = mAllAppWinAnimators.size();
427        for (int i = 0; i < numAllAppWinAnimators; i++) {
428            mAllAppWinAnimators.get(i).finishExit();
429        }
430        mService.mAppTransition.notifyAppTransitionFinishedLocked(mAppToken.token);
431        return false;
432    }
433
434    // This must be called while inside a transaction.
435    boolean showAllWindowsLocked() {
436        boolean isAnimating = false;
437        final int NW = mAllAppWinAnimators.size();
438        for (int i=0; i<NW; i++) {
439            WindowStateAnimator winAnimator = mAllAppWinAnimators.get(i);
440            if (DEBUG_VISIBILITY) Slog.v(TAG, "performing show on: " + winAnimator);
441            winAnimator.performShowLocked();
442            isAnimating |= winAnimator.isAnimationSet();
443        }
444        return isAnimating;
445    }
446
447    void dump(PrintWriter pw, String prefix, boolean dumpAll) {
448        pw.print(prefix); pw.print("mAppToken="); pw.println(mAppToken);
449        pw.print(prefix); pw.print("mAnimator="); pw.println(mAnimator);
450        pw.print(prefix); pw.print("freezingScreen="); pw.print(freezingScreen);
451                pw.print(" allDrawn="); pw.print(allDrawn);
452                pw.print(" animLayerAdjustment="); pw.println(animLayerAdjustment);
453        if (lastFreezeDuration != 0) {
454            pw.print(prefix); pw.print("lastFreezeDuration=");
455                    TimeUtils.formatDuration(lastFreezeDuration, pw); pw.println();
456        }
457        if (animating || animation != null) {
458            pw.print(prefix); pw.print("animating="); pw.println(animating);
459            pw.print(prefix); pw.print("animation="); pw.println(animation);
460        }
461        if (hasTransformation) {
462            pw.print(prefix); pw.print("XForm: ");
463                    transformation.printShortString(pw);
464                    pw.println();
465        }
466        if (thumbnail != null) {
467            pw.print(prefix); pw.print("thumbnail="); pw.print(thumbnail);
468                    pw.print(" layer="); pw.println(thumbnailLayer);
469            pw.print(prefix); pw.print("thumbnailAnimation="); pw.println(thumbnailAnimation);
470            pw.print(prefix); pw.print("thumbnailTransformation=");
471                    pw.println(thumbnailTransformation.toShortString());
472        }
473        for (int i=0; i<mAllAppWinAnimators.size(); i++) {
474            WindowStateAnimator wanim = mAllAppWinAnimators.get(i);
475            pw.print(prefix); pw.print("App Win Anim #"); pw.print(i);
476                    pw.print(": "); pw.println(wanim);
477        }
478    }
479
480    void startProlongAnimation(int prolongType) {
481        mProlongAnimation = prolongType;
482        mClearProlongedAnimation = false;
483    }
484
485    void endProlongedAnimation() {
486        mProlongAnimation = PROLONG_ANIMATION_DISABLED;
487    }
488
489    // This is an animation that does nothing: it just immediately finishes
490    // itself every time it is called.  It is used as a stub animation in cases
491    // where we want to synchronize multiple things that may be animating.
492    static final class DummyAnimation extends Animation {
493        @Override
494        public boolean getTransformation(long currentTime, Transformation outTransformation) {
495            return false;
496        }
497    }
498
499}
500