DimLayerController.java revision 7393c7b3ec4230f2dc2caf4c6a8c25dfcf6a5b91
1package com.android.server.wm; 2 3import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DIM_LAYER; 4import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 5import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 6import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM; 7 8import android.graphics.Rect; 9import android.util.ArrayMap; 10import android.util.Slog; 11import android.util.TypedValue; 12 13import com.android.server.wm.DimLayer.DimLayerUser; 14 15import java.io.PrintWriter; 16 17/** 18 * Centralizes the control of dim layers used for 19 * {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND} 20 * as well as other use cases (such as dimming above a dead window). 21 */ 22class DimLayerController { 23 private static final String TAG_LOCAL = "DimLayerController"; 24 private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM; 25 26 /** Amount of time in milliseconds to animate the dim surface from one value to another, 27 * when no window animation is driving it. */ 28 private static final int DEFAULT_DIM_DURATION = 200; 29 30 /** 31 * The default amount of dim applied over a dead window 32 */ 33 private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f; 34 35 // Shared dim layer for fullscreen users. {@link DimLayerState#dimLayer} will point to this 36 // instead of creating a new object per fullscreen task on a display. 37 private DimLayer mSharedFullScreenDimLayer; 38 39 private ArrayMap<DimLayer.DimLayerUser, DimLayerState> mState = new ArrayMap<>(); 40 41 private DisplayContent mDisplayContent; 42 43 private Rect mTmpBounds = new Rect(); 44 45 DimLayerController(DisplayContent displayContent) { 46 mDisplayContent = displayContent; 47 } 48 49 /** Updates the dim layer bounds, recreating it if needed. */ 50 void updateDimLayer(DimLayer.DimLayerUser dimLayerUser) { 51 DimLayerState state = getOrCreateDimLayerState(dimLayerUser); 52 final boolean previousFullscreen = state.dimLayer != null 53 && state.dimLayer == mSharedFullScreenDimLayer; 54 DimLayer newDimLayer; 55 final int displayId = mDisplayContent.getDisplayId(); 56 if (dimLayerUser.isFullscreen()) { 57 if (previousFullscreen) { 58 // Nothing to do here... 59 return; 60 } 61 // Use shared fullscreen dim layer 62 newDimLayer = mSharedFullScreenDimLayer; 63 if (newDimLayer == null) { 64 if (state.dimLayer != null) { 65 // Re-purpose the previous dim layer. 66 newDimLayer = state.dimLayer; 67 } else { 68 // Create new full screen dim layer. 69 newDimLayer = new DimLayer(mDisplayContent.mService, dimLayerUser, displayId, 70 getDimLayerTag(dimLayerUser)); 71 } 72 dimLayerUser.getDimBounds(mTmpBounds); 73 newDimLayer.setBounds(mTmpBounds); 74 mSharedFullScreenDimLayer = newDimLayer; 75 } else if (state.dimLayer != null) { 76 state.dimLayer.destroySurface(); 77 } 78 } else { 79 newDimLayer = (state.dimLayer == null || previousFullscreen) 80 ? new DimLayer(mDisplayContent.mService, dimLayerUser, displayId, 81 getDimLayerTag(dimLayerUser)) 82 : state.dimLayer; 83 dimLayerUser.getDimBounds(mTmpBounds); 84 newDimLayer.setBounds(mTmpBounds); 85 } 86 state.dimLayer = newDimLayer; 87 } 88 89 private static String getDimLayerTag(DimLayerUser dimLayerUser) { 90 return TAG_LOCAL + "/" + dimLayerUser.toShortString(); 91 } 92 93 private DimLayerState getOrCreateDimLayerState(DimLayer.DimLayerUser dimLayerUser) { 94 if (DEBUG_DIM_LAYER) Slog.v(TAG, "getOrCreateDimLayerState, dimLayerUser=" 95 + dimLayerUser.toShortString()); 96 DimLayerState state = mState.get(dimLayerUser); 97 if (state == null) { 98 state = new DimLayerState(); 99 mState.put(dimLayerUser, state); 100 } 101 return state; 102 } 103 104 private void setContinueDimming(DimLayer.DimLayerUser dimLayerUser) { 105 DimLayerState state = mState.get(dimLayerUser); 106 if (state == null) { 107 if (DEBUG_DIM_LAYER) Slog.w(TAG, "setContinueDimming, no state for: " 108 + dimLayerUser.toShortString()); 109 return; 110 } 111 state.continueDimming = true; 112 } 113 114 boolean isDimming() { 115 for (int i = mState.size() - 1; i >= 0; i--) { 116 DimLayerState state = mState.valueAt(i); 117 if (state.dimLayer != null && state.dimLayer.isDimming()) { 118 return true; 119 } 120 } 121 return false; 122 } 123 124 void resetDimming() { 125 for (int i = mState.size() - 1; i >= 0; i--) { 126 mState.valueAt(i).continueDimming = false; 127 } 128 } 129 130 private boolean getContinueDimming(DimLayer.DimLayerUser dimLayerUser) { 131 DimLayerState state = mState.get(dimLayerUser); 132 return state != null && state.continueDimming; 133 } 134 135 void startDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser, 136 WindowStateAnimator newWinAnimator, boolean aboveApp) { 137 // Only set dim params on the highest dimmed layer. 138 // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer. 139 DimLayerState state = getOrCreateDimLayerState(dimLayerUser); 140 state.dimAbove = aboveApp; 141 if (DEBUG_DIM_LAYER) Slog.v(TAG, "startDimmingIfNeeded," 142 + " dimLayerUser=" + dimLayerUser.toShortString() 143 + " newWinAnimator=" + newWinAnimator 144 + " state.animator=" + state.animator); 145 if (newWinAnimator.getShown() && (state.animator == null 146 || !state.animator.getShown() 147 || state.animator.mAnimLayer <= newWinAnimator.mAnimLayer)) { 148 state.animator = newWinAnimator; 149 if (state.animator.mWin.mAppToken == null && !dimLayerUser.isFullscreen()) { 150 // Dim should cover the entire screen for system windows. 151 mDisplayContent.getLogicalDisplayRect(mTmpBounds); 152 } else { 153 dimLayerUser.getDimBounds(mTmpBounds); 154 } 155 state.dimLayer.setBounds(mTmpBounds); 156 } 157 } 158 159 void stopDimmingIfNeeded() { 160 if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded, mState.size()=" + mState.size()); 161 for (int i = mState.size() - 1; i >= 0; i--) { 162 DimLayer.DimLayerUser dimLayerUser = mState.keyAt(i); 163 stopDimmingIfNeeded(dimLayerUser); 164 } 165 } 166 167 private void stopDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser) { 168 // No need to check if state is null, we know the key has a value. 169 DimLayerState state = mState.get(dimLayerUser); 170 if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded," 171 + " dimLayerUser=" + dimLayerUser.toShortString() 172 + " state.continueDimming=" + state.continueDimming 173 + " state.dimLayer.isDimming=" + state.dimLayer.isDimming()); 174 if (!state.continueDimming && state.dimLayer.isDimming()) { 175 state.animator = null; 176 dimLayerUser.getDimBounds(mTmpBounds); 177 state.dimLayer.setBounds(mTmpBounds); 178 } 179 } 180 181 boolean animateDimLayers() { 182 int fullScreen = -1; 183 int fullScreenAndDimming = -1; 184 boolean result = false; 185 186 for (int i = mState.size() - 1; i >= 0; i--) { 187 DimLayer.DimLayerUser user = mState.keyAt(i); 188 DimLayerState state = mState.valueAt(i); 189 // We have to check that we are acutally the shared fullscreen layer 190 // for this path. If we began as non fullscreen and became fullscreen 191 // (e.g. Docked stack closing), then we may not be the shared layer 192 // and we have to make sure we always animate the layer. 193 if (user.isFullscreen() && state.dimLayer == mSharedFullScreenDimLayer) { 194 fullScreen = i; 195 if (mState.valueAt(i).continueDimming) { 196 fullScreenAndDimming = i; 197 } 198 } else { 199 // We always want to animate the non fullscreen windows, they don't share their 200 // dim layers. 201 result |= animateDimLayers(user); 202 } 203 } 204 // For the shared, full screen dim layer, we prefer the animation that is causing it to 205 // appear. 206 if (fullScreenAndDimming != -1) { 207 result |= animateDimLayers(mState.keyAt(fullScreenAndDimming)); 208 } else if (fullScreen != -1) { 209 // If there is no animation for the full screen dim layer to appear, we can use any of 210 // the animators that will cause it to disappear. 211 result |= animateDimLayers(mState.keyAt(fullScreen)); 212 } 213 return result; 214 } 215 216 private boolean animateDimLayers(DimLayer.DimLayerUser dimLayerUser) { 217 DimLayerState state = mState.get(dimLayerUser); 218 if (DEBUG_DIM_LAYER) Slog.v(TAG, "animateDimLayers," 219 + " dimLayerUser=" + dimLayerUser.toShortString() 220 + " state.animator=" + state.animator 221 + " state.continueDimming=" + state.continueDimming); 222 final int dimLayer; 223 final float dimAmount; 224 if (state.animator == null) { 225 dimLayer = state.dimLayer.getLayer(); 226 dimAmount = 0; 227 } else { 228 if (state.dimAbove) { 229 dimLayer = state.animator.mAnimLayer + LAYER_OFFSET_DIM; 230 dimAmount = DEFAULT_DIM_AMOUNT_DEAD_WINDOW; 231 } else { 232 dimLayer = state.animator.mAnimLayer - LAYER_OFFSET_DIM; 233 dimAmount = state.animator.mWin.mAttrs.dimAmount; 234 } 235 } 236 final float targetAlpha = state.dimLayer.getTargetAlpha(); 237 if (targetAlpha != dimAmount) { 238 if (state.animator == null) { 239 state.dimLayer.hide(DEFAULT_DIM_DURATION); 240 } else { 241 long duration = (state.animator.mAnimating && state.animator.mAnimation != null) 242 ? state.animator.mAnimation.computeDurationHint() 243 : DEFAULT_DIM_DURATION; 244 if (targetAlpha > dimAmount) { 245 duration = getDimLayerFadeDuration(duration); 246 } 247 state.dimLayer.show(dimLayer, dimAmount, duration); 248 } 249 } else if (state.dimLayer.getLayer() != dimLayer) { 250 state.dimLayer.setLayer(dimLayer); 251 } 252 if (state.dimLayer.isAnimating()) { 253 if (!mDisplayContent.mService.okToDisplay()) { 254 // Jump to the end of the animation. 255 state.dimLayer.show(); 256 } else { 257 return state.dimLayer.stepAnimation(); 258 } 259 } 260 return false; 261 } 262 263 boolean isDimming(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator winAnimator) { 264 DimLayerState state = mState.get(dimLayerUser); 265 return state != null && state.animator == winAnimator && state.dimLayer.isDimming(); 266 } 267 268 private long getDimLayerFadeDuration(long duration) { 269 TypedValue tv = new TypedValue(); 270 mDisplayContent.mService.mContext.getResources().getValue( 271 com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true); 272 if (tv.type == TypedValue.TYPE_FRACTION) { 273 duration = (long) tv.getFraction(duration, duration); 274 } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) { 275 duration = tv.data; 276 } 277 return duration; 278 } 279 280 void close() { 281 for (int i = mState.size() - 1; i >= 0; i--) { 282 DimLayerState state = mState.valueAt(i); 283 state.dimLayer.destroySurface(); 284 } 285 mState.clear(); 286 mSharedFullScreenDimLayer = null; 287 } 288 289 void removeDimLayerUser(DimLayer.DimLayerUser dimLayerUser) { 290 DimLayerState state = mState.get(dimLayerUser); 291 if (state != null) { 292 // Destroy the surface, unless it's the shared fullscreen dim. 293 if (state.dimLayer != mSharedFullScreenDimLayer) { 294 state.dimLayer.destroySurface(); 295 } 296 mState.remove(dimLayerUser); 297 } 298 } 299 300 void applyDimBehind(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) { 301 applyDim(dimLayerUser, animator, false /* aboveApp */); 302 } 303 304 void applyDimAbove(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) { 305 applyDim(dimLayerUser, animator, true /* aboveApp */); 306 } 307 308 private void applyDim( 309 DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator, boolean aboveApp) { 310 if (dimLayerUser == null) { 311 Slog.e(TAG, "Trying to apply dim layer for: " + this 312 + ", but no dim layer user found."); 313 return; 314 } 315 if (!getContinueDimming(dimLayerUser)) { 316 setContinueDimming(dimLayerUser); 317 if (!isDimming(dimLayerUser, animator)) { 318 if (DEBUG_DIM_LAYER) Slog.v(TAG, "Win " + this + " start dimming."); 319 startDimmingIfNeeded(dimLayerUser, animator, aboveApp); 320 } 321 } 322 } 323 324 private static class DimLayerState { 325 // The particular window requesting a dim layer. If null, hide dimLayer. 326 WindowStateAnimator animator; 327 // Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the 328 // end then stop any dimming. 329 boolean continueDimming; 330 DimLayer dimLayer; 331 boolean dimAbove; 332 } 333 334 void dump(String prefix, PrintWriter pw) { 335 pw.println(prefix + "DimLayerController"); 336 for (int i = 0, n = mState.size(); i < n; i++) { 337 pw.println(prefix + " " + mState.keyAt(i).toShortString()); 338 pw.print(prefix + " "); 339 DimLayerState state = mState.valueAt(i); 340 pw.print("dimLayer=" + (state.dimLayer == mSharedFullScreenDimLayer ? "shared" : 341 state.dimLayer)); 342 pw.print(", animator=" + state.animator); 343 pw.println(", continueDimming=" + state.continueDimming + "}"); 344 345 } 346 } 347} 348