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