blob: 3ec02b9f66fc7a2f78f673448ae8b4c749fab5cc [file] [log] [blame]
Filip Gruszczynski0689ae92015-10-01 12:30:31 -07001package com.android.server.wm;
2
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -08003import 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;
Chong Zhang112eb8c2015-11-02 11:17:00 -08006import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM;
Filip Gruszczynski0689ae92015-10-01 12:30:31 -07007
8import android.graphics.Rect;
9import android.util.ArrayMap;
10import android.util.Slog;
11import android.util.TypedValue;
12
Jorim Jaggibc5425c2016-03-01 13:51:16 +010013import com.android.server.wm.DimLayer.DimLayerUser;
14
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070015import java.io.PrintWriter;
16
17/**
18 * Centralizes the control of dim layers used for
Chong Zhang112eb8c2015-11-02 11:17:00 -080019 * {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND}
20 * as well as other use cases (such as dimming above a dead window).
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070021 */
Chong Zhang112eb8c2015-11-02 11:17:00 -080022class DimLayerController {
Jorim Jaggibc5425c2016-03-01 13:51:16 +010023 private static final String TAG_LOCAL = "DimLayerController";
24 private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM;
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070025
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
Chong Zhang112eb8c2015-11-02 11:17:00 -080030 /**
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
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070036 // instead of creating a new object per fullscreen task on a display.
37 private DimLayer mSharedFullScreenDimLayer;
38
Chong Zhang112eb8c2015-11-02 11:17:00 -080039 private ArrayMap<DimLayer.DimLayerUser, DimLayerState> mState = new ArrayMap<>();
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070040
41 private DisplayContent mDisplayContent;
42
43 private Rect mTmpBounds = new Rect();
44
Chong Zhang112eb8c2015-11-02 11:17:00 -080045 DimLayerController(DisplayContent displayContent) {
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070046 mDisplayContent = displayContent;
47 }
48
49 /** Updates the dim layer bounds, recreating it if needed. */
50 void updateDimLayer(DimLayer.DimLayerUser dimLayerUser) {
Chong Zhang4c9ba52a2015-11-10 18:36:33 -080051 DimLayerState state = getOrCreateDimLayerState(dimLayerUser);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070052 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.
Jorim Jaggibc5425c2016-03-01 13:51:16 +010069 newDimLayer = new DimLayer(mDisplayContent.mService, dimLayerUser, displayId,
70 getDimLayerTag(dimLayerUser));
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070071 }
Chong Zhang4c9ba52a2015-11-10 18:36:33 -080072 dimLayerUser.getDimBounds(mTmpBounds);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070073 newDimLayer.setBounds(mTmpBounds);
74 mSharedFullScreenDimLayer = newDimLayer;
75 } else if (state.dimLayer != null) {
Chong Zhang112eb8c2015-11-02 11:17:00 -080076 state.dimLayer.destroySurface();
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070077 }
78 } else {
79 newDimLayer = (state.dimLayer == null || previousFullscreen)
Jorim Jaggibc5425c2016-03-01 13:51:16 +010080 ? new DimLayer(mDisplayContent.mService, dimLayerUser, displayId,
81 getDimLayerTag(dimLayerUser))
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070082 : state.dimLayer;
Chong Zhang4c9ba52a2015-11-10 18:36:33 -080083 dimLayerUser.getDimBounds(mTmpBounds);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070084 newDimLayer.setBounds(mTmpBounds);
85 }
86 state.dimLayer = newDimLayer;
87 }
88
Jorim Jaggibc5425c2016-03-01 13:51:16 +010089 private static String getDimLayerTag(DimLayerUser dimLayerUser) {
90 return TAG_LOCAL + "/" + dimLayerUser.toShortString();
91 }
92
Chong Zhang4c9ba52a2015-11-10 18:36:33 -080093 private DimLayerState getOrCreateDimLayerState(DimLayer.DimLayerUser dimLayerUser) {
Chong Zhang112eb8c2015-11-02 11:17:00 -080094 if (DEBUG_DIM_LAYER) Slog.v(TAG, "getOrCreateDimLayerState, dimLayerUser="
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070095 + dimLayerUser.toShortString());
Chong Zhang112eb8c2015-11-02 11:17:00 -080096 DimLayerState state = mState.get(dimLayerUser);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070097 if (state == null) {
Chong Zhang112eb8c2015-11-02 11:17:00 -080098 state = new DimLayerState();
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070099 mState.put(dimLayerUser, state);
100 }
101 return state;
102 }
103
104 private void setContinueDimming(DimLayer.DimLayerUser dimLayerUser) {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800105 DimLayerState state = mState.get(dimLayerUser);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700106 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--) {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800116 DimLayerState state = mState.valueAt(i);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700117 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) {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800131 DimLayerState state = mState.get(dimLayerUser);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700132 return state != null && state.continueDimming;
133 }
134
135 void startDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser,
Chong Zhang112eb8c2015-11-02 11:17:00 -0800136 WindowStateAnimator newWinAnimator, boolean aboveApp) {
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700137 // 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.
Chong Zhang4c9ba52a2015-11-10 18:36:33 -0800139 DimLayerState state = getOrCreateDimLayerState(dimLayerUser);
140 state.dimAbove = aboveApp;
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700141 if (DEBUG_DIM_LAYER) Slog.v(TAG, "startDimmingIfNeeded,"
142 + " dimLayerUser=" + dimLayerUser.toShortString()
143 + " newWinAnimator=" + newWinAnimator
144 + " state.animator=" + state.animator);
Robert Carre6a83512015-11-03 16:09:21 -0800145 if (newWinAnimator.getShown() && (state.animator == null
146 || !state.animator.getShown()
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700147 || 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.
Chong Zhang112eb8c2015-11-02 11:17:00 -0800167 DimLayerState state = mState.get(dimLayerUser);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700168 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.continueDimming && state.dimLayer.isDimming()) {
173 state.animator = null;
Chong Zhang4c9ba52a2015-11-10 18:36:33 -0800174 dimLayerUser.getDimBounds(mTmpBounds);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700175 state.dimLayer.setBounds(mTmpBounds);
176 }
177 }
178
179 boolean animateDimLayers() {
180 int fullScreen = -1;
Filip Gruszczynskib1fa0442015-10-05 12:40:28 -0700181 int fullScreenAndDimming = -1;
182 boolean result = false;
183
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700184 for (int i = mState.size() - 1; i >= 0; i--) {
Filip Gruszczynskib1fa0442015-10-05 12:40:28 -0700185 DimLayer.DimLayerUser user = mState.keyAt(i);
Robert Carr063988c2016-03-31 18:39:40 -0700186 DimLayerState state = mState.valueAt(i);
187 // We have to check that we are acutally the shared fullscreen layer
188 // for this path. If we began as non fullscreen and became fullscreen
189 // (e.g. Docked stack closing), then we may not be the shared layer
190 // and we have to make sure we always animate the layer.
191 if (user.isFullscreen() && state.dimLayer == mSharedFullScreenDimLayer) {
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700192 fullScreen = i;
193 if (mState.valueAt(i).continueDimming) {
Filip Gruszczynskib1fa0442015-10-05 12:40:28 -0700194 fullScreenAndDimming = i;
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700195 }
Filip Gruszczynskib1fa0442015-10-05 12:40:28 -0700196 } else {
197 // We always want to animate the non fullscreen windows, they don't share their
198 // dim layers.
199 result |= animateDimLayers(user);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700200 }
201 }
Filip Gruszczynskib1fa0442015-10-05 12:40:28 -0700202 // For the shared, full screen dim layer, we prefer the animation that is causing it to
203 // appear.
204 if (fullScreenAndDimming != -1) {
205 result |= animateDimLayers(mState.keyAt(fullScreenAndDimming));
206 } else if (fullScreen != -1) {
207 // If there is no animation for the full screen dim layer to appear, we can use any of
208 // the animators that will cause it to disappear.
209 result |= animateDimLayers(mState.keyAt(fullScreen));
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700210 }
Filip Gruszczynskib1fa0442015-10-05 12:40:28 -0700211 return result;
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700212 }
213
214 private boolean animateDimLayers(DimLayer.DimLayerUser dimLayerUser) {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800215 DimLayerState state = mState.get(dimLayerUser);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700216 if (DEBUG_DIM_LAYER) Slog.v(TAG, "animateDimLayers,"
217 + " dimLayerUser=" + dimLayerUser.toShortString()
218 + " state.animator=" + state.animator
219 + " state.continueDimming=" + state.continueDimming);
220 final int dimLayer;
221 final float dimAmount;
222 if (state.animator == null) {
223 dimLayer = state.dimLayer.getLayer();
224 dimAmount = 0;
225 } else {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800226 if (state.dimAbove) {
227 dimLayer = state.animator.mAnimLayer + LAYER_OFFSET_DIM;
228 dimAmount = DEFAULT_DIM_AMOUNT_DEAD_WINDOW;
229 } else {
230 dimLayer = state.animator.mAnimLayer - LAYER_OFFSET_DIM;
231 dimAmount = state.animator.mWin.mAttrs.dimAmount;
232 }
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700233 }
234 final float targetAlpha = state.dimLayer.getTargetAlpha();
235 if (targetAlpha != dimAmount) {
236 if (state.animator == null) {
237 state.dimLayer.hide(DEFAULT_DIM_DURATION);
238 } else {
239 long duration = (state.animator.mAnimating && state.animator.mAnimation != null)
240 ? state.animator.mAnimation.computeDurationHint()
241 : DEFAULT_DIM_DURATION;
242 if (targetAlpha > dimAmount) {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800243 duration = getDimLayerFadeDuration(duration);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700244 }
245 state.dimLayer.show(dimLayer, dimAmount, duration);
246 }
247 } else if (state.dimLayer.getLayer() != dimLayer) {
248 state.dimLayer.setLayer(dimLayer);
249 }
250 if (state.dimLayer.isAnimating()) {
251 if (!mDisplayContent.mService.okToDisplay()) {
252 // Jump to the end of the animation.
253 state.dimLayer.show();
254 } else {
255 return state.dimLayer.stepAnimation();
256 }
257 }
258 return false;
259 }
260
261 boolean isDimming(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator winAnimator) {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800262 DimLayerState state = mState.get(dimLayerUser);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700263 return state != null && state.animator == winAnimator && state.dimLayer.isDimming();
264 }
265
Chong Zhang112eb8c2015-11-02 11:17:00 -0800266 private long getDimLayerFadeDuration(long duration) {
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700267 TypedValue tv = new TypedValue();
268 mDisplayContent.mService.mContext.getResources().getValue(
269 com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
270 if (tv.type == TypedValue.TYPE_FRACTION) {
271 duration = (long) tv.getFraction(duration, duration);
272 } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
273 duration = tv.data;
274 }
275 return duration;
276 }
277
278 void close() {
279 for (int i = mState.size() - 1; i >= 0; i--) {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800280 DimLayerState state = mState.valueAt(i);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700281 state.dimLayer.destroySurface();
282 }
283 mState.clear();
284 mSharedFullScreenDimLayer = null;
285 }
286
287 void removeDimLayerUser(DimLayer.DimLayerUser dimLayerUser) {
Chong Zhangcb1887e2015-11-04 20:59:26 -0800288 DimLayerState state = mState.get(dimLayerUser);
289 if (state != null) {
290 // Destroy the surface, unless it's the shared fullscreen dim.
291 if (state.dimLayer != mSharedFullScreenDimLayer) {
292 state.dimLayer.destroySurface();
293 }
294 mState.remove(dimLayerUser);
295 }
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700296 }
297
298 void applyDimBehind(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800299 applyDim(dimLayerUser, animator, false /* aboveApp */);
300 }
301
302 void applyDimAbove(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) {
303 applyDim(dimLayerUser, animator, true /* aboveApp */);
304 }
305
306 private void applyDim(
307 DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator, boolean aboveApp) {
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700308 if (dimLayerUser == null) {
309 Slog.e(TAG, "Trying to apply dim layer for: " + this
310 + ", but no dim layer user found.");
311 return;
312 }
313 if (!getContinueDimming(dimLayerUser)) {
314 setContinueDimming(dimLayerUser);
315 if (!isDimming(dimLayerUser, animator)) {
316 if (DEBUG_DIM_LAYER) Slog.v(TAG, "Win " + this + " start dimming.");
Chong Zhang112eb8c2015-11-02 11:17:00 -0800317 startDimmingIfNeeded(dimLayerUser, animator, aboveApp);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700318 }
319 }
320 }
321
Chong Zhang112eb8c2015-11-02 11:17:00 -0800322 private static class DimLayerState {
323 // The particular window requesting a dim layer. If null, hide dimLayer.
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700324 WindowStateAnimator animator;
325 // Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the
326 // end then stop any dimming.
327 boolean continueDimming;
328 DimLayer dimLayer;
Chong Zhang112eb8c2015-11-02 11:17:00 -0800329 boolean dimAbove;
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700330 }
331
332 void dump(String prefix, PrintWriter pw) {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800333 pw.println(prefix + "DimLayerController");
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700334 for (int i = 0, n = mState.size(); i < n; i++) {
335 pw.println(prefix + " " + mState.keyAt(i).toShortString());
336 pw.print(prefix + " ");
Chong Zhang112eb8c2015-11-02 11:17:00 -0800337 DimLayerState state = mState.valueAt(i);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700338 pw.print("dimLayer=" + (state.dimLayer == mSharedFullScreenDimLayer ? "shared" :
339 state.dimLayer));
340 pw.print(", animator=" + state.animator);
341 pw.println(", continueDimming=" + state.continueDimming + "}");
342
343 }
344 }
345}