DimLayer.java revision 29bfbb878d54db14204e9b02dc17bfc6e127b6b2
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_DIM_LAYER;
20import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE;
21import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
22import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
23import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
24import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
25
26import android.graphics.PixelFormat;
27import android.graphics.Rect;
28import android.os.SystemClock;
29import android.util.Slog;
30import android.view.DisplayInfo;
31import android.view.SurfaceControl;
32
33import java.io.PrintWriter;
34
35public class DimLayer {
36    private static final String TAG = TAG_WITH_CLASS_NAME ? "DimLayer" : TAG_WM;
37    private final WindowManagerService mService;
38
39    /** Actual surface that dims */
40    private SurfaceControl mDimSurface;
41
42    /** Last value passed to mDimSurface.setAlpha() */
43    private float mAlpha = 0;
44
45    /** Last value passed to mDimSurface.setLayer() */
46    private int mLayer = -1;
47
48    /** Next values to pass to mDimSurface.setPosition() and mDimSurface.setSize() */
49    private final Rect mBounds = new Rect();
50
51    /** Last values passed to mDimSurface.setPosition() and mDimSurface.setSize() */
52    private final Rect mLastBounds = new Rect();
53
54    /** True after mDimSurface.show() has been called, false after mDimSurface.hide(). */
55    private boolean mShowing = false;
56
57    /** Value of mAlpha when beginning transition to mTargetAlpha */
58    private float mStartAlpha = 0;
59
60    /** Final value of mAlpha following transition */
61    private float mTargetAlpha = 0;
62
63    /** Time in units of SystemClock.uptimeMillis() at which the current transition started */
64    private long mStartTime;
65
66    /** Time in milliseconds to take to transition from mStartAlpha to mTargetAlpha */
67    private long mDuration;
68
69    private boolean mDestroyed = false;
70
71    private final int mDisplayId;
72
73
74    /** Interface implemented by users of the dim layer */
75    interface DimLayerUser {
76        /** Returns true if the  dim should be fullscreen. */
77        boolean dimFullscreen();
78        /** Returns the display info. of the dim layer user. */
79        DisplayInfo getDisplayInfo();
80        /** Gets the bounds of the dim layer user. */
81        void getDimBounds(Rect outBounds);
82        String toShortString();
83    }
84    /** The user of this dim layer. */
85    private final DimLayerUser mUser;
86
87    private final String mName;
88
89    DimLayer(WindowManagerService service, DimLayerUser user, int displayId, String name) {
90        mUser = user;
91        mDisplayId = displayId;
92        mService = service;
93        mName = name;
94        if (DEBUG_DIM_LAYER) Slog.v(TAG, "Ctor: displayId=" + displayId);
95    }
96
97    private void constructSurface(WindowManagerService service) {
98        SurfaceControl.openTransaction();
99        try {
100            if (DEBUG_SURFACE_TRACE) {
101                mDimSurface = new WindowSurfaceController.SurfaceTrace(service.mFxSession,
102                    "DimSurface",
103                    16, 16, PixelFormat.OPAQUE,
104                    SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN);
105            } else {
106                mDimSurface = new SurfaceControl(service.mFxSession, mName,
107                    16, 16, PixelFormat.OPAQUE,
108                    SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN);
109            }
110            if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG,
111                    "  DIM " + mDimSurface + ": CREATE");
112            mDimSurface.setLayerStack(mDisplayId);
113            adjustBounds();
114            adjustAlpha(mAlpha);
115            adjustLayer(mLayer);
116        } catch (Exception e) {
117            Slog.e(TAG_WM, "Exception creating Dim surface", e);
118        } finally {
119            SurfaceControl.closeTransaction();
120        }
121    }
122
123    /** Return true if dim layer is showing */
124    boolean isDimming() {
125        return mTargetAlpha != 0;
126    }
127
128    /** Return true if in a transition period */
129    boolean isAnimating() {
130        return mTargetAlpha != mAlpha;
131    }
132
133    float getTargetAlpha() {
134        return mTargetAlpha;
135    }
136
137    void setLayer(int layer) {
138        if (mLayer == layer) {
139            return;
140        }
141        mLayer = layer;
142        adjustLayer(layer);
143    }
144
145    private void adjustLayer(int layer) {
146        if (mDimSurface != null) {
147            mDimSurface.setLayer(layer);
148        }
149    }
150
151    int getLayer() {
152        return mLayer;
153    }
154
155    private void setAlpha(float alpha) {
156        if (mAlpha == alpha) {
157            return;
158        }
159        mAlpha = alpha;
160        adjustAlpha(alpha);
161    }
162
163    private void adjustAlpha(float alpha) {
164        if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha alpha=" + alpha);
165        try {
166            if (mDimSurface != null) {
167                mDimSurface.setAlpha(alpha);
168            }
169            if (alpha == 0 && mShowing) {
170                if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha hiding");
171                if (mDimSurface != null) {
172                    mDimSurface.hide();
173                    mShowing = false;
174                }
175            } else if (alpha > 0 && !mShowing) {
176                if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha showing");
177                if (mDimSurface != null) {
178                    mDimSurface.show();
179                    mShowing = true;
180                }
181            }
182        } catch (RuntimeException e) {
183            Slog.w(TAG, "Failure setting alpha immediately", e);
184        }
185    }
186
187    /**
188     * NOTE: Must be called with Surface transaction open.
189     */
190    private void adjustBounds() {
191        if (mUser.dimFullscreen()) {
192            getBoundsForFullscreen(mBounds);
193        }
194
195        if (mDimSurface != null) {
196            mDimSurface.setPosition(mBounds.left, mBounds.top);
197            mDimSurface.setSize(mBounds.width(), mBounds.height());
198            if (DEBUG_DIM_LAYER) Slog.v(TAG,
199                    "adjustBounds user=" + mUser.toShortString() + " mBounds=" + mBounds);
200        }
201
202        mLastBounds.set(mBounds);
203    }
204
205    private void getBoundsForFullscreen(Rect outBounds) {
206        final int dw, dh;
207        final float xPos, yPos;
208        // Set surface size to screen size.
209        final DisplayInfo info = mUser.getDisplayInfo();
210        // Multiply by 1.5 so that rotating a frozen surface that includes this does not expose
211        // a corner.
212        dw = (int) (info.logicalWidth * 1.5);
213        dh = (int) (info.logicalHeight * 1.5);
214        // back off position so 1/4 of Surface is before and 1/4 is after.
215        xPos = -1 * dw / 6;
216        yPos = -1 * dh / 6;
217        outBounds.set((int) xPos, (int) yPos, (int) xPos + dw, (int) yPos + dh);
218    }
219
220    void setBoundsForFullscreen() {
221        getBoundsForFullscreen(mBounds);
222        setBounds(mBounds);
223    }
224
225    /** @param bounds The new bounds to set */
226    void setBounds(Rect bounds) {
227        mBounds.set(bounds);
228        if (isDimming() && !mLastBounds.equals(bounds)) {
229            try {
230                SurfaceControl.openTransaction();
231                adjustBounds();
232            } catch (RuntimeException e) {
233                Slog.w(TAG, "Failure setting size", e);
234            } finally {
235                SurfaceControl.closeTransaction();
236            }
237        }
238    }
239
240    /**
241     * @param duration The time to test.
242     * @return True if the duration would lead to an earlier end to the current animation.
243     */
244    private boolean durationEndsEarlier(long duration) {
245        return SystemClock.uptimeMillis() + duration < mStartTime + mDuration;
246    }
247
248    /** Jump to the end of the animation.
249     * NOTE: Must be called with Surface transaction open. */
250    void show() {
251        if (isAnimating()) {
252            if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: immediate");
253            show(mLayer, mTargetAlpha, 0);
254        }
255    }
256
257    /**
258     * Begin an animation to a new dim value.
259     * NOTE: Must be called with Surface transaction open.
260     *
261     * @param layer The layer to set the surface to.
262     * @param alpha The dim value to end at.
263     * @param duration How long to take to get there in milliseconds.
264     */
265    void show(int layer, float alpha, long duration) {
266        if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: layer=" + layer + " alpha=" + alpha
267                + " duration=" + duration + ", mDestroyed=" + mDestroyed);
268        if (mDestroyed) {
269            Slog.e(TAG, "show: no Surface");
270            // Make sure isAnimating() returns false.
271            mTargetAlpha = mAlpha = 0;
272            return;
273        }
274
275        if (mDimSurface == null) {
276            constructSurface(mService);
277        }
278
279        if (!mLastBounds.equals(mBounds)) {
280            adjustBounds();
281        }
282        setLayer(layer);
283
284        long curTime = SystemClock.uptimeMillis();
285        final boolean animating = isAnimating();
286        if ((animating && (mTargetAlpha != alpha || durationEndsEarlier(duration)))
287                || (!animating && mAlpha != alpha)) {
288            if (duration <= 0) {
289                // No animation required, just set values.
290                setAlpha(alpha);
291            } else {
292                // Start or continue animation with new parameters.
293                mStartAlpha = mAlpha;
294                mStartTime = curTime;
295                mDuration = duration;
296            }
297        }
298        mTargetAlpha = alpha;
299        if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: mStartAlpha=" + mStartAlpha + " mStartTime="
300                + mStartTime + " mTargetAlpha=" + mTargetAlpha);
301    }
302
303    /** Immediate hide.
304     * NOTE: Must be called with Surface transaction open. */
305    void hide() {
306        if (mShowing) {
307            if (DEBUG_DIM_LAYER) Slog.v(TAG, "hide: immediate");
308            hide(0);
309        }
310    }
311
312    /**
313     * Gradually fade to transparent.
314     * NOTE: Must be called with Surface transaction open.
315     *
316     * @param duration Time to fade in milliseconds.
317     */
318    void hide(long duration) {
319        if (mShowing && (mTargetAlpha != 0 || durationEndsEarlier(duration))) {
320            if (DEBUG_DIM_LAYER) Slog.v(TAG, "hide: duration=" + duration);
321            show(mLayer, 0, duration);
322        }
323    }
324
325    /**
326     * Advance the dimming per the last #show(int, float, long) call.
327     * NOTE: Must be called with Surface transaction open.
328     *
329     * @return True if animation is still required after this step.
330     */
331    boolean stepAnimation() {
332        if (mDestroyed) {
333            Slog.e(TAG, "stepAnimation: surface destroyed");
334            // Ensure that isAnimating() returns false;
335            mTargetAlpha = mAlpha = 0;
336            return false;
337        }
338        if (isAnimating()) {
339            final long curTime = SystemClock.uptimeMillis();
340            final float alphaDelta = mTargetAlpha - mStartAlpha;
341            float alpha = mStartAlpha + alphaDelta * (curTime - mStartTime) / mDuration;
342            if (alphaDelta > 0 && alpha > mTargetAlpha ||
343                    alphaDelta < 0 && alpha < mTargetAlpha) {
344                // Don't exceed limits.
345                alpha = mTargetAlpha;
346            }
347            if (DEBUG_DIM_LAYER) Slog.v(TAG, "stepAnimation: curTime=" + curTime + " alpha=" + alpha);
348            setAlpha(alpha);
349        }
350
351        return isAnimating();
352    }
353
354    /** Cleanup */
355    void destroySurface() {
356        if (DEBUG_DIM_LAYER) Slog.v(TAG, "destroySurface.");
357        if (mDimSurface != null) {
358            mDimSurface.destroy();
359            mDimSurface = null;
360        }
361        mDestroyed = true;
362    }
363
364    public void printTo(String prefix, PrintWriter pw) {
365        pw.print(prefix); pw.print("mDimSurface="); pw.print(mDimSurface);
366                pw.print(" mLayer="); pw.print(mLayer);
367                pw.print(" mAlpha="); pw.println(mAlpha);
368        pw.print(prefix); pw.print("mLastBounds="); pw.print(mLastBounds.toShortString());
369                pw.print(" mBounds="); pw.println(mBounds.toShortString());
370        pw.print(prefix); pw.print("Last animation: ");
371                pw.print(" mDuration="); pw.print(mDuration);
372                pw.print(" mStartTime="); pw.print(mStartTime);
373                pw.print(" curTime="); pw.println(SystemClock.uptimeMillis());
374        pw.print(prefix); pw.print(" mStartAlpha="); pw.print(mStartAlpha);
375                pw.print(" mTargetAlpha="); pw.println(mTargetAlpha);
376    }
377}
378