DimLayerController.java revision 9fe459da7ee3fd462a646b638f647c917c229eb4
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 state.dimLayer.setBounds(mTmpBounds); 153 } 154 } 155 } 156 157 void stopDimmingIfNeeded() { 158 if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded, mState.size()=" + mState.size()); 159 for (int i = mState.size() - 1; i >= 0; i--) { 160 DimLayer.DimLayerUser dimLayerUser = mState.keyAt(i); 161 stopDimmingIfNeeded(dimLayerUser); 162 } 163 } 164 165 private void stopDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser) { 166 // No need to check if state is null, we know the key has a value. 167 DimLayerState state = mState.get(dimLayerUser); 168 if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded," 169 + " dimLayerUser=" + dimLayerUser.toShortString() 170 + " state.continueDimming=" + state.continueDimming 171 + " state.dimLayer.isDimming=" + state.dimLayer.isDimming()); 172 if (state.animator != null && state.animator.mWin.mWillReplaceWindow) { 173 return; 174 } 175 176 if (!state.continueDimming && state.dimLayer.isDimming()) { 177 state.animator = null; 178 dimLayerUser.getDimBounds(mTmpBounds); 179 state.dimLayer.setBounds(mTmpBounds); 180 } 181 } 182 183 boolean animateDimLayers() { 184 int fullScreen = -1; 185 int fullScreenAndDimming = -1; 186 boolean result = false; 187 188 for (int i = mState.size() - 1; i >= 0; i--) { 189 DimLayer.DimLayerUser user = mState.keyAt(i); 190 DimLayerState state = mState.valueAt(i); 191 // We have to check that we are acutally the shared fullscreen layer 192 // for this path. If we began as non fullscreen and became fullscreen 193 // (e.g. Docked stack closing), then we may not be the shared layer 194 // and we have to make sure we always animate the layer. 195 if (user.isFullscreen() && state.dimLayer == mSharedFullScreenDimLayer) { 196 fullScreen = i; 197 if (mState.valueAt(i).continueDimming) { 198 fullScreenAndDimming = i; 199 } 200 } else { 201 // We always want to animate the non fullscreen windows, they don't share their 202 // dim layers. 203 result |= animateDimLayers(user); 204 } 205 } 206 // For the shared, full screen dim layer, we prefer the animation that is causing it to 207 // appear. 208 if (fullScreenAndDimming != -1) { 209 result |= animateDimLayers(mState.keyAt(fullScreenAndDimming)); 210 } else if (fullScreen != -1) { 211 // If there is no animation for the full screen dim layer to appear, we can use any of 212 // the animators that will cause it to disappear. 213 result |= animateDimLayers(mState.keyAt(fullScreen)); 214 } 215 return result; 216 } 217 218 private boolean animateDimLayers(DimLayer.DimLayerUser dimLayerUser) { 219 DimLayerState state = mState.get(dimLayerUser); 220 if (DEBUG_DIM_LAYER) Slog.v(TAG, "animateDimLayers," 221 + " dimLayerUser=" + dimLayerUser.toShortString() 222 + " state.animator=" + state.animator 223 + " state.continueDimming=" + state.continueDimming); 224 final int dimLayer; 225 final float dimAmount; 226 if (state.animator == null) { 227 dimLayer = state.dimLayer.getLayer(); 228 dimAmount = 0; 229 } else { 230 if (state.dimAbove) { 231 dimLayer = state.animator.mAnimLayer + LAYER_OFFSET_DIM; 232 dimAmount = DEFAULT_DIM_AMOUNT_DEAD_WINDOW; 233 } else { 234 dimLayer = state.animator.mAnimLayer - LAYER_OFFSET_DIM; 235 dimAmount = state.animator.mWin.mAttrs.dimAmount; 236 } 237 } 238 final float targetAlpha = state.dimLayer.getTargetAlpha(); 239 if (targetAlpha != dimAmount) { 240 if (state.animator == null) { 241 state.dimLayer.hide(DEFAULT_DIM_DURATION); 242 } else { 243 long duration = (state.animator.mAnimating && state.animator.mAnimation != null) 244 ? state.animator.mAnimation.computeDurationHint() 245 : DEFAULT_DIM_DURATION; 246 if (targetAlpha > dimAmount) { 247 duration = getDimLayerFadeDuration(duration); 248 } 249 state.dimLayer.show(dimLayer, dimAmount, duration); 250 } 251 } else if (state.dimLayer.getLayer() != dimLayer) { 252 state.dimLayer.setLayer(dimLayer); 253 } 254 if (state.dimLayer.isAnimating()) { 255 if (!mDisplayContent.mService.okToDisplay()) { 256 // Jump to the end of the animation. 257 state.dimLayer.show(); 258 } else { 259 return state.dimLayer.stepAnimation(); 260 } 261 } 262 return false; 263 } 264 265 boolean isDimming(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator winAnimator) { 266 DimLayerState state = mState.get(dimLayerUser); 267 return state != null && state.animator == winAnimator && state.dimLayer.isDimming(); 268 } 269 270 private long getDimLayerFadeDuration(long duration) { 271 TypedValue tv = new TypedValue(); 272 mDisplayContent.mService.mContext.getResources().getValue( 273 com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true); 274 if (tv.type == TypedValue.TYPE_FRACTION) { 275 duration = (long) tv.getFraction(duration, duration); 276 } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) { 277 duration = tv.data; 278 } 279 return duration; 280 } 281 282 void close() { 283 for (int i = mState.size() - 1; i >= 0; i--) { 284 DimLayerState state = mState.valueAt(i); 285 state.dimLayer.destroySurface(); 286 } 287 mState.clear(); 288 mSharedFullScreenDimLayer = null; 289 } 290 291 void removeDimLayerUser(DimLayer.DimLayerUser dimLayerUser) { 292 DimLayerState state = mState.get(dimLayerUser); 293 if (state != null) { 294 // Destroy the surface, unless it's the shared fullscreen dim. 295 if (state.dimLayer != mSharedFullScreenDimLayer) { 296 state.dimLayer.destroySurface(); 297 } 298 mState.remove(dimLayerUser); 299 } 300 } 301 302 void applyDimBehind(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) { 303 applyDim(dimLayerUser, animator, false /* aboveApp */); 304 } 305 306 void applyDimAbove(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) { 307 applyDim(dimLayerUser, animator, true /* aboveApp */); 308 } 309 310 void applyDim( 311 DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator, boolean aboveApp) { 312 if (dimLayerUser == null) { 313 Slog.e(TAG, "Trying to apply dim layer for: " + this 314 + ", but no dim layer user found."); 315 return; 316 } 317 if (!getContinueDimming(dimLayerUser)) { 318 setContinueDimming(dimLayerUser); 319 if (!isDimming(dimLayerUser, animator)) { 320 if (DEBUG_DIM_LAYER) Slog.v(TAG, "Win " + this + " start dimming."); 321 startDimmingIfNeeded(dimLayerUser, animator, aboveApp); 322 } 323 } 324 } 325 326 private static class DimLayerState { 327 // The particular window requesting a dim layer. If null, hide dimLayer. 328 WindowStateAnimator animator; 329 // Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the 330 // end then stop any dimming. 331 boolean continueDimming; 332 DimLayer dimLayer; 333 boolean dimAbove; 334 } 335 336 void dump(String prefix, PrintWriter pw) { 337 pw.println(prefix + "DimLayerController"); 338 for (int i = 0, n = mState.size(); i < n; i++) { 339 pw.println(prefix + " " + mState.keyAt(i).toShortString()); 340 pw.print(prefix + " "); 341 DimLayerState state = mState.valueAt(i); 342 pw.print("dimLayer=" + (state.dimLayer == mSharedFullScreenDimLayer ? "shared" : 343 state.dimLayer)); 344 pw.print(", animator=" + state.animator); 345 pw.println(", continueDimming=" + state.continueDimming + "}"); 346 347 } 348 } 349} 350