blob: e5e2175fb61672988cfd6e96a63078b71e395977 [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
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -080019import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
20import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
21
Svetoslav8e3feb12014-02-24 13:46:47 -080022import android.animation.ObjectAnimator;
23import android.animation.ValueAnimator;
Alan Viverette59e53a12016-03-28 13:41:32 -040024import android.annotation.NonNull;
Svetoslav8e3feb12014-02-24 13:46:47 -080025import android.app.Service;
26import android.content.Context;
27import android.graphics.Canvas;
28import android.graphics.Color;
29import android.graphics.Matrix;
30import android.graphics.Paint;
31import android.graphics.Path;
32import android.graphics.PixelFormat;
33import android.graphics.Point;
34import android.graphics.PorterDuff.Mode;
35import android.graphics.Rect;
36import android.graphics.RectF;
37import android.graphics.Region;
38import android.os.Handler;
39import android.os.IBinder;
40import android.os.Looper;
41import android.os.Message;
Phil Weaver396d5492016-03-22 17:53:50 -070042import android.text.TextUtils;
Svetoslav8e3feb12014-02-24 13:46:47 -080043import android.util.ArraySet;
44import android.util.Log;
45import android.util.Slog;
46import android.util.SparseArray;
47import android.util.TypedValue;
48import android.view.MagnificationSpec;
49import android.view.Surface;
50import android.view.Surface.OutOfResourcesException;
51import android.view.SurfaceControl;
Svetoslavf7174e82014-06-12 11:29:35 -070052import android.view.ViewConfiguration;
Svetoslav8e3feb12014-02-24 13:46:47 -080053import android.view.WindowInfo;
54import android.view.WindowManager;
55import android.view.WindowManagerInternal.MagnificationCallbacks;
56import android.view.WindowManagerInternal.WindowsForAccessibilityCallback;
57import android.view.WindowManagerPolicy;
58import android.view.animation.DecelerateInterpolator;
59import android.view.animation.Interpolator;
60
61import com.android.internal.R;
62import com.android.internal.os.SomeArgs;
63
64import java.util.ArrayList;
Allen Hairf20ac2c2016-02-11 17:42:59 -080065import java.util.HashSet;
Svetoslav8e3feb12014-02-24 13:46:47 -080066import java.util.List;
67import java.util.Set;
68
69/**
70 * This class contains the accessibility related logic of the window manger.
71 */
72final class AccessibilityController {
73
74 private final WindowManagerService mWindowManagerService;
75
76 private static final float[] sTempFloats = new float[9];
77
78 public AccessibilityController(WindowManagerService service) {
79 mWindowManagerService = service;
80 }
81
82 private DisplayMagnifier mDisplayMagnifier;
83
84 private WindowsForAccessibilityObserver mWindowsForAccessibilityObserver;
85
86 public void setMagnificationCallbacksLocked(MagnificationCallbacks callbacks) {
87 if (callbacks != null) {
88 if (mDisplayMagnifier != null) {
89 throw new IllegalStateException("Magnification callbacks already set!");
90 }
91 mDisplayMagnifier = new DisplayMagnifier(mWindowManagerService, callbacks);
92 } else {
93 if (mDisplayMagnifier == null) {
94 throw new IllegalStateException("Magnification callbacks already cleared!");
95 }
96 mDisplayMagnifier.destroyLocked();
97 mDisplayMagnifier = null;
98 }
99 }
100
101 public void setWindowsForAccessibilityCallback(WindowsForAccessibilityCallback callback) {
102 if (callback != null) {
103 if (mWindowsForAccessibilityObserver != null) {
104 throw new IllegalStateException(
105 "Windows for accessibility callback already set!");
106 }
107 mWindowsForAccessibilityObserver = new WindowsForAccessibilityObserver(
108 mWindowManagerService, callback);
109 } else {
110 if (mWindowsForAccessibilityObserver == null) {
111 throw new IllegalStateException(
112 "Windows for accessibility callback already cleared!");
113 }
114 mWindowsForAccessibilityObserver = null;
115 }
116 }
117
118 public void setMagnificationSpecLocked(MagnificationSpec spec) {
119 if (mDisplayMagnifier != null) {
120 mDisplayMagnifier.setMagnificationSpecLocked(spec);
121 }
122 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700123 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800124 }
125 }
126
Phil Weaver70439242016-03-10 15:15:49 -0800127 public void getMagnificationRegionLocked(Region outMagnificationRegion) {
Alan Viverette59e53a12016-03-28 13:41:32 -0400128 if (mDisplayMagnifier != null) {
Phil Weaver70439242016-03-10 15:15:49 -0800129 mDisplayMagnifier.getMagnificationRegionLocked(outMagnificationRegion);
Alan Viverette59e53a12016-03-28 13:41:32 -0400130 }
131 }
132
Svetoslavf7174e82014-06-12 11:29:35 -0700133 public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800134 if (mDisplayMagnifier != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700135 mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle);
Svetoslav8e3feb12014-02-24 13:46:47 -0800136 }
137 // Not relevant for the window observer.
138 }
139
140 public void onWindowLayersChangedLocked() {
141 if (mDisplayMagnifier != null) {
142 mDisplayMagnifier.onWindowLayersChangedLocked();
143 }
144 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700145 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800146 }
147 }
148
149 public void onRotationChangedLocked(DisplayContent displayContent, int rotation) {
150 if (mDisplayMagnifier != null) {
151 mDisplayMagnifier.onRotationChangedLocked(displayContent, rotation);
152 }
153 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700154 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800155 }
156 }
157
158 public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
159 if (mDisplayMagnifier != null) {
160 mDisplayMagnifier.onAppWindowTransitionLocked(windowState, transition);
161 }
162 // Not relevant for the window observer.
163 }
164
165 public void onWindowTransitionLocked(WindowState windowState, int transition) {
166 if (mDisplayMagnifier != null) {
167 mDisplayMagnifier.onWindowTransitionLocked(windowState, transition);
168 }
169 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700170 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800171 }
172 }
173
Svetoslav3a0d8782014-12-04 12:50:11 -0800174 public void onWindowFocusChangedNotLocked() {
Svetoslav8e3feb12014-02-24 13:46:47 -0800175 // Not relevant for the display magnifier.
176
Svetoslav3a0d8782014-12-04 12:50:11 -0800177 WindowsForAccessibilityObserver observer = null;
178 synchronized (mWindowManagerService) {
179 observer = mWindowsForAccessibilityObserver;
180 }
181 if (observer != null) {
182 observer.performComputeChangedWindowsNotLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800183 }
184 }
185
Svetoslav4604abc2014-06-10 18:59:30 -0700186
Svetoslavf7174e82014-06-12 11:29:35 -0700187 public void onSomeWindowResizedOrMovedLocked() {
Svetoslav4604abc2014-06-10 18:59:30 -0700188 // Not relevant for the display magnifier.
189
190 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700191 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav4604abc2014-06-10 18:59:30 -0700192 }
193 }
194
Svetoslav8e3feb12014-02-24 13:46:47 -0800195 /** NOTE: This has to be called within a surface transaction. */
196 public void drawMagnifiedRegionBorderIfNeededLocked() {
197 if (mDisplayMagnifier != null) {
198 mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked();
199 }
200 // Not relevant for the window observer.
201 }
202
203 public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
204 if (mDisplayMagnifier != null) {
205 return mDisplayMagnifier.getMagnificationSpecForWindowLocked(windowState);
206 }
207 return null;
208 }
209
210 public boolean hasCallbacksLocked() {
211 return (mDisplayMagnifier != null
212 || mWindowsForAccessibilityObserver != null);
213 }
214
215 private static void populateTransformationMatrixLocked(WindowState windowState,
216 Matrix outMatrix) {
217 sTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx;
218 sTempFloats[Matrix.MSKEW_Y] = windowState.mWinAnimator.mDtDx;
219 sTempFloats[Matrix.MSKEW_X] = windowState.mWinAnimator.mDsDy;
220 sTempFloats[Matrix.MSCALE_Y] = windowState.mWinAnimator.mDtDy;
Filip Gruszczynski2a6a2c22015-10-14 12:00:53 -0700221 sTempFloats[Matrix.MTRANS_X] = windowState.mShownPosition.x;
222 sTempFloats[Matrix.MTRANS_Y] = windowState.mShownPosition.y;
Svetoslav8e3feb12014-02-24 13:46:47 -0800223 sTempFloats[Matrix.MPERSP_0] = 0;
224 sTempFloats[Matrix.MPERSP_1] = 0;
225 sTempFloats[Matrix.MPERSP_2] = 1;
226 outMatrix.setValues(sTempFloats);
227 }
228
229 /**
230 * This class encapsulates the functionality related to display magnification.
231 */
232 private static final class DisplayMagnifier {
233
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -0800234 private static final String LOG_TAG = TAG_WITH_CLASS_NAME ? "DisplayMagnifier" : TAG_WM;
Svetoslav8e3feb12014-02-24 13:46:47 -0800235
236 private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
237 private static final boolean DEBUG_ROTATION = false;
238 private static final boolean DEBUG_LAYERS = false;
239 private static final boolean DEBUG_RECTANGLE_REQUESTED = false;
240 private static final boolean DEBUG_VIEWPORT_WINDOW = false;
241
242 private final Rect mTempRect1 = new Rect();
243 private final Rect mTempRect2 = new Rect();
244
245 private final Region mTempRegion1 = new Region();
246 private final Region mTempRegion2 = new Region();
247 private final Region mTempRegion3 = new Region();
248 private final Region mTempRegion4 = new Region();
249
250 private final Context mContext;
251 private final WindowManagerService mWindowManagerService;
252 private final MagnifiedViewport mMagnifedViewport;
253 private final Handler mHandler;
254
255 private final MagnificationCallbacks mCallbacks;
256
257 private final long mLongAnimationDuration;
258
259 public DisplayMagnifier(WindowManagerService windowManagerService,
260 MagnificationCallbacks callbacks) {
261 mContext = windowManagerService.mContext;
262 mWindowManagerService = windowManagerService;
263 mCallbacks = callbacks;
264 mHandler = new MyHandler(mWindowManagerService.mH.getLooper());
265 mMagnifedViewport = new MagnifiedViewport();
266 mLongAnimationDuration = mContext.getResources().getInteger(
267 com.android.internal.R.integer.config_longAnimTime);
268 }
269
270 public void setMagnificationSpecLocked(MagnificationSpec spec) {
271 mMagnifedViewport.updateMagnificationSpecLocked(spec);
272 mMagnifedViewport.recomputeBoundsLocked();
273 mWindowManagerService.scheduleAnimationLocked();
274 }
275
Svetoslavf7174e82014-06-12 11:29:35 -0700276 public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800277 if (DEBUG_RECTANGLE_REQUESTED) {
278 Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
279 }
280 if (!mMagnifedViewport.isMagnifyingLocked()) {
281 return;
282 }
283 Rect magnifiedRegionBounds = mTempRect2;
284 mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds);
285 if (magnifiedRegionBounds.contains(rectangle)) {
286 return;
287 }
288 SomeArgs args = SomeArgs.obtain();
289 args.argi1 = rectangle.left;
290 args.argi2 = rectangle.top;
291 args.argi3 = rectangle.right;
292 args.argi4 = rectangle.bottom;
293 mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED,
294 args).sendToTarget();
295 }
296
297 public void onWindowLayersChangedLocked() {
298 if (DEBUG_LAYERS) {
299 Slog.i(LOG_TAG, "Layers changed.");
300 }
301 mMagnifedViewport.recomputeBoundsLocked();
302 mWindowManagerService.scheduleAnimationLocked();
303 }
304
305 public void onRotationChangedLocked(DisplayContent displayContent, int rotation) {
306 if (DEBUG_ROTATION) {
307 Slog.i(LOG_TAG, "Rotaton: " + Surface.rotationToString(rotation)
308 + " displayId: " + displayContent.getDisplayId());
309 }
310 mMagnifedViewport.onRotationChangedLocked();
311 mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
312 }
313
314 public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
315 if (DEBUG_WINDOW_TRANSITIONS) {
316 Slog.i(LOG_TAG, "Window transition: "
317 + AppTransition.appTransitionToString(transition)
318 + " displayId: " + windowState.getDisplayId());
319 }
320 final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
321 if (magnifying) {
322 switch (transition) {
323 case AppTransition.TRANSIT_ACTIVITY_OPEN:
324 case AppTransition.TRANSIT_TASK_OPEN:
325 case AppTransition.TRANSIT_TASK_TO_FRONT:
326 case AppTransition.TRANSIT_WALLPAPER_OPEN:
327 case AppTransition.TRANSIT_WALLPAPER_CLOSE:
328 case AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN: {
329 mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED);
330 }
331 }
332 }
333 }
334
335 public void onWindowTransitionLocked(WindowState windowState, int transition) {
336 if (DEBUG_WINDOW_TRANSITIONS) {
337 Slog.i(LOG_TAG, "Window transition: "
338 + AppTransition.appTransitionToString(transition)
339 + " displayId: " + windowState.getDisplayId());
340 }
341 final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
342 final int type = windowState.mAttrs.type;
343 switch (transition) {
344 case WindowManagerPolicy.TRANSIT_ENTER:
345 case WindowManagerPolicy.TRANSIT_SHOW: {
346 if (!magnifying) {
347 break;
348 }
349 switch (type) {
350 case WindowManager.LayoutParams.TYPE_APPLICATION:
Chong Zhangfea963e2016-08-15 17:14:16 -0700351 case WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION:
Svetoslav8e3feb12014-02-24 13:46:47 -0800352 case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
353 case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
354 case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
Wale Ogunwale0a4dc222015-04-14 12:58:42 -0700355 case WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL:
Svetoslav8e3feb12014-02-24 13:46:47 -0800356 case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
357 case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
358 case WindowManager.LayoutParams.TYPE_PHONE:
359 case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
360 case WindowManager.LayoutParams.TYPE_TOAST:
361 case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
362 case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
363 case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
364 case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
365 case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
366 case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
Jason Monk8f7f3182015-11-18 16:35:14 -0500367 case WindowManager.LayoutParams.TYPE_QS_DIALOG:
Adrian Roos9a645132014-10-08 02:59:56 +0200368 case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: {
Svetoslav8e3feb12014-02-24 13:46:47 -0800369 Rect magnifiedRegionBounds = mTempRect2;
370 mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(
371 magnifiedRegionBounds);
372 Rect touchableRegionBounds = mTempRect1;
373 windowState.getTouchableRegion(mTempRegion1);
374 mTempRegion1.getBounds(touchableRegionBounds);
375 if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) {
376 mCallbacks.onRectangleOnScreenRequested(
377 touchableRegionBounds.left,
378 touchableRegionBounds.top,
379 touchableRegionBounds.right,
380 touchableRegionBounds.bottom);
381 }
382 } break;
383 } break;
384 }
385 }
386 }
387
388 public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
389 MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
390 if (spec != null && !spec.isNop()) {
391 WindowManagerPolicy policy = mWindowManagerService.mPolicy;
392 final int windowType = windowState.mAttrs.type;
393 if (!policy.isTopLevelWindow(windowType) && windowState.mAttachedWindow != null
394 && !policy.canMagnifyWindow(windowType)) {
395 return null;
396 }
397 if (!policy.canMagnifyWindow(windowState.mAttrs.type)) {
398 return null;
399 }
400 }
401 return spec;
402 }
403
Phil Weaver70439242016-03-10 15:15:49 -0800404 public void getMagnificationRegionLocked(Region outMagnificationRegion) {
405 mMagnifedViewport.getMagnificationRegionLocked(outMagnificationRegion);
Alan Viverette59e53a12016-03-28 13:41:32 -0400406 }
407
Svetoslav8e3feb12014-02-24 13:46:47 -0800408 public void destroyLocked() {
409 mMagnifedViewport.destroyWindow();
410 }
411
412 /** NOTE: This has to be called within a surface transaction. */
413 public void drawMagnifiedRegionBorderIfNeededLocked() {
414 mMagnifedViewport.drawWindowIfNeededLocked();
415 }
416
417 private final class MagnifiedViewport {
418
Svetoslav8e3feb12014-02-24 13:46:47 -0800419 private final SparseArray<WindowState> mTempWindowStates =
420 new SparseArray<WindowState>();
421
422 private final RectF mTempRectF = new RectF();
423
424 private final Point mTempPoint = new Point();
425
426 private final Matrix mTempMatrix = new Matrix();
427
Phil Weaver70439242016-03-10 15:15:49 -0800428 private final Region mMagnificationRegion = new Region();
429 private final Region mOldMagnificationRegion = new Region();
Svetoslav8e3feb12014-02-24 13:46:47 -0800430
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800431 private final Path mCircularPath;
432
Svetoslav8e3feb12014-02-24 13:46:47 -0800433 private final MagnificationSpec mMagnificationSpec = MagnificationSpec.obtain();
434
435 private final WindowManager mWindowManager;
436
Svetoslav7505e332014-08-22 12:14:28 -0700437 private final float mBorderWidth;
Svetoslav8e3feb12014-02-24 13:46:47 -0800438 private final int mHalfBorderWidth;
Svetoslav7505e332014-08-22 12:14:28 -0700439 private final int mDrawBorderInset;
Svetoslav8e3feb12014-02-24 13:46:47 -0800440
441 private final ViewportWindow mWindow;
442
443 private boolean mFullRedrawNeeded;
444
445 public MagnifiedViewport() {
446 mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE);
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800447 mBorderWidth = mContext.getResources().getDimension(
448 com.android.internal.R.dimen.accessibility_magnification_indicator_width);
Svetoslav7505e332014-08-22 12:14:28 -0700449 mHalfBorderWidth = (int) Math.ceil(mBorderWidth / 2);
450 mDrawBorderInset = (int) mBorderWidth / 2;
Svetoslav8e3feb12014-02-24 13:46:47 -0800451 mWindow = new ViewportWindow(mContext);
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800452
Adam Powell01f280d2015-05-18 16:07:42 -0700453 if (mContext.getResources().getConfiguration().isScreenRound()) {
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800454 mCircularPath = new Path();
455 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
456 final int centerXY = mTempPoint.x / 2;
457 mCircularPath.addCircle(centerXY, centerXY, centerXY, Path.Direction.CW);
458 } else {
459 mCircularPath = null;
460 }
461
Svetoslav8e3feb12014-02-24 13:46:47 -0800462 recomputeBoundsLocked();
463 }
464
Phil Weaver70439242016-03-10 15:15:49 -0800465 public void getMagnificationRegionLocked(@NonNull Region outMagnificationRegion) {
466 outMagnificationRegion.set(mMagnificationRegion);
Alan Viverette59e53a12016-03-28 13:41:32 -0400467 }
468
Svetoslav8e3feb12014-02-24 13:46:47 -0800469 public void updateMagnificationSpecLocked(MagnificationSpec spec) {
470 if (spec != null) {
471 mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY);
472 } else {
473 mMagnificationSpec.clear();
474 }
475 // If this message is pending we are in a rotation animation and do not want
476 // to show the border. We will do so when the pending message is handled.
Svetoslav7505e332014-08-22 12:14:28 -0700477 if (!mHandler.hasMessages(
478 MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800479 setMagnifiedRegionBorderShownLocked(isMagnifyingLocked(), true);
480 }
481 }
482
483 public void recomputeBoundsLocked() {
484 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
485 final int screenWidth = mTempPoint.x;
486 final int screenHeight = mTempPoint.y;
487
Phil Weaver70439242016-03-10 15:15:49 -0800488 mMagnificationRegion.set(0, 0, 0, 0);
489 final Region availableBounds = mTempRegion1;
490 availableBounds.set(0, 0, screenWidth, screenHeight);
Svetoslav8e3feb12014-02-24 13:46:47 -0800491
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800492 if (mCircularPath != null) {
Phil Weaver70439242016-03-10 15:15:49 -0800493 availableBounds.setPath(mCircularPath, availableBounds);
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800494 }
495
Svetoslav8e3feb12014-02-24 13:46:47 -0800496 Region nonMagnifiedBounds = mTempRegion4;
Svet Ganovb21df802014-09-01 19:06:33 -0700497 nonMagnifiedBounds.set(0, 0, 0, 0);
Svetoslav8e3feb12014-02-24 13:46:47 -0800498
499 SparseArray<WindowState> visibleWindows = mTempWindowStates;
500 visibleWindows.clear();
501 populateWindowsOnScreenLocked(visibleWindows);
502
503 final int visibleWindowCount = visibleWindows.size();
504 for (int i = visibleWindowCount - 1; i >= 0; i--) {
505 WindowState windowState = visibleWindows.valueAt(i);
506 if (windowState.mAttrs.type == WindowManager
507 .LayoutParams.TYPE_MAGNIFICATION_OVERLAY) {
508 continue;
509 }
510
Phil Weaver65c06702016-03-15 15:33:46 -0700511 // Consider the touchable portion of the window
Svetoslav8e3feb12014-02-24 13:46:47 -0800512 Matrix matrix = mTempMatrix;
513 populateTransformationMatrixLocked(windowState, matrix);
Phil Weaver65c06702016-03-15 15:33:46 -0700514 Region touchableRegion = mTempRegion3;
515 windowState.getTouchableRegion(touchableRegion);
516 Rect touchableFrame = mTempRect1;
517 touchableRegion.getBounds(touchableFrame);
Svetoslav8e3feb12014-02-24 13:46:47 -0800518 RectF windowFrame = mTempRectF;
Phil Weaver65c06702016-03-15 15:33:46 -0700519 windowFrame.set(touchableFrame);
520 windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
521 matrix.mapRect(windowFrame);
522 Region windowBounds = mTempRegion2;
523 windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
524 (int) windowFrame.right, (int) windowFrame.bottom);
525 // Only update new regions
526 Region portionOfWindowAlreadyAccountedFor = mTempRegion3;
Phil Weaver70439242016-03-10 15:15:49 -0800527 portionOfWindowAlreadyAccountedFor.set(mMagnificationRegion);
Phil Weaver65c06702016-03-15 15:33:46 -0700528 portionOfWindowAlreadyAccountedFor.op(nonMagnifiedBounds, Region.Op.UNION);
529 windowBounds.op(portionOfWindowAlreadyAccountedFor, Region.Op.DIFFERENCE);
Svetoslav8e3feb12014-02-24 13:46:47 -0800530
531 if (mWindowManagerService.mPolicy.canMagnifyWindow(windowState.mAttrs.type)) {
Phil Weaver70439242016-03-10 15:15:49 -0800532 mMagnificationRegion.op(windowBounds, Region.Op.UNION);
533 mMagnificationRegion.op(availableBounds, Region.Op.INTERSECT);
Svetoslav8e3feb12014-02-24 13:46:47 -0800534 } else {
Svetoslav8e3feb12014-02-24 13:46:47 -0800535 nonMagnifiedBounds.op(windowBounds, Region.Op.UNION);
Phil Weaver70439242016-03-10 15:15:49 -0800536 availableBounds.op(windowBounds, Region.Op.DIFFERENCE);
Svetoslav8e3feb12014-02-24 13:46:47 -0800537 }
538
Phil Weaver65c06702016-03-15 15:33:46 -0700539 // Update accounted bounds
Svetoslav8e3feb12014-02-24 13:46:47 -0800540 Region accountedBounds = mTempRegion2;
Phil Weaver70439242016-03-10 15:15:49 -0800541 accountedBounds.set(mMagnificationRegion);
Svetoslav8e3feb12014-02-24 13:46:47 -0800542 accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION);
543 accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT);
544
545 if (accountedBounds.isRect()) {
546 Rect accountedFrame = mTempRect1;
547 accountedBounds.getBounds(accountedFrame);
548 if (accountedFrame.width() == screenWidth
549 && accountedFrame.height() == screenHeight) {
550 break;
551 }
552 }
553 }
554
555 visibleWindows.clear();
556
Phil Weaver70439242016-03-10 15:15:49 -0800557 mMagnificationRegion.op(mDrawBorderInset, mDrawBorderInset,
Svetoslav7505e332014-08-22 12:14:28 -0700558 screenWidth - mDrawBorderInset, screenHeight - mDrawBorderInset,
Svetoslav8e3feb12014-02-24 13:46:47 -0800559 Region.Op.INTERSECT);
560
Phil Weaver70439242016-03-10 15:15:49 -0800561 final boolean magnifiedChanged =
562 !mOldMagnificationRegion.equals(mMagnificationRegion);
563 if (magnifiedChanged) {
564 mWindow.setBounds(mMagnificationRegion);
565 final Rect dirtyRect = mTempRect1;
566 if (mFullRedrawNeeded) {
567 mFullRedrawNeeded = false;
568 dirtyRect.set(mDrawBorderInset, mDrawBorderInset,
569 screenWidth - mDrawBorderInset,
570 screenHeight - mDrawBorderInset);
571 mWindow.invalidate(dirtyRect);
572 } else {
573 final Region dirtyRegion = mTempRegion3;
574 dirtyRegion.set(mMagnificationRegion);
575 dirtyRegion.op(mOldMagnificationRegion, Region.Op.UNION);
576 dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT);
577 dirtyRegion.getBounds(dirtyRect);
578 mWindow.invalidate(dirtyRect);
Svetoslav8e3feb12014-02-24 13:46:47 -0800579 }
580
Phil Weaver70439242016-03-10 15:15:49 -0800581 mOldMagnificationRegion.set(mMagnificationRegion);
Alan Viverette214fb682015-11-17 09:47:11 -0500582 final SomeArgs args = SomeArgs.obtain();
Phil Weaver70439242016-03-10 15:15:49 -0800583 args.arg1 = Region.obtain(mMagnificationRegion);
Alan Viverette214fb682015-11-17 09:47:11 -0500584 mHandler.obtainMessage(
Phil Weaver70439242016-03-10 15:15:49 -0800585 MyHandler.MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED, args)
586 .sendToTarget();
Svetoslav8e3feb12014-02-24 13:46:47 -0800587 }
588 }
589
590 public void onRotationChangedLocked() {
591 // If we are magnifying, hide the magnified border window immediately so
592 // the user does not see strange artifacts during rotation. The screenshot
593 // used for rotation has already the border. After the rotation is complete
594 // we will show the border.
595 if (isMagnifyingLocked()) {
596 setMagnifiedRegionBorderShownLocked(false, false);
597 final long delay = (long) (mLongAnimationDuration
Dianne Hackborneb94fa72014-06-03 17:48:12 -0700598 * mWindowManagerService.getWindowAnimationScaleLocked());
Svetoslav8e3feb12014-02-24 13:46:47 -0800599 Message message = mHandler.obtainMessage(
600 MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED);
601 mHandler.sendMessageDelayed(message, delay);
602 }
603 recomputeBoundsLocked();
604 mWindow.updateSize();
605 }
606
607 public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) {
608 if (shown) {
609 mFullRedrawNeeded = true;
Phil Weaver70439242016-03-10 15:15:49 -0800610 mOldMagnificationRegion.set(0, 0, 0, 0);
Svetoslav8e3feb12014-02-24 13:46:47 -0800611 }
612 mWindow.setShown(shown, animate);
613 }
614
615 public void getMagnifiedFrameInContentCoordsLocked(Rect rect) {
616 MagnificationSpec spec = mMagnificationSpec;
Phil Weaver70439242016-03-10 15:15:49 -0800617 mMagnificationRegion.getBounds(rect);
Svetoslav8e3feb12014-02-24 13:46:47 -0800618 rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
619 rect.scale(1.0f / spec.scale);
620 }
621
622 public boolean isMagnifyingLocked() {
623 return mMagnificationSpec.scale > 1.0f;
624 }
625
626 public MagnificationSpec getMagnificationSpecLocked() {
627 return mMagnificationSpec;
628 }
629
630 /** NOTE: This has to be called within a surface transaction. */
631 public void drawWindowIfNeededLocked() {
632 recomputeBoundsLocked();
633 mWindow.drawIfNeeded();
634 }
635
636 public void destroyWindow() {
637 mWindow.releaseSurface();
638 }
639
640 private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
641 DisplayContent displayContent = mWindowManagerService
642 .getDefaultDisplayContentLocked();
643 WindowList windowList = displayContent.getWindowList();
644 final int windowCount = windowList.size();
645 for (int i = 0; i < windowCount; i++) {
646 WindowState windowState = windowList.get(i);
Craig Mautner165be0c2015-01-27 15:16:58 -0800647 if (windowState.isOnScreen() &&
648 !windowState.mWinAnimator.mEnterAnimationPending) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800649 outWindows.put(windowState.mLayer, windowState);
650 }
651 }
652 }
653
654 private final class ViewportWindow {
655 private static final String SURFACE_TITLE = "Magnification Overlay";
656
Svetoslav8e3feb12014-02-24 13:46:47 -0800657 private final Region mBounds = new Region();
658 private final Rect mDirtyRect = new Rect();
659 private final Paint mPaint = new Paint();
660
Svetoslav8e3feb12014-02-24 13:46:47 -0800661 private final SurfaceControl mSurfaceControl;
662 private final Surface mSurface = new Surface();
663
Svet Ganovb21df802014-09-01 19:06:33 -0700664 private final AnimationController mAnimationController;
665
Svetoslav8e3feb12014-02-24 13:46:47 -0800666 private boolean mShown;
667 private int mAlpha;
668
669 private boolean mInvalidated;
670
671 public ViewportWindow(Context context) {
672 SurfaceControl surfaceControl = null;
673 try {
674 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
675 surfaceControl = new SurfaceControl(mWindowManagerService.mFxSession,
676 SURFACE_TITLE, mTempPoint.x, mTempPoint.y, PixelFormat.TRANSLUCENT,
677 SurfaceControl.HIDDEN);
678 } catch (OutOfResourcesException oore) {
679 /* ignore */
680 }
681 mSurfaceControl = surfaceControl;
682 mSurfaceControl.setLayerStack(mWindowManager.getDefaultDisplay()
683 .getLayerStack());
684 mSurfaceControl.setLayer(mWindowManagerService.mPolicy.windowTypeToLayerLw(
685 WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY)
686 * WindowManagerService.TYPE_LAYER_MULTIPLIER);
687 mSurfaceControl.setPosition(0, 0);
688 mSurface.copyFrom(mSurfaceControl);
689
Svet Ganovb21df802014-09-01 19:06:33 -0700690 mAnimationController = new AnimationController(context,
691 mWindowManagerService.mH.getLooper());
692
Svetoslav8e3feb12014-02-24 13:46:47 -0800693 TypedValue typedValue = new TypedValue();
694 context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight,
695 typedValue, true);
Alan Viverette4a357cd2015-03-18 18:37:18 -0700696 final int borderColor = context.getColor(typedValue.resourceId);
Svetoslav8e3feb12014-02-24 13:46:47 -0800697
698 mPaint.setStyle(Paint.Style.STROKE);
699 mPaint.setStrokeWidth(mBorderWidth);
700 mPaint.setColor(borderColor);
701
Svetoslav8e3feb12014-02-24 13:46:47 -0800702 mInvalidated = true;
703 }
704
705 public void setShown(boolean shown, boolean animate) {
706 synchronized (mWindowManagerService.mWindowMap) {
707 if (mShown == shown) {
708 return;
709 }
710 mShown = shown;
Svet Ganovb21df802014-09-01 19:06:33 -0700711 mAnimationController.onFrameShownStateChanged(shown, animate);
Svetoslav8e3feb12014-02-24 13:46:47 -0800712 if (DEBUG_VIEWPORT_WINDOW) {
713 Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown);
714 }
715 }
716 }
717
718 @SuppressWarnings("unused")
719 // Called reflectively from an animator.
720 public int getAlpha() {
721 synchronized (mWindowManagerService.mWindowMap) {
722 return mAlpha;
723 }
724 }
725
726 public void setAlpha(int alpha) {
727 synchronized (mWindowManagerService.mWindowMap) {
728 if (mAlpha == alpha) {
729 return;
730 }
731 mAlpha = alpha;
732 invalidate(null);
733 if (DEBUG_VIEWPORT_WINDOW) {
734 Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha);
735 }
736 }
737 }
738
739 public void setBounds(Region bounds) {
740 synchronized (mWindowManagerService.mWindowMap) {
741 if (mBounds.equals(bounds)) {
742 return;
743 }
744 mBounds.set(bounds);
745 invalidate(mDirtyRect);
746 if (DEBUG_VIEWPORT_WINDOW) {
747 Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds);
748 }
749 }
750 }
751
752 public void updateSize() {
753 synchronized (mWindowManagerService.mWindowMap) {
754 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
755 mSurfaceControl.setSize(mTempPoint.x, mTempPoint.y);
756 invalidate(mDirtyRect);
757 }
758 }
759
760 public void invalidate(Rect dirtyRect) {
761 if (dirtyRect != null) {
762 mDirtyRect.set(dirtyRect);
763 } else {
764 mDirtyRect.setEmpty();
765 }
766 mInvalidated = true;
767 mWindowManagerService.scheduleAnimationLocked();
768 }
769
770 /** NOTE: This has to be called within a surface transaction. */
771 public void drawIfNeeded() {
772 synchronized (mWindowManagerService.mWindowMap) {
773 if (!mInvalidated) {
774 return;
775 }
776 mInvalidated = false;
777 Canvas canvas = null;
778 try {
779 // Empty dirty rectangle means unspecified.
780 if (mDirtyRect.isEmpty()) {
781 mBounds.getBounds(mDirtyRect);
782 }
783 mDirtyRect.inset(- mHalfBorderWidth, - mHalfBorderWidth);
784 canvas = mSurface.lockCanvas(mDirtyRect);
785 if (DEBUG_VIEWPORT_WINDOW) {
786 Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect);
787 }
788 } catch (IllegalArgumentException iae) {
789 /* ignore */
790 } catch (Surface.OutOfResourcesException oore) {
791 /* ignore */
792 }
793 if (canvas == null) {
794 return;
795 }
796 if (DEBUG_VIEWPORT_WINDOW) {
797 Slog.i(LOG_TAG, "Bounds: " + mBounds);
798 }
799 canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
800 mPaint.setAlpha(mAlpha);
801 Path path = mBounds.getBoundaryPath();
802 canvas.drawPath(path, mPaint);
803
804 mSurface.unlockCanvasAndPost(canvas);
805
806 if (mAlpha > 0) {
807 mSurfaceControl.show();
808 } else {
809 mSurfaceControl.hide();
810 }
811 }
812 }
813
814 public void releaseSurface() {
815 mSurfaceControl.release();
816 mSurface.release();
817 }
Svet Ganovb21df802014-09-01 19:06:33 -0700818
819 private final class AnimationController extends Handler {
820 private static final String PROPERTY_NAME_ALPHA = "alpha";
821
822 private static final int MIN_ALPHA = 0;
823 private static final int MAX_ALPHA = 255;
824
825 private static final int MSG_FRAME_SHOWN_STATE_CHANGED = 1;
826
827 private final ValueAnimator mShowHideFrameAnimator;
828
829 public AnimationController(Context context, Looper looper) {
830 super(looper);
831 mShowHideFrameAnimator = ObjectAnimator.ofInt(ViewportWindow.this,
832 PROPERTY_NAME_ALPHA, MIN_ALPHA, MAX_ALPHA);
833
834 Interpolator interpolator = new DecelerateInterpolator(2.5f);
835 final long longAnimationDuration = context.getResources().getInteger(
836 com.android.internal.R.integer.config_longAnimTime);
837
838 mShowHideFrameAnimator.setInterpolator(interpolator);
839 mShowHideFrameAnimator.setDuration(longAnimationDuration);
840 }
841
842 public void onFrameShownStateChanged(boolean shown, boolean animate) {
843 obtainMessage(MSG_FRAME_SHOWN_STATE_CHANGED,
844 shown ? 1 : 0, animate ? 1 : 0).sendToTarget();
845 }
846
847 @Override
848 public void handleMessage(Message message) {
849 switch (message.what) {
850 case MSG_FRAME_SHOWN_STATE_CHANGED: {
851 final boolean shown = message.arg1 == 1;
852 final boolean animate = message.arg2 == 1;
853
854 if (animate) {
855 if (mShowHideFrameAnimator.isRunning()) {
856 mShowHideFrameAnimator.reverse();
857 } else {
858 if (shown) {
859 mShowHideFrameAnimator.start();
860 } else {
861 mShowHideFrameAnimator.reverse();
862 }
863 }
864 } else {
865 mShowHideFrameAnimator.cancel();
866 if (shown) {
867 setAlpha(MAX_ALPHA);
868 } else {
869 setAlpha(MIN_ALPHA);
870 }
871 }
872 } break;
873 }
874 }
875 }
Svetoslav8e3feb12014-02-24 13:46:47 -0800876 }
877 }
878
879 private class MyHandler extends Handler {
Phil Weaver70439242016-03-10 15:15:49 -0800880 public static final int MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED = 1;
Svetoslav8e3feb12014-02-24 13:46:47 -0800881 public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2;
882 public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
883 public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
884 public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
885
886 public MyHandler(Looper looper) {
887 super(looper);
888 }
889
890 @Override
891 public void handleMessage(Message message) {
892 switch (message.what) {
Phil Weaver70439242016-03-10 15:15:49 -0800893 case MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED: {
Alan Viverette214fb682015-11-17 09:47:11 -0500894 final SomeArgs args = (SomeArgs) message.obj;
895 final Region magnifiedBounds = (Region) args.arg1;
Phil Weaver70439242016-03-10 15:15:49 -0800896 mCallbacks.onMagnificationRegionChanged(magnifiedBounds);
Alan Viverette214fb682015-11-17 09:47:11 -0500897 magnifiedBounds.recycle();
Svetoslav8e3feb12014-02-24 13:46:47 -0800898 } break;
899
900 case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: {
901 SomeArgs args = (SomeArgs) message.obj;
902 final int left = args.argi1;
903 final int top = args.argi2;
904 final int right = args.argi3;
905 final int bottom = args.argi4;
906 mCallbacks.onRectangleOnScreenRequested(left, top, right, bottom);
907 args.recycle();
908 } break;
909
910 case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: {
911 mCallbacks.onUserContextChanged();
912 } break;
913
914 case MESSAGE_NOTIFY_ROTATION_CHANGED: {
915 final int rotation = message.arg1;
916 mCallbacks.onRotationChanged(rotation);
917 } break;
918
919 case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
920 synchronized (mWindowManagerService.mWindowMap) {
921 if (mMagnifedViewport.isMagnifyingLocked()) {
922 mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true);
923 mWindowManagerService.scheduleAnimationLocked();
924 }
925 }
926 } break;
927 }
928 }
929 }
930 }
931
932 /**
933 * This class encapsulates the functionality related to computing the windows
934 * reported for accessibility purposes. These windows are all windows a sighted
935 * user can see on the screen.
936 */
937 private static final class WindowsForAccessibilityObserver {
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -0800938 private static final String LOG_TAG = TAG_WITH_CLASS_NAME ?
939 "WindowsForAccessibilityObserver" : TAG_WM;
Svetoslav8e3feb12014-02-24 13:46:47 -0800940
941 private static final boolean DEBUG = false;
942
943 private final SparseArray<WindowState> mTempWindowStates =
944 new SparseArray<WindowState>();
945
946 private final List<WindowInfo> mOldWindows = new ArrayList<WindowInfo>();
947
948 private final Set<IBinder> mTempBinderSet = new ArraySet<IBinder>();
949
950 private final RectF mTempRectF = new RectF();
951
952 private final Matrix mTempMatrix = new Matrix();
953
954 private final Point mTempPoint = new Point();
955
956 private final Rect mTempRect = new Rect();
957
958 private final Region mTempRegion = new Region();
959
960 private final Region mTempRegion1 = new Region();
961
962 private final Context mContext;
963
964 private final WindowManagerService mWindowManagerService;
965
966 private final Handler mHandler;
967
968 private final WindowsForAccessibilityCallback mCallback;
969
Svetoslavf7174e82014-06-12 11:29:35 -0700970 private final long mRecurringAccessibilityEventsIntervalMillis;
971
Svetoslav8e3feb12014-02-24 13:46:47 -0800972 public WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
973 WindowsForAccessibilityCallback callback) {
974 mContext = windowManagerService.mContext;
975 mWindowManagerService = windowManagerService;
976 mCallback = callback;
977 mHandler = new MyHandler(mWindowManagerService.mH.getLooper());
Svetoslavf7174e82014-06-12 11:29:35 -0700978 mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration
979 .getSendRecurringAccessibilityEventsInterval();
Svetoslav8e3feb12014-02-24 13:46:47 -0800980 computeChangedWindows();
981 }
982
Svetoslav3a0d8782014-12-04 12:50:11 -0800983 public void performComputeChangedWindowsNotLocked() {
984 mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS);
985 computeChangedWindows();
986 }
987
Svetoslavf7174e82014-06-12 11:29:35 -0700988 public void scheduleComputeChangedWindowsLocked() {
Svetoslav3a0d8782014-12-04 12:50:11 -0800989 if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) {
Svetoslavf7174e82014-06-12 11:29:35 -0700990 mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS,
991 mRecurringAccessibilityEventsIntervalMillis);
992 }
993 }
994
Svetoslav8e3feb12014-02-24 13:46:47 -0800995 public void computeChangedWindows() {
996 if (DEBUG) {
997 Slog.i(LOG_TAG, "computeChangedWindows()");
998 }
999
Svetoslav3a0d8782014-12-04 12:50:11 -08001000 boolean windowsChanged = false;
1001 List<WindowInfo> windows = new ArrayList<WindowInfo>();
1002
Svetoslav8e3feb12014-02-24 13:46:47 -08001003 synchronized (mWindowManagerService.mWindowMap) {
Svetoslavf7174e82014-06-12 11:29:35 -07001004 // Do not send the windows if there is no current focus as
1005 // the window manager is still looking for where to put it.
1006 // We will do the work when we get a focus change callback.
1007 if (mWindowManagerService.mCurrentFocus == null) {
1008 return;
1009 }
1010
Svetoslav8e3feb12014-02-24 13:46:47 -08001011 WindowManager windowManager = (WindowManager)
1012 mContext.getSystemService(Context.WINDOW_SERVICE);
1013 windowManager.getDefaultDisplay().getRealSize(mTempPoint);
1014 final int screenWidth = mTempPoint.x;
1015 final int screenHeight = mTempPoint.y;
1016
1017 Region unaccountedSpace = mTempRegion;
1018 unaccountedSpace.set(0, 0, screenWidth, screenHeight);
1019
1020 SparseArray<WindowState> visibleWindows = mTempWindowStates;
1021 populateVisibleWindowsOnScreenLocked(visibleWindows);
1022
Svetoslav8e3feb12014-02-24 13:46:47 -08001023 Set<IBinder> addedWindows = mTempBinderSet;
1024 addedWindows.clear();
1025
Svetoslavf7174e82014-06-12 11:29:35 -07001026 boolean focusedWindowAdded = false;
1027
Svetoslav8e3feb12014-02-24 13:46:47 -08001028 final int visibleWindowCount = visibleWindows.size();
Allen Hairf20ac2c2016-02-11 17:42:59 -08001029 int skipRemainingWindowsForTaskId = -1;
1030 HashSet<Integer> skipRemainingWindowsForTasks = new HashSet<>();
Svetoslav8e3feb12014-02-24 13:46:47 -08001031 for (int i = visibleWindowCount - 1; i >= 0; i--) {
Alan Viverette9538eea2014-11-13 14:49:20 -08001032 final WindowState windowState = visibleWindows.valueAt(i);
Svetoslav8e3feb12014-02-24 13:46:47 -08001033 final int flags = windowState.mAttrs.flags;
Allen Hairf20ac2c2016-02-11 17:42:59 -08001034 final Task task = windowState.getTask();
1035
1036 // If the window is part of a task that we're finished with - ignore.
1037 if (task != null && skipRemainingWindowsForTasks.contains(task.mTaskId)) {
1038 continue;
1039 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001040
Svetoslav3a5c7212014-10-14 09:54:26 -07001041 // If the window is not touchable - ignore.
Svetoslavf7174e82014-06-12 11:29:35 -07001042 if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001043 continue;
1044 }
1045
Alan Viverette9538eea2014-11-13 14:49:20 -08001046 // Compute the bounds in the screen.
1047 final Rect boundsInScreen = mTempRect;
1048 computeWindowBoundsInScreen(windowState, boundsInScreen);
1049
Svetoslav8e3feb12014-02-24 13:46:47 -08001050 // If the window is completely covered by other windows - ignore.
1051 if (unaccountedSpace.quickReject(boundsInScreen)) {
1052 continue;
1053 }
1054
1055 // Add windows of certain types not covered by modal windows.
1056 if (isReportedWindowType(windowState.mAttrs.type)) {
1057 // Add the window to the ones to be reported.
Svetoslavf7174e82014-06-12 11:29:35 -07001058 WindowInfo window = obtainPopulatedWindowInfo(windowState, boundsInScreen);
Svetoslav8e3feb12014-02-24 13:46:47 -08001059 addedWindows.add(window.token);
Svetoslav8e3feb12014-02-24 13:46:47 -08001060 windows.add(window);
Svetoslavf7174e82014-06-12 11:29:35 -07001061 if (windowState.isFocused()) {
1062 focusedWindowAdded = true;
1063 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001064 }
1065
Alan Viveretted0c73f42014-11-18 10:25:04 -08001066 // Account for the space this window takes if the window
1067 // is not an accessibility overlay which does not change
1068 // the reported windows.
1069 if (windowState.mAttrs.type !=
1070 WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
1071 unaccountedSpace.op(boundsInScreen, unaccountedSpace,
1072 Region.Op.REVERSE_DIFFERENCE);
1073 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001074
Allen Hairf20ac2c2016-02-11 17:42:59 -08001075 // If a window is modal it prevents other windows from being touched
Svetoslav8e3feb12014-02-24 13:46:47 -08001076 if ((flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1077 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
Phil Weaverac9ad702016-07-27 18:03:10 -07001078 // Account for all space in the task, whether the windows in it are
1079 // touchable or not. The modal window blocks all touches from the task's
1080 // area.
1081 unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace,
1082 Region.Op.REVERSE_DIFFERENCE);
1083
Allen Hairf20ac2c2016-02-11 17:42:59 -08001084 if (task != null) {
1085 // If the window is associated with a particular task, we can skip the
1086 // rest of the windows for that task.
1087 skipRemainingWindowsForTasks.add(task.mTaskId);
1088 continue;
1089 } else {
1090 // If the window is not associated with a particular task, then it is
1091 // globally modal. In this case we can skip all remaining windows.
1092 break;
1093 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001094 }
Phil Weaverac9ad702016-07-27 18:03:10 -07001095 // We figured out what is touchable for the entire screen - done.
1096 if (unaccountedSpace.isEmpty()) {
1097 break;
1098 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001099 }
1100
Svetoslavf7174e82014-06-12 11:29:35 -07001101 // Always report the focused window.
1102 if (!focusedWindowAdded) {
1103 for (int i = visibleWindowCount - 1; i >= 0; i--) {
1104 WindowState windowState = visibleWindows.valueAt(i);
1105 if (windowState.isFocused()) {
1106 // Compute the bounds in the screen.
1107 Rect boundsInScreen = mTempRect;
1108 computeWindowBoundsInScreen(windowState, boundsInScreen);
1109
1110 // Add the window to the ones to be reported.
1111 WindowInfo window = obtainPopulatedWindowInfo(windowState,
1112 boundsInScreen);
1113 addedWindows.add(window.token);
1114 windows.add(window);
1115 break;
1116 }
1117 }
1118 }
1119
Svetoslav8e3feb12014-02-24 13:46:47 -08001120 // Remove child/parent references to windows that were not added.
1121 final int windowCount = windows.size();
1122 for (int i = 0; i < windowCount; i++) {
1123 WindowInfo window = windows.get(i);
1124 if (!addedWindows.contains(window.parentToken)) {
1125 window.parentToken = null;
1126 }
1127 if (window.childTokens != null) {
1128 final int childTokenCount = window.childTokens.size();
1129 for (int j = childTokenCount - 1; j >= 0; j--) {
1130 if (!addedWindows.contains(window.childTokens.get(j))) {
1131 window.childTokens.remove(j);
1132 }
1133 }
1134 // Leave the child token list if empty.
1135 }
1136 }
1137
1138 visibleWindows.clear();
1139 addedWindows.clear();
1140
1141 // We computed the windows and if they changed notify the client.
Svetoslav8e3feb12014-02-24 13:46:47 -08001142 if (mOldWindows.size() != windows.size()) {
1143 // Different size means something changed.
1144 windowsChanged = true;
1145 } else if (!mOldWindows.isEmpty() || !windows.isEmpty()) {
1146 // Since we always traverse windows from high to low layer
1147 // the old and new windows at the same index should be the
1148 // same, otherwise something changed.
1149 for (int i = 0; i < windowCount; i++) {
1150 WindowInfo oldWindow = mOldWindows.get(i);
1151 WindowInfo newWindow = windows.get(i);
1152 // We do not care for layer changes given the window
1153 // order does not change. This brings no new information
1154 // to the clients.
1155 if (windowChangedNoLayer(oldWindow, newWindow)) {
1156 windowsChanged = true;
1157 break;
1158 }
1159 }
1160 }
1161
1162 if (windowsChanged) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001163 cacheWindows(windows);
Svetoslav8e3feb12014-02-24 13:46:47 -08001164 }
1165 }
Svetoslav3a0d8782014-12-04 12:50:11 -08001166
1167 // Now we do not hold the lock, so send the windows over.
1168 if (windowsChanged) {
1169 if (DEBUG) {
1170 Log.i(LOG_TAG, "Windows changed:" + windows);
1171 }
1172 mCallback.onWindowsForAccessibilityChanged(windows);
1173 } else {
1174 if (DEBUG) {
1175 Log.i(LOG_TAG, "No windows changed.");
1176 }
1177 }
1178
1179 // Recycle the windows as we do not need them.
1180 clearAndRecycleWindows(windows);
Svetoslav8e3feb12014-02-24 13:46:47 -08001181 }
1182
Svetoslavf7174e82014-06-12 11:29:35 -07001183 private void computeWindowBoundsInScreen(WindowState windowState, Rect outBounds) {
1184 // Get the touchable frame.
1185 Region touchableRegion = mTempRegion1;
1186 windowState.getTouchableRegion(touchableRegion);
1187 Rect touchableFrame = mTempRect;
1188 touchableRegion.getBounds(touchableFrame);
1189
1190 // Move to origin as all transforms are captured by the matrix.
1191 RectF windowFrame = mTempRectF;
1192 windowFrame.set(touchableFrame);
1193 windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
1194
1195 // Map the frame to get what appears on the screen.
1196 Matrix matrix = mTempMatrix;
1197 populateTransformationMatrixLocked(windowState, matrix);
1198 matrix.mapRect(windowFrame);
1199
1200 // Got the bounds.
1201 outBounds.set((int) windowFrame.left, (int) windowFrame.top,
1202 (int) windowFrame.right, (int) windowFrame.bottom);
1203 }
1204
1205 private static WindowInfo obtainPopulatedWindowInfo(WindowState windowState,
1206 Rect boundsInScreen) {
1207 WindowInfo window = WindowInfo.obtain();
1208 window.type = windowState.mAttrs.type;
1209 window.layer = windowState.mLayer;
1210 window.token = windowState.mClient.asBinder();
Phil Weaver9be3c7b2016-04-07 15:15:41 -07001211 window.title = windowState.mAttrs.accessibilityTitle;
Phil Weaver396d5492016-03-22 17:53:50 -07001212 window.accessibilityIdOfAnchor = windowState.mAttrs.accessibilityIdOfAnchor;
Svetoslavf7174e82014-06-12 11:29:35 -07001213
1214 WindowState attachedWindow = windowState.mAttachedWindow;
1215 if (attachedWindow != null) {
1216 window.parentToken = attachedWindow.mClient.asBinder();
1217 }
1218
1219 window.focused = windowState.isFocused();
1220 window.boundsInScreen.set(boundsInScreen);
1221
1222 final int childCount = windowState.mChildWindows.size();
1223 if (childCount > 0) {
1224 if (window.childTokens == null) {
1225 window.childTokens = new ArrayList<IBinder>();
1226 }
1227 for (int j = 0; j < childCount; j++) {
1228 WindowState child = windowState.mChildWindows.get(j);
1229 window.childTokens.add(child.mClient.asBinder());
1230 }
1231 }
1232
1233 return window;
1234 }
1235
Svetoslav8e3feb12014-02-24 13:46:47 -08001236 private void cacheWindows(List<WindowInfo> windows) {
1237 final int oldWindowCount = mOldWindows.size();
1238 for (int i = oldWindowCount - 1; i >= 0; i--) {
1239 mOldWindows.remove(i).recycle();
1240 }
1241 final int newWindowCount = windows.size();
1242 for (int i = 0; i < newWindowCount; i++) {
1243 WindowInfo newWindow = windows.get(i);
1244 mOldWindows.add(WindowInfo.obtain(newWindow));
1245 }
1246 }
1247
1248 private boolean windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow) {
1249 if (oldWindow == newWindow) {
1250 return false;
1251 }
Svetoslavf7174e82014-06-12 11:29:35 -07001252 if (oldWindow == null) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001253 return true;
1254 }
Svetoslavf7174e82014-06-12 11:29:35 -07001255 if (newWindow == null) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001256 return true;
1257 }
1258 if (oldWindow.type != newWindow.type) {
1259 return true;
1260 }
1261 if (oldWindow.focused != newWindow.focused) {
1262 return true;
1263 }
1264 if (oldWindow.token == null) {
1265 if (newWindow.token != null) {
1266 return true;
1267 }
1268 } else if (!oldWindow.token.equals(newWindow.token)) {
1269 return true;
1270 }
1271 if (oldWindow.parentToken == null) {
1272 if (newWindow.parentToken != null) {
1273 return true;
1274 }
1275 } else if (!oldWindow.parentToken.equals(newWindow.parentToken)) {
1276 return true;
1277 }
1278 if (!oldWindow.boundsInScreen.equals(newWindow.boundsInScreen)) {
1279 return true;
1280 }
1281 if (oldWindow.childTokens != null && newWindow.childTokens != null
1282 && !oldWindow.childTokens.equals(newWindow.childTokens)) {
1283 return true;
1284 }
Phil Weaver396d5492016-03-22 17:53:50 -07001285 if (!TextUtils.equals(oldWindow.title, newWindow.title)) {
1286 return true;
1287 }
1288 if (oldWindow.accessibilityIdOfAnchor != newWindow.accessibilityIdOfAnchor) {
1289 return true;
1290 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001291 return false;
1292 }
1293
Svetoslav3a0d8782014-12-04 12:50:11 -08001294 private static void clearAndRecycleWindows(List<WindowInfo> windows) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001295 final int windowCount = windows.size();
1296 for (int i = windowCount - 1; i >= 0; i--) {
1297 windows.remove(i).recycle();
1298 }
1299 }
1300
1301 private static boolean isReportedWindowType(int windowType) {
1302 return (windowType != WindowManager.LayoutParams.TYPE_KEYGUARD_SCRIM
1303 && windowType != WindowManager.LayoutParams.TYPE_WALLPAPER
1304 && windowType != WindowManager.LayoutParams.TYPE_BOOT_PROGRESS
1305 && windowType != WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY
1306 && windowType != WindowManager.LayoutParams.TYPE_DRAG
Selim Cinekf83e8242015-05-19 18:08:14 -07001307 && windowType != WindowManager.LayoutParams.TYPE_INPUT_CONSUMER
Svetoslav8e3feb12014-02-24 13:46:47 -08001308 && windowType != WindowManager.LayoutParams.TYPE_POINTER
Svetoslav8e3feb12014-02-24 13:46:47 -08001309 && windowType != WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY
1310 && windowType != WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
1311 && windowType != WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY
1312 && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
1313 }
1314
1315 private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
1316 DisplayContent displayContent = mWindowManagerService
1317 .getDefaultDisplayContentLocked();
1318 WindowList windowList = displayContent.getWindowList();
1319 final int windowCount = windowList.size();
1320 for (int i = 0; i < windowCount; i++) {
1321 WindowState windowState = windowList.get(i);
1322 if (windowState.isVisibleLw()) {
1323 outWindows.put(windowState.mLayer, windowState);
1324 }
1325 }
1326 }
1327
1328 private class MyHandler extends Handler {
Svetoslavf7174e82014-06-12 11:29:35 -07001329 public static final int MESSAGE_COMPUTE_CHANGED_WINDOWS = 1;
Svetoslav8e3feb12014-02-24 13:46:47 -08001330
1331 public MyHandler(Looper looper) {
1332 super(looper, null, false);
1333 }
1334
1335 @Override
1336 @SuppressWarnings("unchecked")
1337 public void handleMessage(Message message) {
1338 switch (message.what) {
Svetoslavf7174e82014-06-12 11:29:35 -07001339 case MESSAGE_COMPUTE_CHANGED_WINDOWS: {
1340 computeChangedWindows();
1341 } break;
Svetoslav8e3feb12014-02-24 13:46:47 -08001342 }
1343 }
1344 }
1345 }
1346}