DimLayer.java revision 902945d1518a8a3b987dd2afdae790cf942b20ec
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    /**
129     * @param layer The new layer value.
130     * @param inTransaction Whether the call is made within a surface transaction.
131     */
132    void adjustSurface(int layer, boolean inTransaction) {
133        final int dw, dh;
134        final float xPos, yPos;
135        if (!mStack.isFullscreen()) {
136            dw = mBounds.width();
137            dh = mBounds.height();
138            xPos = mBounds.left;
139            yPos = mBounds.top;
140        } else {
141            // Set surface size to screen size.
142            final DisplayInfo info = mDisplayContent.getDisplayInfo();
143            // Multiply by 1.5 so that rotating a frozen surface that includes this does not expose
144            // a corner.
145            dw = (int) (info.logicalWidth * 1.5);
146            dh = (int) (info.logicalHeight * 1.5);
147            // back off position so 1/4 of Surface is before and 1/4 is after.
148            xPos = -1 * dw / 6;
149            yPos = -1 * dh / 6;
150        }
151
152        try {
153            if (!inTransaction) {
154                SurfaceControl.openTransaction();
155            }
156            mDimSurface.setPosition(xPos, yPos);
157            mDimSurface.setSize(dw, dh);
158            mDimSurface.setLayer(layer);
159        } catch (RuntimeException e) {
160            Slog.w(TAG, "Failure setting size or layer", e);
161        } finally {
162            if (!inTransaction) {
163                SurfaceControl.closeTransaction();
164            }
165        }
166        mLastBounds.set(mBounds);
167        mLayer = layer;
168    }
169
170    // Assumes that surface transactions are currently closed.
171    void setBounds(Rect bounds) {
172        mBounds.set(bounds);
173        if (isDimming() && !mLastBounds.equals(bounds)) {
174            adjustSurface(mLayer, false);
175        }
176    }
177
178    /**
179     * @param duration The time to test.
180     * @return True if the duration would lead to an earlier end to the current animation.
181     */
182    private boolean durationEndsEarlier(long duration) {
183        return SystemClock.uptimeMillis() + duration < mStartTime + mDuration;
184    }
185
186    /** Jump to the end of the animation.
187     * NOTE: Must be called with Surface transaction open. */
188    void show() {
189        if (isAnimating()) {
190            if (DEBUG) Slog.v(TAG, "show: immediate");
191            show(mLayer, mTargetAlpha, 0);
192        }
193    }
194
195    /**
196     * Begin an animation to a new dim value.
197     * NOTE: Must be called with Surface transaction open.
198     *
199     * @param layer The layer to set the surface to.
200     * @param alpha The dim value to end at.
201     * @param duration How long to take to get there in milliseconds.
202     */
203    void show(int layer, float alpha, long duration) {
204        if (DEBUG) Slog.v(TAG, "show: layer=" + layer + " alpha=" + alpha
205                + " duration=" + duration);
206        if (mDimSurface == null) {
207            Slog.e(TAG, "show: no Surface");
208            // Make sure isAnimating() returns false.
209            mTargetAlpha = mAlpha = 0;
210            return;
211        }
212
213        if (!mLastBounds.equals(mBounds) || mLayer != layer) {
214            adjustSurface(layer, true);
215        }
216
217        long curTime = SystemClock.uptimeMillis();
218        final boolean animating = isAnimating();
219        if ((animating && (mTargetAlpha != alpha || durationEndsEarlier(duration)))
220                || (!animating && mAlpha != alpha)) {
221            if (duration <= 0) {
222                // No animation required, just set values.
223                setAlpha(alpha);
224            } else {
225                // Start or continue animation with new parameters.
226                mStartAlpha = mAlpha;
227                mStartTime = curTime;
228                mDuration = duration;
229            }
230        }
231        if (DEBUG) Slog.v(TAG, "show: mStartAlpha=" + mStartAlpha + " mStartTime=" + mStartTime);
232        mTargetAlpha = alpha;
233    }
234
235    /** Immediate hide.
236     * NOTE: Must be called with Surface transaction open. */
237    void hide() {
238        if (mShowing) {
239            if (DEBUG) Slog.v(TAG, "hide: immediate");
240            hide(0);
241        }
242    }
243
244    /**
245     * Gradually fade to transparent.
246     * NOTE: Must be called with Surface transaction open.
247     *
248     * @param duration Time to fade in milliseconds.
249     */
250    void hide(long duration) {
251        if (mShowing && (mTargetAlpha != 0 || durationEndsEarlier(duration))) {
252            if (DEBUG) Slog.v(TAG, "hide: duration=" + duration);
253            show(mLayer, 0, duration);
254        }
255    }
256
257    /**
258     * Advance the dimming per the last #show(int, float, long) call.
259     * NOTE: Must be called with Surface transaction open.
260     *
261     * @return True if animation is still required after this step.
262     */
263    boolean stepAnimation() {
264        if (mDimSurface == null) {
265            Slog.e(TAG, "stepAnimation: null Surface");
266            // Ensure that isAnimating() returns false;
267            mTargetAlpha = mAlpha = 0;
268            return false;
269        }
270
271        if (isAnimating()) {
272            final long curTime = SystemClock.uptimeMillis();
273            final float alphaDelta = mTargetAlpha - mStartAlpha;
274            float alpha = mStartAlpha + alphaDelta * (curTime - mStartTime) / mDuration;
275            if (alphaDelta > 0 && alpha > mTargetAlpha ||
276                    alphaDelta < 0 && alpha < mTargetAlpha) {
277                // Don't exceed limits.
278                alpha = mTargetAlpha;
279            }
280            if (DEBUG) Slog.v(TAG, "stepAnimation: curTime=" + curTime + " alpha=" + alpha);
281            setAlpha(alpha);
282        }
283
284        return isAnimating();
285    }
286
287    /** Cleanup */
288    void destroySurface() {
289        if (DEBUG) Slog.v(TAG, "destroySurface.");
290        if (mDimSurface != null) {
291            mDimSurface.destroy();
292            mDimSurface = null;
293        }
294    }
295
296    public void printTo(String prefix, PrintWriter pw) {
297        pw.print(prefix); pw.print("mDimSurface="); pw.print(mDimSurface);
298                pw.print(" mLayer="); pw.print(mLayer);
299                pw.print(" mAlpha="); pw.println(mAlpha);
300        pw.print(prefix); pw.print("mLastBounds="); pw.print(mLastBounds.toShortString());
301                pw.print(" mBounds="); pw.println(mBounds.toShortString());
302        pw.print(prefix); pw.print("Last animation: ");
303                pw.print(" mDuration="); pw.print(mDuration);
304                pw.print(" mStartTime="); pw.print(mStartTime);
305                pw.print(" curTime="); pw.println(SystemClock.uptimeMillis());
306        pw.print(prefix); pw.print(" mStartAlpha="); pw.print(mStartAlpha);
307                pw.print(" mTargetAlpha="); pw.println(mTargetAlpha);
308    }
309}
310