blob: 2bda80d99328cb2233b55358c2f94cab617e445b [file] [log] [blame]
Svetoslav8e3feb12014-02-24 13:46:47 -08001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wm;
18
Phil Weaverd321075e2017-06-13 09:13:35 -070019import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
Robert Carr132c9f52017-07-31 17:02:30 -070020import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
Phil Weaverd321075e2017-06-13 09:13:35 -070021
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -080022import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
23import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
24
Svetoslav8e3feb12014-02-24 13:46:47 -080025import android.animation.ObjectAnimator;
26import android.animation.ValueAnimator;
Alan Viverette59e53a12016-03-28 13:41:32 -040027import android.annotation.NonNull;
Svetoslav8e3feb12014-02-24 13:46:47 -080028import android.app.Service;
29import android.content.Context;
30import android.graphics.Canvas;
31import android.graphics.Color;
32import android.graphics.Matrix;
33import android.graphics.Paint;
34import android.graphics.Path;
35import android.graphics.PixelFormat;
36import android.graphics.Point;
37import android.graphics.PorterDuff.Mode;
38import android.graphics.Rect;
39import android.graphics.RectF;
40import android.graphics.Region;
41import android.os.Handler;
42import android.os.IBinder;
43import android.os.Looper;
44import android.os.Message;
Phil Weaver396d5492016-03-22 17:53:50 -070045import android.text.TextUtils;
Svetoslav8e3feb12014-02-24 13:46:47 -080046import android.util.ArraySet;
47import android.util.Log;
48import android.util.Slog;
49import android.util.SparseArray;
50import android.util.TypedValue;
51import android.view.MagnificationSpec;
52import android.view.Surface;
53import android.view.Surface.OutOfResourcesException;
54import android.view.SurfaceControl;
Svetoslavf7174e82014-06-12 11:29:35 -070055import android.view.ViewConfiguration;
Svetoslav8e3feb12014-02-24 13:46:47 -080056import android.view.WindowInfo;
57import android.view.WindowManager;
Svetoslav8e3feb12014-02-24 13:46:47 -080058import android.view.animation.DecelerateInterpolator;
59import android.view.animation.Interpolator;
60
61import com.android.internal.R;
62import com.android.internal.os.SomeArgs;
Adrian Roose99bc052017-11-20 17:55:31 +010063import com.android.server.policy.WindowManagerPolicy;
64import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks;
65import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback;
Svetoslav8e3feb12014-02-24 13:46:47 -080066
67import java.util.ArrayList;
Allen Hairf20ac2c2016-02-11 17:42:59 -080068import java.util.HashSet;
Svetoslav8e3feb12014-02-24 13:46:47 -080069import java.util.List;
70import java.util.Set;
71
72/**
73 * This class contains the accessibility related logic of the window manger.
74 */
75final class AccessibilityController {
76
Robert Carre625fcf2017-09-01 12:36:28 -070077 private final WindowManagerService mService;
Svetoslav8e3feb12014-02-24 13:46:47 -080078
79 private static final float[] sTempFloats = new float[9];
80
81 public AccessibilityController(WindowManagerService service) {
Robert Carre625fcf2017-09-01 12:36:28 -070082 mService = service;
Svetoslav8e3feb12014-02-24 13:46:47 -080083 }
84
85 private DisplayMagnifier mDisplayMagnifier;
86
87 private WindowsForAccessibilityObserver mWindowsForAccessibilityObserver;
88
89 public void setMagnificationCallbacksLocked(MagnificationCallbacks callbacks) {
90 if (callbacks != null) {
91 if (mDisplayMagnifier != null) {
92 throw new IllegalStateException("Magnification callbacks already set!");
93 }
Robert Carre625fcf2017-09-01 12:36:28 -070094 mDisplayMagnifier = new DisplayMagnifier(mService, callbacks);
Svetoslav8e3feb12014-02-24 13:46:47 -080095 } else {
96 if (mDisplayMagnifier == null) {
97 throw new IllegalStateException("Magnification callbacks already cleared!");
98 }
99 mDisplayMagnifier.destroyLocked();
100 mDisplayMagnifier = null;
101 }
102 }
103
104 public void setWindowsForAccessibilityCallback(WindowsForAccessibilityCallback callback) {
105 if (callback != null) {
106 if (mWindowsForAccessibilityObserver != null) {
107 throw new IllegalStateException(
108 "Windows for accessibility callback already set!");
109 }
110 mWindowsForAccessibilityObserver = new WindowsForAccessibilityObserver(
Robert Carre625fcf2017-09-01 12:36:28 -0700111 mService, callback);
Svetoslav8e3feb12014-02-24 13:46:47 -0800112 } else {
113 if (mWindowsForAccessibilityObserver == null) {
114 throw new IllegalStateException(
115 "Windows for accessibility callback already cleared!");
116 }
117 mWindowsForAccessibilityObserver = null;
118 }
119 }
120
Svetoslav Ganovfd138892016-07-13 18:20:42 -0700121 public void performComputeChangedWindowsNotLocked() {
122 WindowsForAccessibilityObserver observer = null;
Robert Carre625fcf2017-09-01 12:36:28 -0700123 synchronized (mService) {
Svetoslav Ganovfd138892016-07-13 18:20:42 -0700124 observer = mWindowsForAccessibilityObserver;
125 }
126 if (observer != null) {
127 observer.performComputeChangedWindowsNotLocked();
128 }
129 }
130
Svetoslav8e3feb12014-02-24 13:46:47 -0800131 public void setMagnificationSpecLocked(MagnificationSpec spec) {
132 if (mDisplayMagnifier != null) {
133 mDisplayMagnifier.setMagnificationSpecLocked(spec);
134 }
135 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700136 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800137 }
138 }
139
Phil Weaver70439242016-03-10 15:15:49 -0800140 public void getMagnificationRegionLocked(Region outMagnificationRegion) {
Alan Viverette59e53a12016-03-28 13:41:32 -0400141 if (mDisplayMagnifier != null) {
Phil Weaver70439242016-03-10 15:15:49 -0800142 mDisplayMagnifier.getMagnificationRegionLocked(outMagnificationRegion);
Alan Viverette59e53a12016-03-28 13:41:32 -0400143 }
144 }
145
Svetoslavf7174e82014-06-12 11:29:35 -0700146 public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800147 if (mDisplayMagnifier != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700148 mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle);
Svetoslav8e3feb12014-02-24 13:46:47 -0800149 }
150 // Not relevant for the window observer.
151 }
152
153 public void onWindowLayersChangedLocked() {
154 if (mDisplayMagnifier != null) {
155 mDisplayMagnifier.onWindowLayersChangedLocked();
156 }
157 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700158 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800159 }
160 }
161
Andrii Kulian8ee72852017-03-10 10:36:45 -0800162 public void onRotationChangedLocked(DisplayContent displayContent) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800163 if (mDisplayMagnifier != null) {
Andrii Kulian8ee72852017-03-10 10:36:45 -0800164 mDisplayMagnifier.onRotationChangedLocked(displayContent);
Svetoslav8e3feb12014-02-24 13:46:47 -0800165 }
166 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700167 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800168 }
169 }
170
171 public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
172 if (mDisplayMagnifier != null) {
173 mDisplayMagnifier.onAppWindowTransitionLocked(windowState, transition);
174 }
175 // Not relevant for the window observer.
176 }
177
178 public void onWindowTransitionLocked(WindowState windowState, int transition) {
179 if (mDisplayMagnifier != null) {
180 mDisplayMagnifier.onWindowTransitionLocked(windowState, transition);
181 }
182 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700183 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800184 }
185 }
186
Svetoslav3a0d8782014-12-04 12:50:11 -0800187 public void onWindowFocusChangedNotLocked() {
Svetoslav8e3feb12014-02-24 13:46:47 -0800188 // Not relevant for the display magnifier.
189
Svetoslav3a0d8782014-12-04 12:50:11 -0800190 WindowsForAccessibilityObserver observer = null;
Robert Carre625fcf2017-09-01 12:36:28 -0700191 synchronized (mService) {
Svetoslav3a0d8782014-12-04 12:50:11 -0800192 observer = mWindowsForAccessibilityObserver;
193 }
194 if (observer != null) {
195 observer.performComputeChangedWindowsNotLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800196 }
197 }
198
Svetoslav4604abc2014-06-10 18:59:30 -0700199
Svetoslavf7174e82014-06-12 11:29:35 -0700200 public void onSomeWindowResizedOrMovedLocked() {
Svetoslav4604abc2014-06-10 18:59:30 -0700201 // Not relevant for the display magnifier.
202
203 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700204 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav4604abc2014-06-10 18:59:30 -0700205 }
206 }
207
Svetoslav8e3feb12014-02-24 13:46:47 -0800208 /** NOTE: This has to be called within a surface transaction. */
209 public void drawMagnifiedRegionBorderIfNeededLocked() {
210 if (mDisplayMagnifier != null) {
211 mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked();
212 }
213 // Not relevant for the window observer.
214 }
215
216 public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
217 if (mDisplayMagnifier != null) {
218 return mDisplayMagnifier.getMagnificationSpecForWindowLocked(windowState);
219 }
220 return null;
221 }
222
223 public boolean hasCallbacksLocked() {
224 return (mDisplayMagnifier != null
225 || mWindowsForAccessibilityObserver != null);
226 }
227
Casey Burkhardt74922c62017-02-13 12:43:16 -0800228 public void setForceShowMagnifiableBoundsLocked(boolean show) {
229 if (mDisplayMagnifier != null) {
230 mDisplayMagnifier.setForceShowMagnifiableBoundsLocked(show);
Phil Weaver251db072017-03-28 08:35:38 -0700231 mDisplayMagnifier.showMagnificationBoundsIfNeeded();
Casey Burkhardt74922c62017-02-13 12:43:16 -0800232 }
233 }
234
Svetoslav8e3feb12014-02-24 13:46:47 -0800235 private static void populateTransformationMatrixLocked(WindowState windowState,
236 Matrix outMatrix) {
Jorim Jaggieb0d3bc2017-12-15 14:56:19 +0100237 windowState.getTransformationMatrix(sTempFloats, outMatrix);
Svetoslav8e3feb12014-02-24 13:46:47 -0800238 }
239
240 /**
241 * This class encapsulates the functionality related to display magnification.
242 */
243 private static final class DisplayMagnifier {
244
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -0800245 private static final String LOG_TAG = TAG_WITH_CLASS_NAME ? "DisplayMagnifier" : TAG_WM;
Svetoslav8e3feb12014-02-24 13:46:47 -0800246
247 private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
248 private static final boolean DEBUG_ROTATION = false;
249 private static final boolean DEBUG_LAYERS = false;
250 private static final boolean DEBUG_RECTANGLE_REQUESTED = false;
251 private static final boolean DEBUG_VIEWPORT_WINDOW = false;
252
253 private final Rect mTempRect1 = new Rect();
254 private final Rect mTempRect2 = new Rect();
255
256 private final Region mTempRegion1 = new Region();
257 private final Region mTempRegion2 = new Region();
258 private final Region mTempRegion3 = new Region();
259 private final Region mTempRegion4 = new Region();
260
261 private final Context mContext;
Robert Carre625fcf2017-09-01 12:36:28 -0700262 private final WindowManagerService mService;
Svetoslav8e3feb12014-02-24 13:46:47 -0800263 private final MagnifiedViewport mMagnifedViewport;
264 private final Handler mHandler;
265
266 private final MagnificationCallbacks mCallbacks;
267
268 private final long mLongAnimationDuration;
269
Casey Burkhardt74922c62017-02-13 12:43:16 -0800270 private boolean mForceShowMagnifiableBounds = false;
271
Svetoslav8e3feb12014-02-24 13:46:47 -0800272 public DisplayMagnifier(WindowManagerService windowManagerService,
273 MagnificationCallbacks callbacks) {
274 mContext = windowManagerService.mContext;
Robert Carre625fcf2017-09-01 12:36:28 -0700275 mService = windowManagerService;
Svetoslav8e3feb12014-02-24 13:46:47 -0800276 mCallbacks = callbacks;
Robert Carre625fcf2017-09-01 12:36:28 -0700277 mHandler = new MyHandler(mService.mH.getLooper());
Svetoslav8e3feb12014-02-24 13:46:47 -0800278 mMagnifedViewport = new MagnifiedViewport();
279 mLongAnimationDuration = mContext.getResources().getInteger(
280 com.android.internal.R.integer.config_longAnimTime);
281 }
282
283 public void setMagnificationSpecLocked(MagnificationSpec spec) {
284 mMagnifedViewport.updateMagnificationSpecLocked(spec);
285 mMagnifedViewport.recomputeBoundsLocked();
Robert Carrb1579c82017-09-05 14:54:47 -0700286
287 mService.applyMagnificationSpec(spec);
Robert Carre625fcf2017-09-01 12:36:28 -0700288 mService.scheduleAnimationLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800289 }
290
Casey Burkhardt74922c62017-02-13 12:43:16 -0800291 public void setForceShowMagnifiableBoundsLocked(boolean show) {
292 mForceShowMagnifiableBounds = show;
293 mMagnifedViewport.setMagnifiedRegionBorderShownLocked(show, true);
294 }
295
296 public boolean isForceShowingMagnifiableBoundsLocked() {
297 return mForceShowMagnifiableBounds;
298 }
299
Svetoslavf7174e82014-06-12 11:29:35 -0700300 public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800301 if (DEBUG_RECTANGLE_REQUESTED) {
302 Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
303 }
304 if (!mMagnifedViewport.isMagnifyingLocked()) {
305 return;
306 }
307 Rect magnifiedRegionBounds = mTempRect2;
308 mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds);
309 if (magnifiedRegionBounds.contains(rectangle)) {
310 return;
311 }
312 SomeArgs args = SomeArgs.obtain();
313 args.argi1 = rectangle.left;
314 args.argi2 = rectangle.top;
315 args.argi3 = rectangle.right;
316 args.argi4 = rectangle.bottom;
317 mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED,
318 args).sendToTarget();
319 }
320
321 public void onWindowLayersChangedLocked() {
322 if (DEBUG_LAYERS) {
323 Slog.i(LOG_TAG, "Layers changed.");
324 }
325 mMagnifedViewport.recomputeBoundsLocked();
Robert Carre625fcf2017-09-01 12:36:28 -0700326 mService.scheduleAnimationLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800327 }
328
Andrii Kulian8ee72852017-03-10 10:36:45 -0800329 public void onRotationChangedLocked(DisplayContent displayContent) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800330 if (DEBUG_ROTATION) {
Andrii Kulian8ee72852017-03-10 10:36:45 -0800331 final int rotation = displayContent.getRotation();
332 Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation)
Svetoslav8e3feb12014-02-24 13:46:47 -0800333 + " displayId: " + displayContent.getDisplayId());
334 }
335 mMagnifedViewport.onRotationChangedLocked();
336 mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
337 }
338
339 public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
340 if (DEBUG_WINDOW_TRANSITIONS) {
341 Slog.i(LOG_TAG, "Window transition: "
342 + AppTransition.appTransitionToString(transition)
343 + " displayId: " + windowState.getDisplayId());
344 }
345 final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
346 if (magnifying) {
347 switch (transition) {
348 case AppTransition.TRANSIT_ACTIVITY_OPEN:
349 case AppTransition.TRANSIT_TASK_OPEN:
350 case AppTransition.TRANSIT_TASK_TO_FRONT:
351 case AppTransition.TRANSIT_WALLPAPER_OPEN:
352 case AppTransition.TRANSIT_WALLPAPER_CLOSE:
353 case AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN: {
354 mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED);
355 }
356 }
357 }
358 }
359
360 public void onWindowTransitionLocked(WindowState windowState, int transition) {
361 if (DEBUG_WINDOW_TRANSITIONS) {
362 Slog.i(LOG_TAG, "Window transition: "
363 + AppTransition.appTransitionToString(transition)
364 + " displayId: " + windowState.getDisplayId());
365 }
366 final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
367 final int type = windowState.mAttrs.type;
368 switch (transition) {
369 case WindowManagerPolicy.TRANSIT_ENTER:
370 case WindowManagerPolicy.TRANSIT_SHOW: {
371 if (!magnifying) {
372 break;
373 }
374 switch (type) {
375 case WindowManager.LayoutParams.TYPE_APPLICATION:
Chong Zhangfea963e2016-08-15 17:14:16 -0700376 case WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION:
Svetoslav8e3feb12014-02-24 13:46:47 -0800377 case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
378 case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
379 case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
Wale Ogunwale0a4dc222015-04-14 12:58:42 -0700380 case WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL:
Svetoslav8e3feb12014-02-24 13:46:47 -0800381 case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
382 case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
383 case WindowManager.LayoutParams.TYPE_PHONE:
384 case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
385 case WindowManager.LayoutParams.TYPE_TOAST:
386 case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
Wale Ogunwale5cd907d2017-01-26 14:14:08 -0800387 case WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY:
Svetoslav8e3feb12014-02-24 13:46:47 -0800388 case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
389 case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
390 case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
391 case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
392 case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
Jason Monk8f7f3182015-11-18 16:35:14 -0500393 case WindowManager.LayoutParams.TYPE_QS_DIALOG:
Adrian Roos9a645132014-10-08 02:59:56 +0200394 case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: {
Svetoslav8e3feb12014-02-24 13:46:47 -0800395 Rect magnifiedRegionBounds = mTempRect2;
396 mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(
397 magnifiedRegionBounds);
398 Rect touchableRegionBounds = mTempRect1;
399 windowState.getTouchableRegion(mTempRegion1);
400 mTempRegion1.getBounds(touchableRegionBounds);
401 if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) {
402 mCallbacks.onRectangleOnScreenRequested(
403 touchableRegionBounds.left,
404 touchableRegionBounds.top,
405 touchableRegionBounds.right,
406 touchableRegionBounds.bottom);
407 }
408 } break;
409 } break;
410 }
411 }
412 }
413
414 public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
415 MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
416 if (spec != null && !spec.isNop()) {
Robert Carrb1579c82017-09-05 14:54:47 -0700417 if (!windowState.shouldMagnify()) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800418 return null;
419 }
420 }
421 return spec;
422 }
423
Phil Weaver70439242016-03-10 15:15:49 -0800424 public void getMagnificationRegionLocked(Region outMagnificationRegion) {
Phil Weaver53b690b2017-08-14 17:42:39 -0700425 // Make sure we're working with the most current bounds
426 mMagnifedViewport.recomputeBoundsLocked();
Phil Weaver70439242016-03-10 15:15:49 -0800427 mMagnifedViewport.getMagnificationRegionLocked(outMagnificationRegion);
Alan Viverette59e53a12016-03-28 13:41:32 -0400428 }
429
Svetoslav8e3feb12014-02-24 13:46:47 -0800430 public void destroyLocked() {
431 mMagnifedViewport.destroyWindow();
432 }
433
Phil Weaver251db072017-03-28 08:35:38 -0700434 // Can be called outside of a surface transaction
435 public void showMagnificationBoundsIfNeeded() {
436 mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)
437 .sendToTarget();
438 }
439
Svetoslav8e3feb12014-02-24 13:46:47 -0800440 /** NOTE: This has to be called within a surface transaction. */
441 public void drawMagnifiedRegionBorderIfNeededLocked() {
442 mMagnifedViewport.drawWindowIfNeededLocked();
443 }
444
445 private final class MagnifiedViewport {
446
Svetoslav8e3feb12014-02-24 13:46:47 -0800447 private final SparseArray<WindowState> mTempWindowStates =
448 new SparseArray<WindowState>();
449
450 private final RectF mTempRectF = new RectF();
451
452 private final Point mTempPoint = new Point();
453
454 private final Matrix mTempMatrix = new Matrix();
455
Phil Weaver70439242016-03-10 15:15:49 -0800456 private final Region mMagnificationRegion = new Region();
457 private final Region mOldMagnificationRegion = new Region();
Svetoslav8e3feb12014-02-24 13:46:47 -0800458
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800459 private final Path mCircularPath;
460
Svetoslav8e3feb12014-02-24 13:46:47 -0800461 private final MagnificationSpec mMagnificationSpec = MagnificationSpec.obtain();
462
463 private final WindowManager mWindowManager;
464
Svetoslav7505e332014-08-22 12:14:28 -0700465 private final float mBorderWidth;
Svetoslav8e3feb12014-02-24 13:46:47 -0800466 private final int mHalfBorderWidth;
Svetoslav7505e332014-08-22 12:14:28 -0700467 private final int mDrawBorderInset;
Svetoslav8e3feb12014-02-24 13:46:47 -0800468
469 private final ViewportWindow mWindow;
470
471 private boolean mFullRedrawNeeded;
Robert Carrb1579c82017-09-05 14:54:47 -0700472 private int mTempLayer = 0;
Svetoslav8e3feb12014-02-24 13:46:47 -0800473
474 public MagnifiedViewport() {
475 mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE);
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800476 mBorderWidth = mContext.getResources().getDimension(
477 com.android.internal.R.dimen.accessibility_magnification_indicator_width);
Svetoslav7505e332014-08-22 12:14:28 -0700478 mHalfBorderWidth = (int) Math.ceil(mBorderWidth / 2);
479 mDrawBorderInset = (int) mBorderWidth / 2;
Svetoslav8e3feb12014-02-24 13:46:47 -0800480 mWindow = new ViewportWindow(mContext);
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800481
Adam Powell01f280d2015-05-18 16:07:42 -0700482 if (mContext.getResources().getConfiguration().isScreenRound()) {
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800483 mCircularPath = new Path();
484 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
485 final int centerXY = mTempPoint.x / 2;
486 mCircularPath.addCircle(centerXY, centerXY, centerXY, Path.Direction.CW);
487 } else {
488 mCircularPath = null;
489 }
490
Svetoslav8e3feb12014-02-24 13:46:47 -0800491 recomputeBoundsLocked();
492 }
493
Phil Weaver70439242016-03-10 15:15:49 -0800494 public void getMagnificationRegionLocked(@NonNull Region outMagnificationRegion) {
495 outMagnificationRegion.set(mMagnificationRegion);
Alan Viverette59e53a12016-03-28 13:41:32 -0400496 }
497
Svetoslav8e3feb12014-02-24 13:46:47 -0800498 public void updateMagnificationSpecLocked(MagnificationSpec spec) {
499 if (spec != null) {
500 mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY);
501 } else {
502 mMagnificationSpec.clear();
503 }
504 // If this message is pending we are in a rotation animation and do not want
505 // to show the border. We will do so when the pending message is handled.
Svetoslav7505e332014-08-22 12:14:28 -0700506 if (!mHandler.hasMessages(
507 MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
Casey Burkhardt74922c62017-02-13 12:43:16 -0800508 setMagnifiedRegionBorderShownLocked(
509 isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked(), true);
Svetoslav8e3feb12014-02-24 13:46:47 -0800510 }
511 }
512
513 public void recomputeBoundsLocked() {
514 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
515 final int screenWidth = mTempPoint.x;
516 final int screenHeight = mTempPoint.y;
517
Phil Weaver70439242016-03-10 15:15:49 -0800518 mMagnificationRegion.set(0, 0, 0, 0);
519 final Region availableBounds = mTempRegion1;
520 availableBounds.set(0, 0, screenWidth, screenHeight);
Svetoslav8e3feb12014-02-24 13:46:47 -0800521
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800522 if (mCircularPath != null) {
Phil Weaver70439242016-03-10 15:15:49 -0800523 availableBounds.setPath(mCircularPath, availableBounds);
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800524 }
525
Svetoslav8e3feb12014-02-24 13:46:47 -0800526 Region nonMagnifiedBounds = mTempRegion4;
Svet Ganovb21df802014-09-01 19:06:33 -0700527 nonMagnifiedBounds.set(0, 0, 0, 0);
Svetoslav8e3feb12014-02-24 13:46:47 -0800528
529 SparseArray<WindowState> visibleWindows = mTempWindowStates;
530 visibleWindows.clear();
531 populateWindowsOnScreenLocked(visibleWindows);
532
533 final int visibleWindowCount = visibleWindows.size();
534 for (int i = visibleWindowCount - 1; i >= 0; i--) {
535 WindowState windowState = visibleWindows.valueAt(i);
Phil Weaverd321075e2017-06-13 09:13:35 -0700536 if ((windowState.mAttrs.type == TYPE_MAGNIFICATION_OVERLAY)
537 || ((windowState.mAttrs.privateFlags
Robert Carr132c9f52017-07-31 17:02:30 -0700538 & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0)) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800539 continue;
540 }
541
Phil Weaver65c06702016-03-15 15:33:46 -0700542 // Consider the touchable portion of the window
Svetoslav8e3feb12014-02-24 13:46:47 -0800543 Matrix matrix = mTempMatrix;
544 populateTransformationMatrixLocked(windowState, matrix);
Phil Weaver65c06702016-03-15 15:33:46 -0700545 Region touchableRegion = mTempRegion3;
546 windowState.getTouchableRegion(touchableRegion);
547 Rect touchableFrame = mTempRect1;
548 touchableRegion.getBounds(touchableFrame);
Svetoslav8e3feb12014-02-24 13:46:47 -0800549 RectF windowFrame = mTempRectF;
Phil Weaver65c06702016-03-15 15:33:46 -0700550 windowFrame.set(touchableFrame);
551 windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
552 matrix.mapRect(windowFrame);
553 Region windowBounds = mTempRegion2;
554 windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
555 (int) windowFrame.right, (int) windowFrame.bottom);
556 // Only update new regions
557 Region portionOfWindowAlreadyAccountedFor = mTempRegion3;
Phil Weaver70439242016-03-10 15:15:49 -0800558 portionOfWindowAlreadyAccountedFor.set(mMagnificationRegion);
Phil Weaver65c06702016-03-15 15:33:46 -0700559 portionOfWindowAlreadyAccountedFor.op(nonMagnifiedBounds, Region.Op.UNION);
560 windowBounds.op(portionOfWindowAlreadyAccountedFor, Region.Op.DIFFERENCE);
Svetoslav8e3feb12014-02-24 13:46:47 -0800561
Robert Carrb1579c82017-09-05 14:54:47 -0700562 if (windowState.shouldMagnify()) {
Phil Weaver70439242016-03-10 15:15:49 -0800563 mMagnificationRegion.op(windowBounds, Region.Op.UNION);
564 mMagnificationRegion.op(availableBounds, Region.Op.INTERSECT);
Svetoslav8e3feb12014-02-24 13:46:47 -0800565 } else {
Svetoslav8e3feb12014-02-24 13:46:47 -0800566 nonMagnifiedBounds.op(windowBounds, Region.Op.UNION);
Phil Weaver70439242016-03-10 15:15:49 -0800567 availableBounds.op(windowBounds, Region.Op.DIFFERENCE);
Svetoslav8e3feb12014-02-24 13:46:47 -0800568 }
569
Phil Weaver65c06702016-03-15 15:33:46 -0700570 // Update accounted bounds
Svetoslav8e3feb12014-02-24 13:46:47 -0800571 Region accountedBounds = mTempRegion2;
Phil Weaver70439242016-03-10 15:15:49 -0800572 accountedBounds.set(mMagnificationRegion);
Svetoslav8e3feb12014-02-24 13:46:47 -0800573 accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION);
574 accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT);
575
576 if (accountedBounds.isRect()) {
577 Rect accountedFrame = mTempRect1;
578 accountedBounds.getBounds(accountedFrame);
579 if (accountedFrame.width() == screenWidth
580 && accountedFrame.height() == screenHeight) {
581 break;
582 }
583 }
584 }
585
586 visibleWindows.clear();
587
Phil Weaver70439242016-03-10 15:15:49 -0800588 mMagnificationRegion.op(mDrawBorderInset, mDrawBorderInset,
Svetoslav7505e332014-08-22 12:14:28 -0700589 screenWidth - mDrawBorderInset, screenHeight - mDrawBorderInset,
Svetoslav8e3feb12014-02-24 13:46:47 -0800590 Region.Op.INTERSECT);
591
Phil Weaver70439242016-03-10 15:15:49 -0800592 final boolean magnifiedChanged =
593 !mOldMagnificationRegion.equals(mMagnificationRegion);
594 if (magnifiedChanged) {
595 mWindow.setBounds(mMagnificationRegion);
596 final Rect dirtyRect = mTempRect1;
597 if (mFullRedrawNeeded) {
598 mFullRedrawNeeded = false;
599 dirtyRect.set(mDrawBorderInset, mDrawBorderInset,
600 screenWidth - mDrawBorderInset,
601 screenHeight - mDrawBorderInset);
602 mWindow.invalidate(dirtyRect);
603 } else {
604 final Region dirtyRegion = mTempRegion3;
605 dirtyRegion.set(mMagnificationRegion);
606 dirtyRegion.op(mOldMagnificationRegion, Region.Op.UNION);
607 dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT);
608 dirtyRegion.getBounds(dirtyRect);
609 mWindow.invalidate(dirtyRect);
Svetoslav8e3feb12014-02-24 13:46:47 -0800610 }
611
Phil Weaver70439242016-03-10 15:15:49 -0800612 mOldMagnificationRegion.set(mMagnificationRegion);
Alan Viverette214fb682015-11-17 09:47:11 -0500613 final SomeArgs args = SomeArgs.obtain();
Phil Weaver70439242016-03-10 15:15:49 -0800614 args.arg1 = Region.obtain(mMagnificationRegion);
Alan Viverette214fb682015-11-17 09:47:11 -0500615 mHandler.obtainMessage(
Phil Weaver70439242016-03-10 15:15:49 -0800616 MyHandler.MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED, args)
617 .sendToTarget();
Svetoslav8e3feb12014-02-24 13:46:47 -0800618 }
619 }
620
621 public void onRotationChangedLocked() {
Casey Burkhardt74922c62017-02-13 12:43:16 -0800622 // If we are showing the magnification border, hide it immediately so
Svetoslav8e3feb12014-02-24 13:46:47 -0800623 // the user does not see strange artifacts during rotation. The screenshot
Casey Burkhardt74922c62017-02-13 12:43:16 -0800624 // used for rotation already has the border. After the rotation is complete
Svetoslav8e3feb12014-02-24 13:46:47 -0800625 // we will show the border.
Casey Burkhardt74922c62017-02-13 12:43:16 -0800626 if (isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked()) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800627 setMagnifiedRegionBorderShownLocked(false, false);
628 final long delay = (long) (mLongAnimationDuration
Robert Carre625fcf2017-09-01 12:36:28 -0700629 * mService.getWindowAnimationScaleLocked());
Svetoslav8e3feb12014-02-24 13:46:47 -0800630 Message message = mHandler.obtainMessage(
631 MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED);
632 mHandler.sendMessageDelayed(message, delay);
633 }
634 recomputeBoundsLocked();
635 mWindow.updateSize();
636 }
637
638 public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) {
639 if (shown) {
640 mFullRedrawNeeded = true;
Phil Weaver70439242016-03-10 15:15:49 -0800641 mOldMagnificationRegion.set(0, 0, 0, 0);
Svetoslav8e3feb12014-02-24 13:46:47 -0800642 }
643 mWindow.setShown(shown, animate);
644 }
645
646 public void getMagnifiedFrameInContentCoordsLocked(Rect rect) {
647 MagnificationSpec spec = mMagnificationSpec;
Phil Weaver70439242016-03-10 15:15:49 -0800648 mMagnificationRegion.getBounds(rect);
Svetoslav8e3feb12014-02-24 13:46:47 -0800649 rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
650 rect.scale(1.0f / spec.scale);
651 }
652
653 public boolean isMagnifyingLocked() {
654 return mMagnificationSpec.scale > 1.0f;
655 }
656
657 public MagnificationSpec getMagnificationSpecLocked() {
658 return mMagnificationSpec;
659 }
660
661 /** NOTE: This has to be called within a surface transaction. */
662 public void drawWindowIfNeededLocked() {
663 recomputeBoundsLocked();
664 mWindow.drawIfNeeded();
665 }
666
667 public void destroyWindow() {
668 mWindow.releaseSurface();
669 }
670
671 private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
Robert Carre625fcf2017-09-01 12:36:28 -0700672 final DisplayContent dc = mService.getDefaultDisplayContentLocked();
Robert Carrb1579c82017-09-05 14:54:47 -0700673 mTempLayer = 0;
Wale Ogunwaled1880962016-11-08 10:31:59 -0800674 dc.forAllWindows((w) -> {
675 if (w.isOnScreen() && w.isVisibleLw()
676 && !w.mWinAnimator.mEnterAnimationPending) {
Robert Carrb1579c82017-09-05 14:54:47 -0700677 mTempLayer++;
678 outWindows.put(mTempLayer, w);
Svetoslav8e3feb12014-02-24 13:46:47 -0800679 }
Wale Ogunwaled1880962016-11-08 10:31:59 -0800680 }, false /* traverseTopToBottom */ );
Svetoslav8e3feb12014-02-24 13:46:47 -0800681 }
682
683 private final class ViewportWindow {
684 private static final String SURFACE_TITLE = "Magnification Overlay";
685
Svetoslav8e3feb12014-02-24 13:46:47 -0800686 private final Region mBounds = new Region();
687 private final Rect mDirtyRect = new Rect();
688 private final Paint mPaint = new Paint();
689
Svetoslav8e3feb12014-02-24 13:46:47 -0800690 private final SurfaceControl mSurfaceControl;
691 private final Surface mSurface = new Surface();
692
Svet Ganovb21df802014-09-01 19:06:33 -0700693 private final AnimationController mAnimationController;
694
Svetoslav8e3feb12014-02-24 13:46:47 -0800695 private boolean mShown;
696 private int mAlpha;
697
698 private boolean mInvalidated;
699
700 public ViewportWindow(Context context) {
701 SurfaceControl surfaceControl = null;
702 try {
703 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
Robert Carrb1579c82017-09-05 14:54:47 -0700704 surfaceControl = mService.getDefaultDisplayContentLocked().makeOverlay()
Robert Carre625fcf2017-09-01 12:36:28 -0700705 .setName(SURFACE_TITLE)
706 .setSize(mTempPoint.x, mTempPoint.y) // not a typo
707 .setFormat(PixelFormat.TRANSLUCENT)
708 .build();
Svetoslav8e3feb12014-02-24 13:46:47 -0800709 } catch (OutOfResourcesException oore) {
710 /* ignore */
711 }
712 mSurfaceControl = surfaceControl;
Robert Carre625fcf2017-09-01 12:36:28 -0700713 mSurfaceControl.setLayer(mService.mPolicy.getWindowLayerFromTypeLw(
Phil Weaverd321075e2017-06-13 09:13:35 -0700714 TYPE_MAGNIFICATION_OVERLAY)
Svetoslav8e3feb12014-02-24 13:46:47 -0800715 * WindowManagerService.TYPE_LAYER_MULTIPLIER);
716 mSurfaceControl.setPosition(0, 0);
717 mSurface.copyFrom(mSurfaceControl);
718
Svet Ganovb21df802014-09-01 19:06:33 -0700719 mAnimationController = new AnimationController(context,
Robert Carre625fcf2017-09-01 12:36:28 -0700720 mService.mH.getLooper());
Svet Ganovb21df802014-09-01 19:06:33 -0700721
Svetoslav8e3feb12014-02-24 13:46:47 -0800722 TypedValue typedValue = new TypedValue();
723 context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight,
724 typedValue, true);
Alan Viverette4a357cd2015-03-18 18:37:18 -0700725 final int borderColor = context.getColor(typedValue.resourceId);
Svetoslav8e3feb12014-02-24 13:46:47 -0800726
727 mPaint.setStyle(Paint.Style.STROKE);
728 mPaint.setStrokeWidth(mBorderWidth);
729 mPaint.setColor(borderColor);
730
Svetoslav8e3feb12014-02-24 13:46:47 -0800731 mInvalidated = true;
732 }
733
734 public void setShown(boolean shown, boolean animate) {
Robert Carre625fcf2017-09-01 12:36:28 -0700735 synchronized (mService.mWindowMap) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800736 if (mShown == shown) {
737 return;
738 }
739 mShown = shown;
Svet Ganovb21df802014-09-01 19:06:33 -0700740 mAnimationController.onFrameShownStateChanged(shown, animate);
Svetoslav8e3feb12014-02-24 13:46:47 -0800741 if (DEBUG_VIEWPORT_WINDOW) {
742 Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown);
743 }
744 }
745 }
746
747 @SuppressWarnings("unused")
748 // Called reflectively from an animator.
749 public int getAlpha() {
Robert Carre625fcf2017-09-01 12:36:28 -0700750 synchronized (mService.mWindowMap) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800751 return mAlpha;
752 }
753 }
754
755 public void setAlpha(int alpha) {
Robert Carre625fcf2017-09-01 12:36:28 -0700756 synchronized (mService.mWindowMap) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800757 if (mAlpha == alpha) {
758 return;
759 }
760 mAlpha = alpha;
761 invalidate(null);
762 if (DEBUG_VIEWPORT_WINDOW) {
763 Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha);
764 }
765 }
766 }
767
768 public void setBounds(Region bounds) {
Robert Carre625fcf2017-09-01 12:36:28 -0700769 synchronized (mService.mWindowMap) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800770 if (mBounds.equals(bounds)) {
771 return;
772 }
773 mBounds.set(bounds);
774 invalidate(mDirtyRect);
775 if (DEBUG_VIEWPORT_WINDOW) {
776 Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds);
777 }
778 }
779 }
780
781 public void updateSize() {
Robert Carre625fcf2017-09-01 12:36:28 -0700782 synchronized (mService.mWindowMap) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800783 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
784 mSurfaceControl.setSize(mTempPoint.x, mTempPoint.y);
785 invalidate(mDirtyRect);
786 }
787 }
788
789 public void invalidate(Rect dirtyRect) {
790 if (dirtyRect != null) {
791 mDirtyRect.set(dirtyRect);
792 } else {
793 mDirtyRect.setEmpty();
794 }
795 mInvalidated = true;
Robert Carre625fcf2017-09-01 12:36:28 -0700796 mService.scheduleAnimationLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800797 }
798
799 /** NOTE: This has to be called within a surface transaction. */
800 public void drawIfNeeded() {
Robert Carre625fcf2017-09-01 12:36:28 -0700801 synchronized (mService.mWindowMap) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800802 if (!mInvalidated) {
803 return;
804 }
805 mInvalidated = false;
806 Canvas canvas = null;
807 try {
808 // Empty dirty rectangle means unspecified.
809 if (mDirtyRect.isEmpty()) {
810 mBounds.getBounds(mDirtyRect);
811 }
812 mDirtyRect.inset(- mHalfBorderWidth, - mHalfBorderWidth);
813 canvas = mSurface.lockCanvas(mDirtyRect);
814 if (DEBUG_VIEWPORT_WINDOW) {
815 Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect);
816 }
817 } catch (IllegalArgumentException iae) {
818 /* ignore */
819 } catch (Surface.OutOfResourcesException oore) {
820 /* ignore */
821 }
822 if (canvas == null) {
823 return;
824 }
825 if (DEBUG_VIEWPORT_WINDOW) {
826 Slog.i(LOG_TAG, "Bounds: " + mBounds);
827 }
828 canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
829 mPaint.setAlpha(mAlpha);
830 Path path = mBounds.getBoundaryPath();
831 canvas.drawPath(path, mPaint);
832
833 mSurface.unlockCanvasAndPost(canvas);
834
835 if (mAlpha > 0) {
836 mSurfaceControl.show();
837 } else {
838 mSurfaceControl.hide();
839 }
840 }
841 }
842
843 public void releaseSurface() {
844 mSurfaceControl.release();
845 mSurface.release();
846 }
Svet Ganovb21df802014-09-01 19:06:33 -0700847
848 private final class AnimationController extends Handler {
849 private static final String PROPERTY_NAME_ALPHA = "alpha";
850
851 private static final int MIN_ALPHA = 0;
852 private static final int MAX_ALPHA = 255;
853
854 private static final int MSG_FRAME_SHOWN_STATE_CHANGED = 1;
855
856 private final ValueAnimator mShowHideFrameAnimator;
857
858 public AnimationController(Context context, Looper looper) {
859 super(looper);
860 mShowHideFrameAnimator = ObjectAnimator.ofInt(ViewportWindow.this,
861 PROPERTY_NAME_ALPHA, MIN_ALPHA, MAX_ALPHA);
862
863 Interpolator interpolator = new DecelerateInterpolator(2.5f);
864 final long longAnimationDuration = context.getResources().getInteger(
865 com.android.internal.R.integer.config_longAnimTime);
866
867 mShowHideFrameAnimator.setInterpolator(interpolator);
868 mShowHideFrameAnimator.setDuration(longAnimationDuration);
869 }
870
871 public void onFrameShownStateChanged(boolean shown, boolean animate) {
872 obtainMessage(MSG_FRAME_SHOWN_STATE_CHANGED,
873 shown ? 1 : 0, animate ? 1 : 0).sendToTarget();
874 }
875
876 @Override
877 public void handleMessage(Message message) {
878 switch (message.what) {
879 case MSG_FRAME_SHOWN_STATE_CHANGED: {
880 final boolean shown = message.arg1 == 1;
881 final boolean animate = message.arg2 == 1;
882
883 if (animate) {
884 if (mShowHideFrameAnimator.isRunning()) {
885 mShowHideFrameAnimator.reverse();
886 } else {
887 if (shown) {
888 mShowHideFrameAnimator.start();
889 } else {
890 mShowHideFrameAnimator.reverse();
891 }
892 }
893 } else {
894 mShowHideFrameAnimator.cancel();
895 if (shown) {
896 setAlpha(MAX_ALPHA);
897 } else {
898 setAlpha(MIN_ALPHA);
899 }
900 }
901 } break;
902 }
903 }
904 }
Svetoslav8e3feb12014-02-24 13:46:47 -0800905 }
906 }
907
908 private class MyHandler extends Handler {
Phil Weaver70439242016-03-10 15:15:49 -0800909 public static final int MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED = 1;
Svetoslav8e3feb12014-02-24 13:46:47 -0800910 public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2;
911 public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
912 public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
913 public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
914
915 public MyHandler(Looper looper) {
916 super(looper);
917 }
918
919 @Override
920 public void handleMessage(Message message) {
921 switch (message.what) {
Phil Weaver70439242016-03-10 15:15:49 -0800922 case MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED: {
Alan Viverette214fb682015-11-17 09:47:11 -0500923 final SomeArgs args = (SomeArgs) message.obj;
924 final Region magnifiedBounds = (Region) args.arg1;
Phil Weaver70439242016-03-10 15:15:49 -0800925 mCallbacks.onMagnificationRegionChanged(magnifiedBounds);
Alan Viverette214fb682015-11-17 09:47:11 -0500926 magnifiedBounds.recycle();
Svetoslav8e3feb12014-02-24 13:46:47 -0800927 } break;
928
929 case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: {
930 SomeArgs args = (SomeArgs) message.obj;
931 final int left = args.argi1;
932 final int top = args.argi2;
933 final int right = args.argi3;
934 final int bottom = args.argi4;
935 mCallbacks.onRectangleOnScreenRequested(left, top, right, bottom);
936 args.recycle();
937 } break;
938
939 case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: {
940 mCallbacks.onUserContextChanged();
941 } break;
942
943 case MESSAGE_NOTIFY_ROTATION_CHANGED: {
944 final int rotation = message.arg1;
945 mCallbacks.onRotationChanged(rotation);
946 } break;
947
948 case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
Robert Carre625fcf2017-09-01 12:36:28 -0700949 synchronized (mService.mWindowMap) {
Casey Burkhardt74922c62017-02-13 12:43:16 -0800950 if (mMagnifedViewport.isMagnifyingLocked()
951 || isForceShowingMagnifiableBoundsLocked()) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800952 mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true);
Robert Carre625fcf2017-09-01 12:36:28 -0700953 mService.scheduleAnimationLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800954 }
955 }
956 } break;
957 }
958 }
959 }
960 }
961
962 /**
963 * This class encapsulates the functionality related to computing the windows
964 * reported for accessibility purposes. These windows are all windows a sighted
965 * user can see on the screen.
966 */
967 private static final class WindowsForAccessibilityObserver {
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -0800968 private static final String LOG_TAG = TAG_WITH_CLASS_NAME ?
969 "WindowsForAccessibilityObserver" : TAG_WM;
Svetoslav8e3feb12014-02-24 13:46:47 -0800970
971 private static final boolean DEBUG = false;
972
973 private final SparseArray<WindowState> mTempWindowStates =
974 new SparseArray<WindowState>();
975
976 private final List<WindowInfo> mOldWindows = new ArrayList<WindowInfo>();
977
978 private final Set<IBinder> mTempBinderSet = new ArraySet<IBinder>();
979
980 private final RectF mTempRectF = new RectF();
981
982 private final Matrix mTempMatrix = new Matrix();
983
984 private final Point mTempPoint = new Point();
985
986 private final Rect mTempRect = new Rect();
987
988 private final Region mTempRegion = new Region();
989
990 private final Region mTempRegion1 = new Region();
991
992 private final Context mContext;
993
Robert Carre625fcf2017-09-01 12:36:28 -0700994 private final WindowManagerService mService;
Svetoslav8e3feb12014-02-24 13:46:47 -0800995
996 private final Handler mHandler;
997
998 private final WindowsForAccessibilityCallback mCallback;
999
Svetoslavf7174e82014-06-12 11:29:35 -07001000 private final long mRecurringAccessibilityEventsIntervalMillis;
1001
Robert Carrb1579c82017-09-05 14:54:47 -07001002 private int mTempLayer = 0;
1003
Svetoslav8e3feb12014-02-24 13:46:47 -08001004 public WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
1005 WindowsForAccessibilityCallback callback) {
1006 mContext = windowManagerService.mContext;
Robert Carre625fcf2017-09-01 12:36:28 -07001007 mService = windowManagerService;
Svetoslav8e3feb12014-02-24 13:46:47 -08001008 mCallback = callback;
Robert Carre625fcf2017-09-01 12:36:28 -07001009 mHandler = new MyHandler(mService.mH.getLooper());
Svetoslavf7174e82014-06-12 11:29:35 -07001010 mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration
1011 .getSendRecurringAccessibilityEventsInterval();
Svetoslav8e3feb12014-02-24 13:46:47 -08001012 computeChangedWindows();
1013 }
1014
Svetoslav3a0d8782014-12-04 12:50:11 -08001015 public void performComputeChangedWindowsNotLocked() {
1016 mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS);
1017 computeChangedWindows();
1018 }
1019
Svetoslavf7174e82014-06-12 11:29:35 -07001020 public void scheduleComputeChangedWindowsLocked() {
Svetoslav3a0d8782014-12-04 12:50:11 -08001021 if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) {
Svetoslavf7174e82014-06-12 11:29:35 -07001022 mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS,
1023 mRecurringAccessibilityEventsIntervalMillis);
1024 }
1025 }
1026
Svetoslav8e3feb12014-02-24 13:46:47 -08001027 public void computeChangedWindows() {
1028 if (DEBUG) {
1029 Slog.i(LOG_TAG, "computeChangedWindows()");
1030 }
1031
Svetoslav3a0d8782014-12-04 12:50:11 -08001032 boolean windowsChanged = false;
1033 List<WindowInfo> windows = new ArrayList<WindowInfo>();
1034
Robert Carre625fcf2017-09-01 12:36:28 -07001035 synchronized (mService.mWindowMap) {
Svetoslavf7174e82014-06-12 11:29:35 -07001036 // Do not send the windows if there is no current focus as
1037 // the window manager is still looking for where to put it.
1038 // We will do the work when we get a focus change callback.
Robert Carre625fcf2017-09-01 12:36:28 -07001039 if (mService.mCurrentFocus == null) {
Svetoslavf7174e82014-06-12 11:29:35 -07001040 return;
1041 }
1042
Svetoslav8e3feb12014-02-24 13:46:47 -08001043 WindowManager windowManager = (WindowManager)
1044 mContext.getSystemService(Context.WINDOW_SERVICE);
1045 windowManager.getDefaultDisplay().getRealSize(mTempPoint);
1046 final int screenWidth = mTempPoint.x;
1047 final int screenHeight = mTempPoint.y;
1048
1049 Region unaccountedSpace = mTempRegion;
1050 unaccountedSpace.set(0, 0, screenWidth, screenHeight);
1051
Wale Ogunwalef7cab102016-10-25 15:25:14 -07001052 final SparseArray<WindowState> visibleWindows = mTempWindowStates;
Svetoslav8e3feb12014-02-24 13:46:47 -08001053 populateVisibleWindowsOnScreenLocked(visibleWindows);
Svetoslav8e3feb12014-02-24 13:46:47 -08001054 Set<IBinder> addedWindows = mTempBinderSet;
1055 addedWindows.clear();
1056
Svetoslavf7174e82014-06-12 11:29:35 -07001057 boolean focusedWindowAdded = false;
1058
Svetoslav8e3feb12014-02-24 13:46:47 -08001059 final int visibleWindowCount = visibleWindows.size();
Allen Hairf20ac2c2016-02-11 17:42:59 -08001060 HashSet<Integer> skipRemainingWindowsForTasks = new HashSet<>();
Svetoslav8e3feb12014-02-24 13:46:47 -08001061 for (int i = visibleWindowCount - 1; i >= 0; i--) {
Alan Viverette9538eea2014-11-13 14:49:20 -08001062 final WindowState windowState = visibleWindows.valueAt(i);
Svetoslav8e3feb12014-02-24 13:46:47 -08001063 final int flags = windowState.mAttrs.flags;
Allen Hairf20ac2c2016-02-11 17:42:59 -08001064 final Task task = windowState.getTask();
1065
1066 // If the window is part of a task that we're finished with - ignore.
1067 if (task != null && skipRemainingWindowsForTasks.contains(task.mTaskId)) {
1068 continue;
1069 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001070
Svetoslav3a5c7212014-10-14 09:54:26 -07001071 // If the window is not touchable - ignore.
Svetoslavf7174e82014-06-12 11:29:35 -07001072 if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001073 continue;
1074 }
1075
Alan Viverette9538eea2014-11-13 14:49:20 -08001076 // Compute the bounds in the screen.
1077 final Rect boundsInScreen = mTempRect;
1078 computeWindowBoundsInScreen(windowState, boundsInScreen);
1079
Svetoslav8e3feb12014-02-24 13:46:47 -08001080 // If the window is completely covered by other windows - ignore.
1081 if (unaccountedSpace.quickReject(boundsInScreen)) {
1082 continue;
1083 }
1084
1085 // Add windows of certain types not covered by modal windows.
1086 if (isReportedWindowType(windowState.mAttrs.type)) {
1087 // Add the window to the ones to be reported.
Svetoslavf7174e82014-06-12 11:29:35 -07001088 WindowInfo window = obtainPopulatedWindowInfo(windowState, boundsInScreen);
Robert Carrb1579c82017-09-05 14:54:47 -07001089 window.layer = addedWindows.size();
Svetoslav8e3feb12014-02-24 13:46:47 -08001090 addedWindows.add(window.token);
Svetoslav8e3feb12014-02-24 13:46:47 -08001091 windows.add(window);
Svetoslavf7174e82014-06-12 11:29:35 -07001092 if (windowState.isFocused()) {
1093 focusedWindowAdded = true;
1094 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001095 }
1096
Alan Viveretted0c73f42014-11-18 10:25:04 -08001097 // Account for the space this window takes if the window
1098 // is not an accessibility overlay which does not change
1099 // the reported windows.
1100 if (windowState.mAttrs.type !=
1101 WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
1102 unaccountedSpace.op(boundsInScreen, unaccountedSpace,
1103 Region.Op.REVERSE_DIFFERENCE);
1104 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001105
Allen Hairf20ac2c2016-02-11 17:42:59 -08001106 // If a window is modal it prevents other windows from being touched
Svetoslav8e3feb12014-02-24 13:46:47 -08001107 if ((flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1108 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
Phil Weaverac9ad702016-07-27 18:03:10 -07001109 // Account for all space in the task, whether the windows in it are
1110 // touchable or not. The modal window blocks all touches from the task's
1111 // area.
1112 unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace,
1113 Region.Op.REVERSE_DIFFERENCE);
1114
Allen Hairf20ac2c2016-02-11 17:42:59 -08001115 if (task != null) {
1116 // If the window is associated with a particular task, we can skip the
1117 // rest of the windows for that task.
1118 skipRemainingWindowsForTasks.add(task.mTaskId);
1119 continue;
1120 } else {
1121 // If the window is not associated with a particular task, then it is
1122 // globally modal. In this case we can skip all remaining windows.
1123 break;
1124 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001125 }
Phil Weaverac9ad702016-07-27 18:03:10 -07001126 // We figured out what is touchable for the entire screen - done.
1127 if (unaccountedSpace.isEmpty()) {
1128 break;
1129 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001130 }
1131
Svetoslavf7174e82014-06-12 11:29:35 -07001132 // Always report the focused window.
1133 if (!focusedWindowAdded) {
1134 for (int i = visibleWindowCount - 1; i >= 0; i--) {
1135 WindowState windowState = visibleWindows.valueAt(i);
1136 if (windowState.isFocused()) {
1137 // Compute the bounds in the screen.
1138 Rect boundsInScreen = mTempRect;
1139 computeWindowBoundsInScreen(windowState, boundsInScreen);
1140
1141 // Add the window to the ones to be reported.
1142 WindowInfo window = obtainPopulatedWindowInfo(windowState,
1143 boundsInScreen);
1144 addedWindows.add(window.token);
1145 windows.add(window);
1146 break;
1147 }
1148 }
1149 }
1150
Svetoslav8e3feb12014-02-24 13:46:47 -08001151 // Remove child/parent references to windows that were not added.
1152 final int windowCount = windows.size();
1153 for (int i = 0; i < windowCount; i++) {
1154 WindowInfo window = windows.get(i);
1155 if (!addedWindows.contains(window.parentToken)) {
1156 window.parentToken = null;
1157 }
1158 if (window.childTokens != null) {
1159 final int childTokenCount = window.childTokens.size();
1160 for (int j = childTokenCount - 1; j >= 0; j--) {
1161 if (!addedWindows.contains(window.childTokens.get(j))) {
1162 window.childTokens.remove(j);
1163 }
1164 }
1165 // Leave the child token list if empty.
1166 }
1167 }
1168
1169 visibleWindows.clear();
1170 addedWindows.clear();
1171
1172 // We computed the windows and if they changed notify the client.
Svetoslav8e3feb12014-02-24 13:46:47 -08001173 if (mOldWindows.size() != windows.size()) {
1174 // Different size means something changed.
1175 windowsChanged = true;
1176 } else if (!mOldWindows.isEmpty() || !windows.isEmpty()) {
1177 // Since we always traverse windows from high to low layer
1178 // the old and new windows at the same index should be the
1179 // same, otherwise something changed.
1180 for (int i = 0; i < windowCount; i++) {
1181 WindowInfo oldWindow = mOldWindows.get(i);
1182 WindowInfo newWindow = windows.get(i);
1183 // We do not care for layer changes given the window
1184 // order does not change. This brings no new information
1185 // to the clients.
1186 if (windowChangedNoLayer(oldWindow, newWindow)) {
1187 windowsChanged = true;
1188 break;
1189 }
1190 }
1191 }
1192
1193 if (windowsChanged) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001194 cacheWindows(windows);
Svetoslav8e3feb12014-02-24 13:46:47 -08001195 }
1196 }
Svetoslav3a0d8782014-12-04 12:50:11 -08001197
1198 // Now we do not hold the lock, so send the windows over.
1199 if (windowsChanged) {
1200 if (DEBUG) {
1201 Log.i(LOG_TAG, "Windows changed:" + windows);
1202 }
1203 mCallback.onWindowsForAccessibilityChanged(windows);
1204 } else {
1205 if (DEBUG) {
1206 Log.i(LOG_TAG, "No windows changed.");
1207 }
1208 }
1209
1210 // Recycle the windows as we do not need them.
1211 clearAndRecycleWindows(windows);
Svetoslav8e3feb12014-02-24 13:46:47 -08001212 }
1213
Svetoslavf7174e82014-06-12 11:29:35 -07001214 private void computeWindowBoundsInScreen(WindowState windowState, Rect outBounds) {
1215 // Get the touchable frame.
1216 Region touchableRegion = mTempRegion1;
1217 windowState.getTouchableRegion(touchableRegion);
1218 Rect touchableFrame = mTempRect;
1219 touchableRegion.getBounds(touchableFrame);
1220
1221 // Move to origin as all transforms are captured by the matrix.
1222 RectF windowFrame = mTempRectF;
1223 windowFrame.set(touchableFrame);
1224 windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
1225
1226 // Map the frame to get what appears on the screen.
1227 Matrix matrix = mTempMatrix;
1228 populateTransformationMatrixLocked(windowState, matrix);
1229 matrix.mapRect(windowFrame);
1230
1231 // Got the bounds.
1232 outBounds.set((int) windowFrame.left, (int) windowFrame.top,
1233 (int) windowFrame.right, (int) windowFrame.bottom);
1234 }
1235
Wale Ogunwaleadde52e2016-07-16 13:11:55 -07001236 private static WindowInfo obtainPopulatedWindowInfo(
1237 WindowState windowState, Rect boundsInScreen) {
1238 final WindowInfo window = windowState.getWindowInfo();
Svetoslavf7174e82014-06-12 11:29:35 -07001239 window.boundsInScreen.set(boundsInScreen);
Svetoslavf7174e82014-06-12 11:29:35 -07001240 return window;
1241 }
1242
Svetoslav8e3feb12014-02-24 13:46:47 -08001243 private void cacheWindows(List<WindowInfo> windows) {
1244 final int oldWindowCount = mOldWindows.size();
1245 for (int i = oldWindowCount - 1; i >= 0; i--) {
1246 mOldWindows.remove(i).recycle();
1247 }
1248 final int newWindowCount = windows.size();
1249 for (int i = 0; i < newWindowCount; i++) {
1250 WindowInfo newWindow = windows.get(i);
1251 mOldWindows.add(WindowInfo.obtain(newWindow));
1252 }
1253 }
1254
1255 private boolean windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow) {
1256 if (oldWindow == newWindow) {
1257 return false;
1258 }
Svetoslavf7174e82014-06-12 11:29:35 -07001259 if (oldWindow == null) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001260 return true;
1261 }
Svetoslavf7174e82014-06-12 11:29:35 -07001262 if (newWindow == null) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001263 return true;
1264 }
1265 if (oldWindow.type != newWindow.type) {
1266 return true;
1267 }
1268 if (oldWindow.focused != newWindow.focused) {
1269 return true;
1270 }
1271 if (oldWindow.token == null) {
1272 if (newWindow.token != null) {
1273 return true;
1274 }
1275 } else if (!oldWindow.token.equals(newWindow.token)) {
1276 return true;
1277 }
1278 if (oldWindow.parentToken == null) {
1279 if (newWindow.parentToken != null) {
1280 return true;
1281 }
1282 } else if (!oldWindow.parentToken.equals(newWindow.parentToken)) {
1283 return true;
1284 }
1285 if (!oldWindow.boundsInScreen.equals(newWindow.boundsInScreen)) {
1286 return true;
1287 }
1288 if (oldWindow.childTokens != null && newWindow.childTokens != null
1289 && !oldWindow.childTokens.equals(newWindow.childTokens)) {
1290 return true;
1291 }
Phil Weaver396d5492016-03-22 17:53:50 -07001292 if (!TextUtils.equals(oldWindow.title, newWindow.title)) {
1293 return true;
1294 }
1295 if (oldWindow.accessibilityIdOfAnchor != newWindow.accessibilityIdOfAnchor) {
1296 return true;
1297 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001298 return false;
1299 }
1300
Svetoslav3a0d8782014-12-04 12:50:11 -08001301 private static void clearAndRecycleWindows(List<WindowInfo> windows) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001302 final int windowCount = windows.size();
1303 for (int i = windowCount - 1; i >= 0; i--) {
1304 windows.remove(i).recycle();
1305 }
1306 }
1307
1308 private static boolean isReportedWindowType(int windowType) {
Jorim Jaggi73294b62016-10-26 18:02:36 -07001309 return (windowType != WindowManager.LayoutParams.TYPE_WALLPAPER
Svetoslav8e3feb12014-02-24 13:46:47 -08001310 && windowType != WindowManager.LayoutParams.TYPE_BOOT_PROGRESS
1311 && windowType != WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY
1312 && windowType != WindowManager.LayoutParams.TYPE_DRAG
Selim Cinekf83e8242015-05-19 18:08:14 -07001313 && windowType != WindowManager.LayoutParams.TYPE_INPUT_CONSUMER
Svetoslav8e3feb12014-02-24 13:46:47 -08001314 && windowType != WindowManager.LayoutParams.TYPE_POINTER
Phil Weaverd321075e2017-06-13 09:13:35 -07001315 && windowType != TYPE_MAGNIFICATION_OVERLAY
Svetoslav8e3feb12014-02-24 13:46:47 -08001316 && windowType != WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
1317 && windowType != WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY
1318 && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
1319 }
1320
1321 private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
Robert Carre625fcf2017-09-01 12:36:28 -07001322 final DisplayContent dc = mService.getDefaultDisplayContentLocked();
Robert Carrb1579c82017-09-05 14:54:47 -07001323 mTempLayer = 0;
Wale Ogunwaled1880962016-11-08 10:31:59 -08001324 dc.forAllWindows((w) -> {
1325 if (w.isVisibleLw()) {
Robert Carrb1579c82017-09-05 14:54:47 -07001326 outWindows.put(mTempLayer++, w);
Svetoslav8e3feb12014-02-24 13:46:47 -08001327 }
Wale Ogunwaled1880962016-11-08 10:31:59 -08001328 }, false /* traverseTopToBottom */ );
Svetoslav8e3feb12014-02-24 13:46:47 -08001329 }
1330
1331 private class MyHandler extends Handler {
Svetoslavf7174e82014-06-12 11:29:35 -07001332 public static final int MESSAGE_COMPUTE_CHANGED_WINDOWS = 1;
Svetoslav8e3feb12014-02-24 13:46:47 -08001333
1334 public MyHandler(Looper looper) {
1335 super(looper, null, false);
1336 }
1337
1338 @Override
1339 @SuppressWarnings("unchecked")
1340 public void handleMessage(Message message) {
1341 switch (message.what) {
Svetoslavf7174e82014-06-12 11:29:35 -07001342 case MESSAGE_COMPUTE_CHANGED_WINDOWS: {
1343 computeChangedWindows();
1344 } break;
Svetoslav8e3feb12014-02-24 13:46:47 -08001345 }
1346 }
1347 }
1348 }
1349}