blob: 6d1cec4a9f10ebc327fc8d6d060c7fb5dc52b26f [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
13import java.io.PrintWriter;
14
15/**
16 * Centralizes the control of dim layers used for
Chong Zhang112eb8c2015-11-02 11:17:00 -080017 * {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND}
18 * as well as other use cases (such as dimming above a dead window).
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070019 */
Chong Zhang112eb8c2015-11-02 11:17:00 -080020class DimLayerController {
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -080021 private static final String TAG = TAG_WITH_CLASS_NAME ? "DimLayerController" : TAG_WM;
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070022
23 /** Amount of time in milliseconds to animate the dim surface from one value to another,
24 * when no window animation is driving it. */
25 private static final int DEFAULT_DIM_DURATION = 200;
26
Chong Zhang112eb8c2015-11-02 11:17:00 -080027 /**
28 * The default amount of dim applied over a dead window
29 */
30 private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
31
32 // Shared dim layer for fullscreen users. {@link DimLayerState#dimLayer} will point to this
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070033 // instead of creating a new object per fullscreen task on a display.
34 private DimLayer mSharedFullScreenDimLayer;
35
Chong Zhang112eb8c2015-11-02 11:17:00 -080036 private ArrayMap<DimLayer.DimLayerUser, DimLayerState> mState = new ArrayMap<>();
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070037
38 private DisplayContent mDisplayContent;
39
40 private Rect mTmpBounds = new Rect();
41
Chong Zhang112eb8c2015-11-02 11:17:00 -080042 DimLayerController(DisplayContent displayContent) {
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070043 mDisplayContent = displayContent;
44 }
45
46 /** Updates the dim layer bounds, recreating it if needed. */
47 void updateDimLayer(DimLayer.DimLayerUser dimLayerUser) {
Chong Zhang4c9ba52a2015-11-10 18:36:33 -080048 DimLayerState state = getOrCreateDimLayerState(dimLayerUser);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070049 final boolean previousFullscreen = state.dimLayer != null
50 && state.dimLayer == mSharedFullScreenDimLayer;
51 DimLayer newDimLayer;
52 final int displayId = mDisplayContent.getDisplayId();
53 if (dimLayerUser.isFullscreen()) {
54 if (previousFullscreen) {
55 // Nothing to do here...
56 return;
57 }
58 // Use shared fullscreen dim layer
59 newDimLayer = mSharedFullScreenDimLayer;
60 if (newDimLayer == null) {
61 if (state.dimLayer != null) {
62 // Re-purpose the previous dim layer.
63 newDimLayer = state.dimLayer;
64 } else {
65 // Create new full screen dim layer.
66 newDimLayer = new DimLayer(mDisplayContent.mService, dimLayerUser, displayId);
67 }
Chong Zhang4c9ba52a2015-11-10 18:36:33 -080068 dimLayerUser.getDimBounds(mTmpBounds);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070069 newDimLayer.setBounds(mTmpBounds);
70 mSharedFullScreenDimLayer = newDimLayer;
71 } else if (state.dimLayer != null) {
Chong Zhang112eb8c2015-11-02 11:17:00 -080072 state.dimLayer.destroySurface();
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070073 }
74 } else {
75 newDimLayer = (state.dimLayer == null || previousFullscreen)
76 ? new DimLayer(mDisplayContent.mService, dimLayerUser, displayId)
77 : state.dimLayer;
Chong Zhang4c9ba52a2015-11-10 18:36:33 -080078 dimLayerUser.getDimBounds(mTmpBounds);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070079 newDimLayer.setBounds(mTmpBounds);
80 }
81 state.dimLayer = newDimLayer;
82 }
83
Chong Zhang4c9ba52a2015-11-10 18:36:33 -080084 private DimLayerState getOrCreateDimLayerState(DimLayer.DimLayerUser dimLayerUser) {
Chong Zhang112eb8c2015-11-02 11:17:00 -080085 if (DEBUG_DIM_LAYER) Slog.v(TAG, "getOrCreateDimLayerState, dimLayerUser="
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070086 + dimLayerUser.toShortString());
Chong Zhang112eb8c2015-11-02 11:17:00 -080087 DimLayerState state = mState.get(dimLayerUser);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070088 if (state == null) {
Chong Zhang112eb8c2015-11-02 11:17:00 -080089 state = new DimLayerState();
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070090 mState.put(dimLayerUser, state);
91 }
92 return state;
93 }
94
95 private void setContinueDimming(DimLayer.DimLayerUser dimLayerUser) {
Chong Zhang112eb8c2015-11-02 11:17:00 -080096 DimLayerState state = mState.get(dimLayerUser);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -070097 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--) {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800107 DimLayerState state = mState.valueAt(i);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700108 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) {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800122 DimLayerState state = mState.get(dimLayerUser);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700123 return state != null && state.continueDimming;
124 }
125
126 void startDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser,
Chong Zhang112eb8c2015-11-02 11:17:00 -0800127 WindowStateAnimator newWinAnimator, boolean aboveApp) {
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700128 // 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.
Chong Zhang4c9ba52a2015-11-10 18:36:33 -0800130 DimLayerState state = getOrCreateDimLayerState(dimLayerUser);
131 state.dimAbove = aboveApp;
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700132 if (DEBUG_DIM_LAYER) Slog.v(TAG, "startDimmingIfNeeded,"
133 + " dimLayerUser=" + dimLayerUser.toShortString()
134 + " newWinAnimator=" + newWinAnimator
135 + " state.animator=" + state.animator);
Robert Carre6a83512015-11-03 16:09:21 -0800136 if (newWinAnimator.getShown() && (state.animator == null
137 || !state.animator.getShown()
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700138 || state.animator.mAnimLayer <= newWinAnimator.mAnimLayer)) {
139 state.animator = newWinAnimator;
140 if (state.animator.mWin.mAppToken == null && !dimLayerUser.isFullscreen()) {
141 // Dim should cover the entire screen for system windows.
142 mDisplayContent.getLogicalDisplayRect(mTmpBounds);
143 state.dimLayer.setBounds(mTmpBounds);
144 }
145 }
146 }
147
148 void stopDimmingIfNeeded() {
149 if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded, mState.size()=" + mState.size());
150 for (int i = mState.size() - 1; i >= 0; i--) {
151 DimLayer.DimLayerUser dimLayerUser = mState.keyAt(i);
152 stopDimmingIfNeeded(dimLayerUser);
153 }
154 }
155
156 private void stopDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser) {
157 // No need to check if state is null, we know the key has a value.
Chong Zhang112eb8c2015-11-02 11:17:00 -0800158 DimLayerState state = mState.get(dimLayerUser);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700159 if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded,"
160 + " dimLayerUser=" + dimLayerUser.toShortString()
161 + " state.continueDimming=" + state.continueDimming
162 + " state.dimLayer.isDimming=" + state.dimLayer.isDimming());
163 if (!state.continueDimming && state.dimLayer.isDimming()) {
164 state.animator = null;
Chong Zhang4c9ba52a2015-11-10 18:36:33 -0800165 dimLayerUser.getDimBounds(mTmpBounds);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700166 state.dimLayer.setBounds(mTmpBounds);
167 }
168 }
169
170 boolean animateDimLayers() {
171 int fullScreen = -1;
Filip Gruszczynskib1fa0442015-10-05 12:40:28 -0700172 int fullScreenAndDimming = -1;
173 boolean result = false;
174
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700175 for (int i = mState.size() - 1; i >= 0; i--) {
Filip Gruszczynskib1fa0442015-10-05 12:40:28 -0700176 DimLayer.DimLayerUser user = mState.keyAt(i);
177 if (user.isFullscreen()) {
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700178 fullScreen = i;
179 if (mState.valueAt(i).continueDimming) {
Filip Gruszczynskib1fa0442015-10-05 12:40:28 -0700180 fullScreenAndDimming = i;
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700181 }
Filip Gruszczynskib1fa0442015-10-05 12:40:28 -0700182 } else {
183 // We always want to animate the non fullscreen windows, they don't share their
184 // dim layers.
185 result |= animateDimLayers(user);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700186 }
187 }
Filip Gruszczynskib1fa0442015-10-05 12:40:28 -0700188 // For the shared, full screen dim layer, we prefer the animation that is causing it to
189 // appear.
190 if (fullScreenAndDimming != -1) {
191 result |= animateDimLayers(mState.keyAt(fullScreenAndDimming));
192 } else if (fullScreen != -1) {
193 // If there is no animation for the full screen dim layer to appear, we can use any of
194 // the animators that will cause it to disappear.
195 result |= animateDimLayers(mState.keyAt(fullScreen));
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700196 }
Filip Gruszczynskib1fa0442015-10-05 12:40:28 -0700197 return result;
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700198 }
199
200 private boolean animateDimLayers(DimLayer.DimLayerUser dimLayerUser) {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800201 DimLayerState state = mState.get(dimLayerUser);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700202 if (DEBUG_DIM_LAYER) Slog.v(TAG, "animateDimLayers,"
203 + " dimLayerUser=" + dimLayerUser.toShortString()
204 + " state.animator=" + state.animator
205 + " state.continueDimming=" + state.continueDimming);
206 final int dimLayer;
207 final float dimAmount;
208 if (state.animator == null) {
209 dimLayer = state.dimLayer.getLayer();
210 dimAmount = 0;
211 } else {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800212 if (state.dimAbove) {
213 dimLayer = state.animator.mAnimLayer + LAYER_OFFSET_DIM;
214 dimAmount = DEFAULT_DIM_AMOUNT_DEAD_WINDOW;
215 } else {
216 dimLayer = state.animator.mAnimLayer - LAYER_OFFSET_DIM;
217 dimAmount = state.animator.mWin.mAttrs.dimAmount;
218 }
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700219 }
220 final float targetAlpha = state.dimLayer.getTargetAlpha();
221 if (targetAlpha != dimAmount) {
222 if (state.animator == null) {
223 state.dimLayer.hide(DEFAULT_DIM_DURATION);
224 } else {
225 long duration = (state.animator.mAnimating && state.animator.mAnimation != null)
226 ? state.animator.mAnimation.computeDurationHint()
227 : DEFAULT_DIM_DURATION;
228 if (targetAlpha > dimAmount) {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800229 duration = getDimLayerFadeDuration(duration);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700230 }
231 state.dimLayer.show(dimLayer, dimAmount, duration);
232 }
233 } else if (state.dimLayer.getLayer() != dimLayer) {
234 state.dimLayer.setLayer(dimLayer);
235 }
236 if (state.dimLayer.isAnimating()) {
237 if (!mDisplayContent.mService.okToDisplay()) {
238 // Jump to the end of the animation.
239 state.dimLayer.show();
240 } else {
241 return state.dimLayer.stepAnimation();
242 }
243 }
244 return false;
245 }
246
247 boolean isDimming(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator winAnimator) {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800248 DimLayerState state = mState.get(dimLayerUser);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700249 return state != null && state.animator == winAnimator && state.dimLayer.isDimming();
250 }
251
Chong Zhang112eb8c2015-11-02 11:17:00 -0800252 private long getDimLayerFadeDuration(long duration) {
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700253 TypedValue tv = new TypedValue();
254 mDisplayContent.mService.mContext.getResources().getValue(
255 com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
256 if (tv.type == TypedValue.TYPE_FRACTION) {
257 duration = (long) tv.getFraction(duration, duration);
258 } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
259 duration = tv.data;
260 }
261 return duration;
262 }
263
264 void close() {
265 for (int i = mState.size() - 1; i >= 0; i--) {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800266 DimLayerState state = mState.valueAt(i);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700267 state.dimLayer.destroySurface();
268 }
269 mState.clear();
270 mSharedFullScreenDimLayer = null;
271 }
272
273 void removeDimLayerUser(DimLayer.DimLayerUser dimLayerUser) {
Chong Zhangcb1887e2015-11-04 20:59:26 -0800274 DimLayerState state = mState.get(dimLayerUser);
275 if (state != null) {
276 // Destroy the surface, unless it's the shared fullscreen dim.
277 if (state.dimLayer != mSharedFullScreenDimLayer) {
278 state.dimLayer.destroySurface();
279 }
280 mState.remove(dimLayerUser);
281 }
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700282 }
283
284 void applyDimBehind(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800285 applyDim(dimLayerUser, animator, false /* aboveApp */);
286 }
287
288 void applyDimAbove(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) {
289 applyDim(dimLayerUser, animator, true /* aboveApp */);
290 }
291
292 private void applyDim(
293 DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator, boolean aboveApp) {
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700294 if (dimLayerUser == null) {
295 Slog.e(TAG, "Trying to apply dim layer for: " + this
296 + ", but no dim layer user found.");
297 return;
298 }
299 if (!getContinueDimming(dimLayerUser)) {
300 setContinueDimming(dimLayerUser);
301 if (!isDimming(dimLayerUser, animator)) {
302 if (DEBUG_DIM_LAYER) Slog.v(TAG, "Win " + this + " start dimming.");
Chong Zhang112eb8c2015-11-02 11:17:00 -0800303 startDimmingIfNeeded(dimLayerUser, animator, aboveApp);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700304 }
305 }
306 }
307
Chong Zhang112eb8c2015-11-02 11:17:00 -0800308 private static class DimLayerState {
309 // The particular window requesting a dim layer. If null, hide dimLayer.
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700310 WindowStateAnimator animator;
311 // Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the
312 // end then stop any dimming.
313 boolean continueDimming;
314 DimLayer dimLayer;
Chong Zhang112eb8c2015-11-02 11:17:00 -0800315 boolean dimAbove;
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700316 }
317
318 void dump(String prefix, PrintWriter pw) {
Chong Zhang112eb8c2015-11-02 11:17:00 -0800319 pw.println(prefix + "DimLayerController");
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700320 for (int i = 0, n = mState.size(); i < n; i++) {
321 pw.println(prefix + " " + mState.keyAt(i).toShortString());
322 pw.print(prefix + " ");
Chong Zhang112eb8c2015-11-02 11:17:00 -0800323 DimLayerState state = mState.valueAt(i);
Filip Gruszczynski0689ae92015-10-01 12:30:31 -0700324 pw.print("dimLayer=" + (state.dimLayer == mSharedFullScreenDimLayer ? "shared" :
325 state.dimLayer));
326 pw.print(", animator=" + state.animator);
327 pw.println(", continueDimming=" + state.continueDimming + "}");
328
329 }
330 }
331}