DimLayer.java revision df88d73092c62a1a3cd2b2056ca63ae2e70cc238
1// Copyright 2012 Google Inc. All Rights Reserved.
2
3package com.android.server.wm;
4
5import android.graphics.PixelFormat;
6import android.graphics.Rect;
7import android.os.SystemClock;
8import android.util.Slog;
9import android.view.DisplayInfo;
10import android.view.SurfaceControl;
11
12import java.io.PrintWriter;
13
14public class DimLayer {
15    private static final String TAG = "DimLayer";
16    private static final boolean DEBUG = false;
17
18    /** Reference to the owner of this object. */
19    final DisplayContent mDisplayContent;
20
21    /** Actual surface that dims */
22    SurfaceControl mDimSurface;
23
24    /** Last value passed to mDimSurface.setAlpha() */
25    float mAlpha = 0;
26
27    /** Last value passed to mDimSurface.setLayer() */
28    int mLayer = -1;
29
30    /** Next values to pass to mDimSurface.setPosition() and mDimSurface.setSize() */
31    Rect mBounds = new Rect();
32
33    /** Last values passed to mDimSurface.setPosition() and mDimSurface.setSize() */
34    Rect mLastBounds = new Rect();
35
36    /** True after mDimSurface.show() has been called, false after mDimSurface.hide(). */
37    private boolean mShowing = false;
38
39    /** Value of mAlpha when beginning transition to mTargetAlpha */
40    float mStartAlpha = 0;
41
42    /** Final value of mAlpha following transition */
43    float mTargetAlpha = 0;
44
45    /** Time in units of SystemClock.uptimeMillis() at which the current transition started */
46    long mStartTime;
47
48    /** Time in milliseconds to take to transition from mStartAlpha to mTargetAlpha */
49    long mDuration;
50
51    /** Owning stack */
52    final TaskStack mStack;
53
54    DimLayer(WindowManagerService service, TaskStack stack, DisplayContent displayContent) {
55        mStack = stack;
56        mDisplayContent = displayContent;
57        final int displayId = mDisplayContent.getDisplayId();
58        if (DEBUG) Slog.v(TAG, "Ctor: displayId=" + displayId);
59        SurfaceControl.openTransaction();
60        try {
61            if (WindowManagerService.DEBUG_SURFACE_TRACE) {
62                mDimSurface = new WindowStateAnimator.SurfaceTrace(service.mFxSession,
63                    "DimSurface",
64                    16, 16, PixelFormat.OPAQUE,
65                    SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN);
66            } else {
67                mDimSurface = new SurfaceControl(service.mFxSession, TAG,
68                    16, 16, PixelFormat.OPAQUE,
69                    SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN);
70            }
71            if (WindowManagerService.SHOW_TRANSACTIONS ||
72                    WindowManagerService.SHOW_SURFACE_ALLOC) Slog.i(TAG,
73                            "  DIM " + mDimSurface + ": CREATE");
74            mDimSurface.setLayerStack(displayId);
75        } catch (Exception e) {
76            Slog.e(WindowManagerService.TAG, "Exception creating Dim surface", e);
77        } finally {
78            SurfaceControl.closeTransaction();
79        }
80    }
81
82    /** Return true if dim layer is showing */
83    boolean isDimming() {
84        return mTargetAlpha != 0;
85    }
86
87    /** Return true if in a transition period */
88    boolean isAnimating() {
89        return mTargetAlpha != mAlpha;
90    }
91
92    float getTargetAlpha() {
93        return mTargetAlpha;
94    }
95
96    void setLayer(int layer) {
97        if (mLayer != layer) {
98            mLayer = layer;
99            mDimSurface.setLayer(layer);
100        }
101    }
102
103    int getLayer() {
104        return mLayer;
105    }
106
107    private void setAlpha(float alpha) {
108        if (mAlpha != alpha) {
109            if (DEBUG) Slog.v(TAG, "setAlpha alpha=" + alpha);
110            try {
111                mDimSurface.setAlpha(alpha);
112                if (alpha == 0 && mShowing) {
113                    if (DEBUG) Slog.v(TAG, "setAlpha hiding");
114                    mDimSurface.hide();
115                    mShowing = false;
116                } else if (alpha > 0 && !mShowing) {
117                    if (DEBUG) Slog.v(TAG, "setAlpha showing");
118                    mDimSurface.show();
119                    mShowing = true;
120                }
121            } catch (RuntimeException e) {
122                Slog.w(TAG, "Failure setting alpha immediately", e);
123            }
124            mAlpha = alpha;
125        }
126    }
127
128    void setBounds(Rect bounds) {
129        mBounds.set(bounds);
130    }
131
132    /**
133     * @param duration The time to test.
134     * @return True if the duration would lead to an earlier end to the current animation.
135     */
136    private boolean durationEndsEarlier(long duration) {
137        return SystemClock.uptimeMillis() + duration < mStartTime + mDuration;
138    }
139
140    /** Jump to the end of the animation.
141     * NOTE: Must be called with Surface transaction open. */
142    void show() {
143        if (isAnimating()) {
144            if (DEBUG) Slog.v(TAG, "show: immediate");
145            show(mLayer, mTargetAlpha, 0);
146        }
147    }
148
149    /**
150     * Begin an animation to a new dim value.
151     * NOTE: Must be called with Surface transaction open.
152     *
153     * @param layer The layer to set the surface to.
154     * @param alpha The dim value to end at.
155     * @param duration How long to take to get there in milliseconds.
156     */
157    void show(int layer, float alpha, long duration) {
158        if (DEBUG) Slog.v(TAG, "show: layer=" + layer + " alpha=" + alpha
159                + " duration=" + duration);
160        if (mDimSurface == null) {
161            Slog.e(TAG, "show: no Surface");
162            // Make sure isAnimating() returns false.
163            mTargetAlpha = mAlpha = 0;
164            return;
165        }
166
167        final int dw, dh;
168        final float xPos, yPos;
169        if (!mStack.isFullscreen()) {
170            dw = mBounds.width();
171            dh = mBounds.height();
172            xPos = mBounds.left;
173            yPos = mBounds.right;
174        } else {
175            // Set surface size to screen size.
176            final DisplayInfo info = mDisplayContent.getDisplayInfo();
177            // Multiply by 1.5 so that rotating a frozen surface that includes this does not expose a
178            // corner.
179            dw = (int) (info.logicalWidth * 1.5);
180            dh = (int) (info.logicalHeight * 1.5);
181            // back off position so 1/4 of Surface is before and 1/4 is after.
182            xPos = -1 * dw / 6;
183            yPos = -1 * dh / 6;
184        }
185
186        if (!mLastBounds.equals(mBounds) || mLayer != layer) {
187            try {
188                mDimSurface.setPosition(xPos, yPos);
189                mDimSurface.setSize(dw, dh);
190                mDimSurface.setLayer(layer);
191            } catch (RuntimeException e) {
192                Slog.w(TAG, "Failure setting size or layer", e);
193            }
194            mLastBounds.set(mBounds);
195            mLayer = layer;
196        }
197
198        long curTime = SystemClock.uptimeMillis();
199        final boolean animating = isAnimating();
200        if ((animating && (mTargetAlpha != alpha || durationEndsEarlier(duration)))
201                || (!animating && mAlpha != alpha)) {
202            if (duration <= 0) {
203                // No animation required, just set values.
204                setAlpha(alpha);
205            } else {
206                // Start or continue animation with new parameters.
207                mStartAlpha = mAlpha;
208                mStartTime = curTime;
209                mDuration = duration;
210            }
211        }
212        if (DEBUG) Slog.v(TAG, "show: mStartAlpha=" + mStartAlpha + " mStartTime=" + mStartTime);
213        mTargetAlpha = alpha;
214    }
215
216    /** Immediate hide.
217     * NOTE: Must be called with Surface transaction open. */
218    void hide() {
219        if (mShowing) {
220            if (DEBUG) Slog.v(TAG, "hide: immediate");
221            hide(0);
222        }
223    }
224
225    /**
226     * Gradually fade to transparent.
227     * NOTE: Must be called with Surface transaction open.
228     *
229     * @param duration Time to fade in milliseconds.
230     */
231    void hide(long duration) {
232        if (mShowing && (mTargetAlpha != 0 || durationEndsEarlier(duration))) {
233            if (DEBUG) Slog.v(TAG, "hide: duration=" + duration);
234            show(mLayer, 0, duration);
235        }
236    }
237
238    /**
239     * Advance the dimming per the last #show(int, float, long) call.
240     * NOTE: Must be called with Surface transaction open.
241     *
242     * @return True if animation is still required after this step.
243     */
244    boolean stepAnimation() {
245        if (mDimSurface == null) {
246            Slog.e(TAG, "stepAnimation: null Surface");
247            // Ensure that isAnimating() returns false;
248            mTargetAlpha = mAlpha = 0;
249            return false;
250        }
251
252        if (isAnimating()) {
253            final long curTime = SystemClock.uptimeMillis();
254            final float alphaDelta = mTargetAlpha - mStartAlpha;
255            float alpha = mStartAlpha + alphaDelta * (curTime - mStartTime) / mDuration;
256            if (alphaDelta > 0 && alpha > mTargetAlpha ||
257                    alphaDelta < 0 && alpha < mTargetAlpha) {
258                // Don't exceed limits.
259                alpha = mTargetAlpha;
260            }
261            if (DEBUG) Slog.v(TAG, "stepAnimation: curTime=" + curTime + " alpha=" + alpha);
262            setAlpha(alpha);
263        }
264
265        return isAnimating();
266    }
267
268    /** Cleanup */
269    void destroySurface() {
270        if (DEBUG) Slog.v(TAG, "destroySurface.");
271        if (mDimSurface != null) {
272            mDimSurface.destroy();
273            mDimSurface = null;
274        }
275    }
276
277    public void printTo(String prefix, PrintWriter pw) {
278        pw.print(prefix); pw.print("mDimSurface="); pw.print(mDimSurface);
279                pw.print(" mLayer="); pw.print(mLayer);
280                pw.print(" mAlpha="); pw.println(mAlpha);
281        pw.print(prefix); pw.print("mLastBounds="); pw.print(mLastBounds.toShortString());
282                pw.print(" mBounds="); pw.println(mBounds.toShortString());
283        pw.print(prefix); pw.print("Last animation: ");
284                pw.print(" mDuration="); pw.print(mDuration);
285                pw.print(" mStartTime="); pw.print(mStartTime);
286                pw.print(" curTime="); pw.println(SystemClock.uptimeMillis());
287        pw.print(prefix); pw.print(" mStartAlpha="); pw.print(mStartAlpha);
288                pw.print(" mTargetAlpha="); pw.println(mTargetAlpha);
289    }
290}
291