blob: 7f97c463774e234fbbc3fbeb4ae9cae8763ce7eb [file] [log] [blame]
Filip Gruszczynski0689ae92015-10-01 12:30:31 -07001package com.android.server.wm;
2
Jorim Jaggi72a6f052016-05-05 16:25:43 -07003import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -08004import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DIM_LAYER;
5import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
6import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
Chong Zhang112eb8c2015-11-02 11:17:00 -08007import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM;
Filip Gruszczynski0689ae92015-10-01 12:30:31 -07008
9import android.graphics.Rect;
10import android.util.ArrayMap;
11import android.util.Slog;
12import android.util.TypedValue;
13
Jorim Jaggibc5425c2016-03-01 13:51:16 +010014import com.android.server.wm.DimLayer.DimLayerUser;
15
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070016import java.io.PrintWriter;
17
18/**
19 * Centralizes the control of dim layers used for
Chong Zhang112eb8c2015-11-02 11:17:00 -080020 * {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND}
21 * as well as other use cases (such as dimming above a dead window).
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070022 */
Chong Zhang112eb8c2015-11-02 11:17:00 -080023class DimLayerController {
Jorim Jaggibc5425c2016-03-01 13:51:16 +010024 private static final String TAG_LOCAL = "DimLayerController";
25 private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM;
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070026
27 /** Amount of time in milliseconds to animate the dim surface from one value to another,
28 * when no window animation is driving it. */
29 private static final int DEFAULT_DIM_DURATION = 200;
30
Chong Zhang112eb8c2015-11-02 11:17:00 -080031 /**
32 * The default amount of dim applied over a dead window
33 */
34 private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
35
36 // Shared dim layer for fullscreen users. {@link DimLayerState#dimLayer} will point to this
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070037 // instead of creating a new object per fullscreen task on a display.
38 private DimLayer mSharedFullScreenDimLayer;
39
Chong Zhang112eb8c2015-11-02 11:17:00 -080040 private ArrayMap<DimLayer.DimLayerUser, DimLayerState> mState = new ArrayMap<>();
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070041
42 private DisplayContent mDisplayContent;
43
44 private Rect mTmpBounds = new Rect();
45
Chong Zhang112eb8c2015-11-02 11:17:00 -080046 DimLayerController(DisplayContent displayContent) {
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070047 mDisplayContent = displayContent;
48 }
49
50 /** Updates the dim layer bounds, recreating it if needed. */
51 void updateDimLayer(DimLayer.DimLayerUser dimLayerUser) {
Wale Ogunwale29bfbb82016-05-12 15:13:52 -070052 final DimLayerState state = getOrCreateDimLayerState(dimLayerUser);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070053 final boolean previousFullscreen = state.dimLayer != null
54 && state.dimLayer == mSharedFullScreenDimLayer;
55 DimLayer newDimLayer;
56 final int displayId = mDisplayContent.getDisplayId();
Wale Ogunwale29bfbb82016-05-12 15:13:52 -070057 if (dimLayerUser.dimFullscreen()) {
58 if (previousFullscreen && mSharedFullScreenDimLayer != null) {
59 // Update the bounds for fullscreen in case of rotation.
60 mSharedFullScreenDimLayer.setBoundsForFullscreen();
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070061 return;
62 }
63 // Use shared fullscreen dim layer
64 newDimLayer = mSharedFullScreenDimLayer;
65 if (newDimLayer == null) {
66 if (state.dimLayer != null) {
67 // Re-purpose the previous dim layer.
68 newDimLayer = state.dimLayer;
69 } else {
70 // Create new full screen dim layer.
Jorim Jaggibc5425c2016-03-01 13:51:16 +010071 newDimLayer = new DimLayer(mDisplayContent.mService, dimLayerUser, displayId,
72 getDimLayerTag(dimLayerUser));
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070073 }
Chong Zhang4c9ba52a2015-11-10 18:36:33 -080074 dimLayerUser.getDimBounds(mTmpBounds);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070075 newDimLayer.setBounds(mTmpBounds);
76 mSharedFullScreenDimLayer = newDimLayer;
77 } else if (state.dimLayer != null) {
Chong Zhang112eb8c2015-11-02 11:17:00 -080078 state.dimLayer.destroySurface();
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070079 }
80 } else {
81 newDimLayer = (state.dimLayer == null || previousFullscreen)
Jorim Jaggibc5425c2016-03-01 13:51:16 +010082 ? new DimLayer(mDisplayContent.mService, dimLayerUser, displayId,
83 getDimLayerTag(dimLayerUser))
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070084 : state.dimLayer;
Chong Zhang4c9ba52a2015-11-10 18:36:33 -080085 dimLayerUser.getDimBounds(mTmpBounds);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070086 newDimLayer.setBounds(mTmpBounds);
87 }
88 state.dimLayer = newDimLayer;
89 }
90
Jorim Jaggibc5425c2016-03-01 13:51:16 +010091 private static String getDimLayerTag(DimLayerUser dimLayerUser) {
92 return TAG_LOCAL + "/" + dimLayerUser.toShortString();
93 }
94
Chong Zhang4c9ba52a2015-11-10 18:36:33 -080095 private DimLayerState getOrCreateDimLayerState(DimLayer.DimLayerUser dimLayerUser) {
Chong Zhang112eb8c2015-11-02 11:17:00 -080096 if (DEBUG_DIM_LAYER) Slog.v(TAG, "getOrCreateDimLayerState, dimLayerUser="
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070097 + dimLayerUser.toShortString());
Chong Zhang112eb8c2015-11-02 11:17:00 -080098 DimLayerState state = mState.get(dimLayerUser);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070099 if (state == null) {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800100 state = new DimLayerState();
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700101 mState.put(dimLayerUser, state);
102 }
103 return state;
104 }
105
106 private void setContinueDimming(DimLayer.DimLayerUser dimLayerUser) {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800107 DimLayerState state = mState.get(dimLayerUser);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700108 if (state == null) {
109 if (DEBUG_DIM_LAYER) Slog.w(TAG, "setContinueDimming, no state for: "
110 + dimLayerUser.toShortString());
111 return;
112 }
113 state.continueDimming = true;
114 }
115
116 boolean isDimming() {
117 for (int i = mState.size() - 1; i >= 0; i--) {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800118 DimLayerState state = mState.valueAt(i);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700119 if (state.dimLayer != null && state.dimLayer.isDimming()) {
120 return true;
121 }
122 }
123 return false;
124 }
125
126 void resetDimming() {
127 for (int i = mState.size() - 1; i >= 0; i--) {
128 mState.valueAt(i).continueDimming = false;
129 }
130 }
131
132 private boolean getContinueDimming(DimLayer.DimLayerUser dimLayerUser) {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800133 DimLayerState state = mState.get(dimLayerUser);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700134 return state != null && state.continueDimming;
135 }
136
137 void startDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser,
Chong Zhang112eb8c2015-11-02 11:17:00 -0800138 WindowStateAnimator newWinAnimator, boolean aboveApp) {
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700139 // Only set dim params on the highest dimmed layer.
140 // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
Chong Zhang4c9ba52a2015-11-10 18:36:33 -0800141 DimLayerState state = getOrCreateDimLayerState(dimLayerUser);
142 state.dimAbove = aboveApp;
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700143 if (DEBUG_DIM_LAYER) Slog.v(TAG, "startDimmingIfNeeded,"
144 + " dimLayerUser=" + dimLayerUser.toShortString()
145 + " newWinAnimator=" + newWinAnimator
146 + " state.animator=" + state.animator);
Robert Carre6a83512015-11-03 16:09:21 -0800147 if (newWinAnimator.getShown() && (state.animator == null
148 || !state.animator.getShown()
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700149 || state.animator.mAnimLayer <= newWinAnimator.mAnimLayer)) {
150 state.animator = newWinAnimator;
Wale Ogunwale29bfbb82016-05-12 15:13:52 -0700151 if (state.animator.mWin.mAppToken == null && !dimLayerUser.dimFullscreen()) {
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700152 // Dim should cover the entire screen for system windows.
153 mDisplayContent.getLogicalDisplayRect(mTmpBounds);
Robert Carr7393c7b2016-04-17 19:24:21 -0700154 } else {
155 dimLayerUser.getDimBounds(mTmpBounds);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700156 }
Robert Carr7393c7b2016-04-17 19:24:21 -0700157 state.dimLayer.setBounds(mTmpBounds);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700158 }
159 }
160
161 void stopDimmingIfNeeded() {
162 if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded, mState.size()=" + mState.size());
163 for (int i = mState.size() - 1; i >= 0; i--) {
164 DimLayer.DimLayerUser dimLayerUser = mState.keyAt(i);
165 stopDimmingIfNeeded(dimLayerUser);
166 }
167 }
168
169 private void stopDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser) {
170 // No need to check if state is null, we know the key has a value.
Chong Zhang112eb8c2015-11-02 11:17:00 -0800171 DimLayerState state = mState.get(dimLayerUser);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700172 if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded,"
173 + " dimLayerUser=" + dimLayerUser.toShortString()
174 + " state.continueDimming=" + state.continueDimming
175 + " state.dimLayer.isDimming=" + state.dimLayer.isDimming());
Robert Carr9fe459d2016-04-07 23:32:28 -0700176 if (state.animator != null && state.animator.mWin.mWillReplaceWindow) {
177 return;
178 }
179
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700180 if (!state.continueDimming && state.dimLayer.isDimming()) {
181 state.animator = null;
Chong Zhang4c9ba52a2015-11-10 18:36:33 -0800182 dimLayerUser.getDimBounds(mTmpBounds);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700183 state.dimLayer.setBounds(mTmpBounds);
184 }
185 }
186
187 boolean animateDimLayers() {
188 int fullScreen = -1;
Filip Gruszczynskib1fa0442015-10-05 12:40:28 -0700189 int fullScreenAndDimming = -1;
190 boolean result = false;
191
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700192 for (int i = mState.size() - 1; i >= 0; i--) {
Filip Gruszczynskib1fa0442015-10-05 12:40:28 -0700193 DimLayer.DimLayerUser user = mState.keyAt(i);
Robert Carr063988c2016-03-31 18:39:40 -0700194 DimLayerState state = mState.valueAt(i);
Wale Ogunwale29bfbb82016-05-12 15:13:52 -0700195 // We have to check that we are actually the shared fullscreen layer
Robert Carr063988c2016-03-31 18:39:40 -0700196 // for this path. If we began as non fullscreen and became fullscreen
197 // (e.g. Docked stack closing), then we may not be the shared layer
198 // and we have to make sure we always animate the layer.
Wale Ogunwale29bfbb82016-05-12 15:13:52 -0700199 if (user.dimFullscreen() && state.dimLayer == mSharedFullScreenDimLayer) {
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700200 fullScreen = i;
201 if (mState.valueAt(i).continueDimming) {
Filip Gruszczynskib1fa0442015-10-05 12:40:28 -0700202 fullScreenAndDimming = i;
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700203 }
Filip Gruszczynskib1fa0442015-10-05 12:40:28 -0700204 } else {
205 // We always want to animate the non fullscreen windows, they don't share their
206 // dim layers.
207 result |= animateDimLayers(user);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700208 }
209 }
Filip Gruszczynskib1fa0442015-10-05 12:40:28 -0700210 // For the shared, full screen dim layer, we prefer the animation that is causing it to
211 // appear.
212 if (fullScreenAndDimming != -1) {
213 result |= animateDimLayers(mState.keyAt(fullScreenAndDimming));
214 } else if (fullScreen != -1) {
215 // If there is no animation for the full screen dim layer to appear, we can use any of
216 // the animators that will cause it to disappear.
217 result |= animateDimLayers(mState.keyAt(fullScreen));
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700218 }
Filip Gruszczynskib1fa0442015-10-05 12:40:28 -0700219 return result;
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700220 }
221
222 private boolean animateDimLayers(DimLayer.DimLayerUser dimLayerUser) {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800223 DimLayerState state = mState.get(dimLayerUser);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700224 if (DEBUG_DIM_LAYER) Slog.v(TAG, "animateDimLayers,"
225 + " dimLayerUser=" + dimLayerUser.toShortString()
226 + " state.animator=" + state.animator
227 + " state.continueDimming=" + state.continueDimming);
228 final int dimLayer;
229 final float dimAmount;
230 if (state.animator == null) {
231 dimLayer = state.dimLayer.getLayer();
232 dimAmount = 0;
233 } else {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800234 if (state.dimAbove) {
235 dimLayer = state.animator.mAnimLayer + LAYER_OFFSET_DIM;
236 dimAmount = DEFAULT_DIM_AMOUNT_DEAD_WINDOW;
237 } else {
238 dimLayer = state.animator.mAnimLayer - LAYER_OFFSET_DIM;
239 dimAmount = state.animator.mWin.mAttrs.dimAmount;
240 }
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700241 }
242 final float targetAlpha = state.dimLayer.getTargetAlpha();
243 if (targetAlpha != dimAmount) {
244 if (state.animator == null) {
245 state.dimLayer.hide(DEFAULT_DIM_DURATION);
246 } else {
247 long duration = (state.animator.mAnimating && state.animator.mAnimation != null)
248 ? state.animator.mAnimation.computeDurationHint()
249 : DEFAULT_DIM_DURATION;
250 if (targetAlpha > dimAmount) {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800251 duration = getDimLayerFadeDuration(duration);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700252 }
253 state.dimLayer.show(dimLayer, dimAmount, duration);
Jorim Jaggi72a6f052016-05-05 16:25:43 -0700254
255 // If we showed a dim layer, make sure to redo the layout because some things depend
256 // on whether a dim layer is showing or not.
257 if (targetAlpha == 0) {
258 mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
259 mDisplayContent.layoutNeeded = true;
260 }
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700261 }
262 } else if (state.dimLayer.getLayer() != dimLayer) {
263 state.dimLayer.setLayer(dimLayer);
264 }
265 if (state.dimLayer.isAnimating()) {
266 if (!mDisplayContent.mService.okToDisplay()) {
267 // Jump to the end of the animation.
268 state.dimLayer.show();
269 } else {
270 return state.dimLayer.stepAnimation();
271 }
272 }
273 return false;
274 }
275
276 boolean isDimming(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator winAnimator) {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800277 DimLayerState state = mState.get(dimLayerUser);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700278 return state != null && state.animator == winAnimator && state.dimLayer.isDimming();
279 }
280
Chong Zhang112eb8c2015-11-02 11:17:00 -0800281 private long getDimLayerFadeDuration(long duration) {
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700282 TypedValue tv = new TypedValue();
283 mDisplayContent.mService.mContext.getResources().getValue(
284 com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
285 if (tv.type == TypedValue.TYPE_FRACTION) {
286 duration = (long) tv.getFraction(duration, duration);
287 } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
288 duration = tv.data;
289 }
290 return duration;
291 }
292
293 void close() {
294 for (int i = mState.size() - 1; i >= 0; i--) {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800295 DimLayerState state = mState.valueAt(i);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700296 state.dimLayer.destroySurface();
297 }
298 mState.clear();
299 mSharedFullScreenDimLayer = null;
300 }
301
302 void removeDimLayerUser(DimLayer.DimLayerUser dimLayerUser) {
Chong Zhangcb1887e2015-11-04 20:59:26 -0800303 DimLayerState state = mState.get(dimLayerUser);
304 if (state != null) {
305 // Destroy the surface, unless it's the shared fullscreen dim.
306 if (state.dimLayer != mSharedFullScreenDimLayer) {
307 state.dimLayer.destroySurface();
308 }
309 mState.remove(dimLayerUser);
310 }
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700311 }
312
313 void applyDimBehind(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800314 applyDim(dimLayerUser, animator, false /* aboveApp */);
315 }
316
317 void applyDimAbove(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) {
318 applyDim(dimLayerUser, animator, true /* aboveApp */);
319 }
320
Robert Carr9fe459d2016-04-07 23:32:28 -0700321 void applyDim(
Chong Zhang112eb8c2015-11-02 11:17:00 -0800322 DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator, boolean aboveApp) {
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700323 if (dimLayerUser == null) {
324 Slog.e(TAG, "Trying to apply dim layer for: " + this
325 + ", but no dim layer user found.");
326 return;
327 }
328 if (!getContinueDimming(dimLayerUser)) {
329 setContinueDimming(dimLayerUser);
330 if (!isDimming(dimLayerUser, animator)) {
331 if (DEBUG_DIM_LAYER) Slog.v(TAG, "Win " + this + " start dimming.");
Chong Zhang112eb8c2015-11-02 11:17:00 -0800332 startDimmingIfNeeded(dimLayerUser, animator, aboveApp);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700333 }
334 }
335 }
336
Chong Zhang112eb8c2015-11-02 11:17:00 -0800337 private static class DimLayerState {
338 // The particular window requesting a dim layer. If null, hide dimLayer.
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700339 WindowStateAnimator animator;
340 // Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the
341 // end then stop any dimming.
342 boolean continueDimming;
343 DimLayer dimLayer;
Chong Zhang112eb8c2015-11-02 11:17:00 -0800344 boolean dimAbove;
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700345 }
346
347 void dump(String prefix, PrintWriter pw) {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800348 pw.println(prefix + "DimLayerController");
Wale Ogunwale29bfbb82016-05-12 15:13:52 -0700349 final String doubleSpace = " ";
350 final String prefixPlusDoubleSpace = prefix + doubleSpace;
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700351
Wale Ogunwale29bfbb82016-05-12 15:13:52 -0700352 for (int i = 0, n = mState.size(); i < n; i++) {
353 pw.println(prefixPlusDoubleSpace + mState.keyAt(i).toShortString());
354 DimLayerState state = mState.valueAt(i);
355 pw.println(prefixPlusDoubleSpace + doubleSpace + "dimLayer="
356 + (state.dimLayer == mSharedFullScreenDimLayer ? "shared" : state.dimLayer)
357 + ", animator=" + state.animator + ", continueDimming=" + state.continueDimming);
358 if (state.dimLayer != null) {
359 state.dimLayer.printTo(prefixPlusDoubleSpace + doubleSpace, pw);
360 }
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700361 }
362 }
363}