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 static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DIM_LAYER; 20 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE; 21 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC; 22 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS; 23 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 24 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 25 26 import android.graphics.PixelFormat; 27 import android.graphics.Rect; 28 import android.os.SystemClock; 29 import android.util.Slog; 30 import android.view.DisplayInfo; 31 import android.view.SurfaceControl; 32 33 import java.io.PrintWriter; 34 35 public 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