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