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