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