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