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