blob: f138add7ba09c062b75aca4c179453cd5620a706 [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
Svetoslav Ganovfd138892016-07-13 18:20:42 -0700118 public void performComputeChangedWindowsNotLocked() {
119 WindowsForAccessibilityObserver observer = null;
120 synchronized (mWindowManagerService) {
121 observer = mWindowsForAccessibilityObserver;
122 }
123 if (observer != null) {
124 observer.performComputeChangedWindowsNotLocked();
125 }
126 }
127
Svetoslav8e3feb12014-02-24 13:46:47 -0800128 public void setMagnificationSpecLocked(MagnificationSpec spec) {
129 if (mDisplayMagnifier != null) {
130 mDisplayMagnifier.setMagnificationSpecLocked(spec);
131 }
132 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700133 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800134 }
135 }
136
Phil Weaver70439242016-03-10 15:15:49 -0800137 public void getMagnificationRegionLocked(Region outMagnificationRegion) {
Alan Viverette59e53a12016-03-28 13:41:32 -0400138 if (mDisplayMagnifier != null) {
Phil Weaver70439242016-03-10 15:15:49 -0800139 mDisplayMagnifier.getMagnificationRegionLocked(outMagnificationRegion);
Alan Viverette59e53a12016-03-28 13:41:32 -0400140 }
141 }
142
Svetoslavf7174e82014-06-12 11:29:35 -0700143 public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800144 if (mDisplayMagnifier != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700145 mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle);
Svetoslav8e3feb12014-02-24 13:46:47 -0800146 }
147 // Not relevant for the window observer.
148 }
149
150 public void onWindowLayersChangedLocked() {
151 if (mDisplayMagnifier != null) {
152 mDisplayMagnifier.onWindowLayersChangedLocked();
153 }
154 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700155 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800156 }
157 }
158
Andrii Kulian8ee72852017-03-10 10:36:45 -0800159 public void onRotationChangedLocked(DisplayContent displayContent) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800160 if (mDisplayMagnifier != null) {
Andrii Kulian8ee72852017-03-10 10:36:45 -0800161 mDisplayMagnifier.onRotationChangedLocked(displayContent);
Svetoslav8e3feb12014-02-24 13:46:47 -0800162 }
163 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700164 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800165 }
166 }
167
168 public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
169 if (mDisplayMagnifier != null) {
170 mDisplayMagnifier.onAppWindowTransitionLocked(windowState, transition);
171 }
172 // Not relevant for the window observer.
173 }
174
175 public void onWindowTransitionLocked(WindowState windowState, int transition) {
176 if (mDisplayMagnifier != null) {
177 mDisplayMagnifier.onWindowTransitionLocked(windowState, transition);
178 }
179 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700180 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800181 }
182 }
183
Svetoslav3a0d8782014-12-04 12:50:11 -0800184 public void onWindowFocusChangedNotLocked() {
Svetoslav8e3feb12014-02-24 13:46:47 -0800185 // Not relevant for the display magnifier.
186
Svetoslav3a0d8782014-12-04 12:50:11 -0800187 WindowsForAccessibilityObserver observer = null;
188 synchronized (mWindowManagerService) {
189 observer = mWindowsForAccessibilityObserver;
190 }
191 if (observer != null) {
192 observer.performComputeChangedWindowsNotLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800193 }
194 }
195
Svetoslav4604abc2014-06-10 18:59:30 -0700196
Svetoslavf7174e82014-06-12 11:29:35 -0700197 public void onSomeWindowResizedOrMovedLocked() {
Svetoslav4604abc2014-06-10 18:59:30 -0700198 // Not relevant for the display magnifier.
199
200 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700201 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav4604abc2014-06-10 18:59:30 -0700202 }
203 }
204
Svetoslav8e3feb12014-02-24 13:46:47 -0800205 /** NOTE: This has to be called within a surface transaction. */
206 public void drawMagnifiedRegionBorderIfNeededLocked() {
207 if (mDisplayMagnifier != null) {
208 mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked();
209 }
210 // Not relevant for the window observer.
211 }
212
213 public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
214 if (mDisplayMagnifier != null) {
215 return mDisplayMagnifier.getMagnificationSpecForWindowLocked(windowState);
216 }
217 return null;
218 }
219
220 public boolean hasCallbacksLocked() {
221 return (mDisplayMagnifier != null
222 || mWindowsForAccessibilityObserver != null);
223 }
224
Casey Burkhardt74922c62017-02-13 12:43:16 -0800225 public void setForceShowMagnifiableBoundsLocked(boolean show) {
226 if (mDisplayMagnifier != null) {
227 mDisplayMagnifier.setForceShowMagnifiableBoundsLocked(show);
Phil Weaver251db072017-03-28 08:35:38 -0700228 mDisplayMagnifier.showMagnificationBoundsIfNeeded();
Casey Burkhardt74922c62017-02-13 12:43:16 -0800229 }
230 }
231
Svetoslav8e3feb12014-02-24 13:46:47 -0800232 private static void populateTransformationMatrixLocked(WindowState windowState,
233 Matrix outMatrix) {
234 sTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx;
235 sTempFloats[Matrix.MSKEW_Y] = windowState.mWinAnimator.mDtDx;
Robert Carr0edf18f2017-02-21 20:01:47 -0800236 sTempFloats[Matrix.MSKEW_X] = windowState.mWinAnimator.mDtDy;
237 sTempFloats[Matrix.MSCALE_Y] = windowState.mWinAnimator.mDsDy;
Filip Gruszczynski2a6a2c22015-10-14 12:00:53 -0700238 sTempFloats[Matrix.MTRANS_X] = windowState.mShownPosition.x;
239 sTempFloats[Matrix.MTRANS_Y] = windowState.mShownPosition.y;
Svetoslav8e3feb12014-02-24 13:46:47 -0800240 sTempFloats[Matrix.MPERSP_0] = 0;
241 sTempFloats[Matrix.MPERSP_1] = 0;
242 sTempFloats[Matrix.MPERSP_2] = 1;
243 outMatrix.setValues(sTempFloats);
244 }
245
246 /**
247 * This class encapsulates the functionality related to display magnification.
248 */
249 private static final class DisplayMagnifier {
250
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -0800251 private static final String LOG_TAG = TAG_WITH_CLASS_NAME ? "DisplayMagnifier" : TAG_WM;
Svetoslav8e3feb12014-02-24 13:46:47 -0800252
253 private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
254 private static final boolean DEBUG_ROTATION = false;
255 private static final boolean DEBUG_LAYERS = false;
256 private static final boolean DEBUG_RECTANGLE_REQUESTED = false;
257 private static final boolean DEBUG_VIEWPORT_WINDOW = false;
258
259 private final Rect mTempRect1 = new Rect();
260 private final Rect mTempRect2 = new Rect();
261
262 private final Region mTempRegion1 = new Region();
263 private final Region mTempRegion2 = new Region();
264 private final Region mTempRegion3 = new Region();
265 private final Region mTempRegion4 = new Region();
266
267 private final Context mContext;
268 private final WindowManagerService mWindowManagerService;
269 private final MagnifiedViewport mMagnifedViewport;
270 private final Handler mHandler;
271
272 private final MagnificationCallbacks mCallbacks;
273
274 private final long mLongAnimationDuration;
275
Casey Burkhardt74922c62017-02-13 12:43:16 -0800276 private boolean mForceShowMagnifiableBounds = false;
277
Svetoslav8e3feb12014-02-24 13:46:47 -0800278 public DisplayMagnifier(WindowManagerService windowManagerService,
279 MagnificationCallbacks callbacks) {
280 mContext = windowManagerService.mContext;
281 mWindowManagerService = windowManagerService;
282 mCallbacks = callbacks;
283 mHandler = new MyHandler(mWindowManagerService.mH.getLooper());
284 mMagnifedViewport = new MagnifiedViewport();
285 mLongAnimationDuration = mContext.getResources().getInteger(
286 com.android.internal.R.integer.config_longAnimTime);
287 }
288
289 public void setMagnificationSpecLocked(MagnificationSpec spec) {
290 mMagnifedViewport.updateMagnificationSpecLocked(spec);
291 mMagnifedViewport.recomputeBoundsLocked();
292 mWindowManagerService.scheduleAnimationLocked();
293 }
294
Casey Burkhardt74922c62017-02-13 12:43:16 -0800295 public void setForceShowMagnifiableBoundsLocked(boolean show) {
296 mForceShowMagnifiableBounds = show;
297 mMagnifedViewport.setMagnifiedRegionBorderShownLocked(show, true);
298 }
299
300 public boolean isForceShowingMagnifiableBoundsLocked() {
301 return mForceShowMagnifiableBounds;
302 }
303
Svetoslavf7174e82014-06-12 11:29:35 -0700304 public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800305 if (DEBUG_RECTANGLE_REQUESTED) {
306 Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
307 }
308 if (!mMagnifedViewport.isMagnifyingLocked()) {
309 return;
310 }
311 Rect magnifiedRegionBounds = mTempRect2;
312 mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds);
313 if (magnifiedRegionBounds.contains(rectangle)) {
314 return;
315 }
316 SomeArgs args = SomeArgs.obtain();
317 args.argi1 = rectangle.left;
318 args.argi2 = rectangle.top;
319 args.argi3 = rectangle.right;
320 args.argi4 = rectangle.bottom;
321 mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED,
322 args).sendToTarget();
323 }
324
325 public void onWindowLayersChangedLocked() {
326 if (DEBUG_LAYERS) {
327 Slog.i(LOG_TAG, "Layers changed.");
328 }
329 mMagnifedViewport.recomputeBoundsLocked();
330 mWindowManagerService.scheduleAnimationLocked();
331 }
332
Andrii Kulian8ee72852017-03-10 10:36:45 -0800333 public void onRotationChangedLocked(DisplayContent displayContent) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800334 if (DEBUG_ROTATION) {
Andrii Kulian8ee72852017-03-10 10:36:45 -0800335 final int rotation = displayContent.getRotation();
336 Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation)
Svetoslav8e3feb12014-02-24 13:46:47 -0800337 + " displayId: " + displayContent.getDisplayId());
338 }
339 mMagnifedViewport.onRotationChangedLocked();
340 mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
341 }
342
343 public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
344 if (DEBUG_WINDOW_TRANSITIONS) {
345 Slog.i(LOG_TAG, "Window transition: "
346 + AppTransition.appTransitionToString(transition)
347 + " displayId: " + windowState.getDisplayId());
348 }
349 final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
350 if (magnifying) {
351 switch (transition) {
352 case AppTransition.TRANSIT_ACTIVITY_OPEN:
353 case AppTransition.TRANSIT_TASK_OPEN:
354 case AppTransition.TRANSIT_TASK_TO_FRONT:
355 case AppTransition.TRANSIT_WALLPAPER_OPEN:
356 case AppTransition.TRANSIT_WALLPAPER_CLOSE:
357 case AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN: {
358 mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED);
359 }
360 }
361 }
362 }
363
364 public void onWindowTransitionLocked(WindowState windowState, int transition) {
365 if (DEBUG_WINDOW_TRANSITIONS) {
366 Slog.i(LOG_TAG, "Window transition: "
367 + AppTransition.appTransitionToString(transition)
368 + " displayId: " + windowState.getDisplayId());
369 }
370 final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
371 final int type = windowState.mAttrs.type;
372 switch (transition) {
373 case WindowManagerPolicy.TRANSIT_ENTER:
374 case WindowManagerPolicy.TRANSIT_SHOW: {
375 if (!magnifying) {
376 break;
377 }
378 switch (type) {
379 case WindowManager.LayoutParams.TYPE_APPLICATION:
Chong Zhangfea963e2016-08-15 17:14:16 -0700380 case WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION:
Svetoslav8e3feb12014-02-24 13:46:47 -0800381 case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
382 case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
383 case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
Wale Ogunwale0a4dc222015-04-14 12:58:42 -0700384 case WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL:
Svetoslav8e3feb12014-02-24 13:46:47 -0800385 case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
386 case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
387 case WindowManager.LayoutParams.TYPE_PHONE:
388 case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
389 case WindowManager.LayoutParams.TYPE_TOAST:
390 case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
Wale Ogunwale5cd907d2017-01-26 14:14:08 -0800391 case WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY:
Svetoslav8e3feb12014-02-24 13:46:47 -0800392 case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
393 case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
394 case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
395 case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
396 case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
Jason Monk8f7f3182015-11-18 16:35:14 -0500397 case WindowManager.LayoutParams.TYPE_QS_DIALOG:
Adrian Roos9a645132014-10-08 02:59:56 +0200398 case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: {
Svetoslav8e3feb12014-02-24 13:46:47 -0800399 Rect magnifiedRegionBounds = mTempRect2;
400 mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(
401 magnifiedRegionBounds);
402 Rect touchableRegionBounds = mTempRect1;
403 windowState.getTouchableRegion(mTempRegion1);
404 mTempRegion1.getBounds(touchableRegionBounds);
405 if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) {
406 mCallbacks.onRectangleOnScreenRequested(
407 touchableRegionBounds.left,
408 touchableRegionBounds.top,
409 touchableRegionBounds.right,
410 touchableRegionBounds.bottom);
411 }
412 } break;
413 } break;
414 }
415 }
416 }
417
418 public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
419 MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
420 if (spec != null && !spec.isNop()) {
421 WindowManagerPolicy policy = mWindowManagerService.mPolicy;
422 final int windowType = windowState.mAttrs.type;
Wale Ogunwale7ed4d372016-07-09 15:28:55 -0700423 if (!policy.isTopLevelWindow(windowType) && windowState.isChildWindow()
Svetoslav8e3feb12014-02-24 13:46:47 -0800424 && !policy.canMagnifyWindow(windowType)) {
425 return null;
426 }
427 if (!policy.canMagnifyWindow(windowState.mAttrs.type)) {
428 return null;
429 }
430 }
431 return spec;
432 }
433
Phil Weaver70439242016-03-10 15:15:49 -0800434 public void getMagnificationRegionLocked(Region outMagnificationRegion) {
435 mMagnifedViewport.getMagnificationRegionLocked(outMagnificationRegion);
Alan Viverette59e53a12016-03-28 13:41:32 -0400436 }
437
Svetoslav8e3feb12014-02-24 13:46:47 -0800438 public void destroyLocked() {
439 mMagnifedViewport.destroyWindow();
440 }
441
Phil Weaver251db072017-03-28 08:35:38 -0700442 // Can be called outside of a surface transaction
443 public void showMagnificationBoundsIfNeeded() {
444 mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)
445 .sendToTarget();
446 }
447
Svetoslav8e3feb12014-02-24 13:46:47 -0800448 /** NOTE: This has to be called within a surface transaction. */
449 public void drawMagnifiedRegionBorderIfNeededLocked() {
450 mMagnifedViewport.drawWindowIfNeededLocked();
451 }
452
453 private final class MagnifiedViewport {
454
Svetoslav8e3feb12014-02-24 13:46:47 -0800455 private final SparseArray<WindowState> mTempWindowStates =
456 new SparseArray<WindowState>();
457
458 private final RectF mTempRectF = new RectF();
459
460 private final Point mTempPoint = new Point();
461
462 private final Matrix mTempMatrix = new Matrix();
463
Phil Weaver70439242016-03-10 15:15:49 -0800464 private final Region mMagnificationRegion = new Region();
465 private final Region mOldMagnificationRegion = new Region();
Svetoslav8e3feb12014-02-24 13:46:47 -0800466
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800467 private final Path mCircularPath;
468
Svetoslav8e3feb12014-02-24 13:46:47 -0800469 private final MagnificationSpec mMagnificationSpec = MagnificationSpec.obtain();
470
471 private final WindowManager mWindowManager;
472
Svetoslav7505e332014-08-22 12:14:28 -0700473 private final float mBorderWidth;
Svetoslav8e3feb12014-02-24 13:46:47 -0800474 private final int mHalfBorderWidth;
Svetoslav7505e332014-08-22 12:14:28 -0700475 private final int mDrawBorderInset;
Svetoslav8e3feb12014-02-24 13:46:47 -0800476
477 private final ViewportWindow mWindow;
478
479 private boolean mFullRedrawNeeded;
480
481 public MagnifiedViewport() {
482 mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE);
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800483 mBorderWidth = mContext.getResources().getDimension(
484 com.android.internal.R.dimen.accessibility_magnification_indicator_width);
Svetoslav7505e332014-08-22 12:14:28 -0700485 mHalfBorderWidth = (int) Math.ceil(mBorderWidth / 2);
486 mDrawBorderInset = (int) mBorderWidth / 2;
Svetoslav8e3feb12014-02-24 13:46:47 -0800487 mWindow = new ViewportWindow(mContext);
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800488
Adam Powell01f280d2015-05-18 16:07:42 -0700489 if (mContext.getResources().getConfiguration().isScreenRound()) {
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800490 mCircularPath = new Path();
491 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
492 final int centerXY = mTempPoint.x / 2;
493 mCircularPath.addCircle(centerXY, centerXY, centerXY, Path.Direction.CW);
494 } else {
495 mCircularPath = null;
496 }
497
Svetoslav8e3feb12014-02-24 13:46:47 -0800498 recomputeBoundsLocked();
499 }
500
Phil Weaver70439242016-03-10 15:15:49 -0800501 public void getMagnificationRegionLocked(@NonNull Region outMagnificationRegion) {
502 outMagnificationRegion.set(mMagnificationRegion);
Alan Viverette59e53a12016-03-28 13:41:32 -0400503 }
504
Svetoslav8e3feb12014-02-24 13:46:47 -0800505 public void updateMagnificationSpecLocked(MagnificationSpec spec) {
506 if (spec != null) {
507 mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY);
508 } else {
509 mMagnificationSpec.clear();
510 }
511 // If this message is pending we are in a rotation animation and do not want
512 // to show the border. We will do so when the pending message is handled.
Svetoslav7505e332014-08-22 12:14:28 -0700513 if (!mHandler.hasMessages(
514 MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
Casey Burkhardt74922c62017-02-13 12:43:16 -0800515 setMagnifiedRegionBorderShownLocked(
516 isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked(), true);
Svetoslav8e3feb12014-02-24 13:46:47 -0800517 }
518 }
519
520 public void recomputeBoundsLocked() {
521 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
522 final int screenWidth = mTempPoint.x;
523 final int screenHeight = mTempPoint.y;
524
Phil Weaver70439242016-03-10 15:15:49 -0800525 mMagnificationRegion.set(0, 0, 0, 0);
526 final Region availableBounds = mTempRegion1;
527 availableBounds.set(0, 0, screenWidth, screenHeight);
Svetoslav8e3feb12014-02-24 13:46:47 -0800528
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800529 if (mCircularPath != null) {
Phil Weaver70439242016-03-10 15:15:49 -0800530 availableBounds.setPath(mCircularPath, availableBounds);
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800531 }
532
Svetoslav8e3feb12014-02-24 13:46:47 -0800533 Region nonMagnifiedBounds = mTempRegion4;
Svet Ganovb21df802014-09-01 19:06:33 -0700534 nonMagnifiedBounds.set(0, 0, 0, 0);
Svetoslav8e3feb12014-02-24 13:46:47 -0800535
536 SparseArray<WindowState> visibleWindows = mTempWindowStates;
537 visibleWindows.clear();
538 populateWindowsOnScreenLocked(visibleWindows);
539
540 final int visibleWindowCount = visibleWindows.size();
541 for (int i = visibleWindowCount - 1; i >= 0; i--) {
542 WindowState windowState = visibleWindows.valueAt(i);
543 if (windowState.mAttrs.type == WindowManager
544 .LayoutParams.TYPE_MAGNIFICATION_OVERLAY) {
545 continue;
546 }
547
Phil Weaver65c06702016-03-15 15:33:46 -0700548 // Consider the touchable portion of the window
Svetoslav8e3feb12014-02-24 13:46:47 -0800549 Matrix matrix = mTempMatrix;
550 populateTransformationMatrixLocked(windowState, matrix);
Phil Weaver65c06702016-03-15 15:33:46 -0700551 Region touchableRegion = mTempRegion3;
552 windowState.getTouchableRegion(touchableRegion);
553 Rect touchableFrame = mTempRect1;
554 touchableRegion.getBounds(touchableFrame);
Svetoslav8e3feb12014-02-24 13:46:47 -0800555 RectF windowFrame = mTempRectF;
Phil Weaver65c06702016-03-15 15:33:46 -0700556 windowFrame.set(touchableFrame);
557 windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
558 matrix.mapRect(windowFrame);
559 Region windowBounds = mTempRegion2;
560 windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
561 (int) windowFrame.right, (int) windowFrame.bottom);
562 // Only update new regions
563 Region portionOfWindowAlreadyAccountedFor = mTempRegion3;
Phil Weaver70439242016-03-10 15:15:49 -0800564 portionOfWindowAlreadyAccountedFor.set(mMagnificationRegion);
Phil Weaver65c06702016-03-15 15:33:46 -0700565 portionOfWindowAlreadyAccountedFor.op(nonMagnifiedBounds, Region.Op.UNION);
566 windowBounds.op(portionOfWindowAlreadyAccountedFor, Region.Op.DIFFERENCE);
Svetoslav8e3feb12014-02-24 13:46:47 -0800567
568 if (mWindowManagerService.mPolicy.canMagnifyWindow(windowState.mAttrs.type)) {
Phil Weaver70439242016-03-10 15:15:49 -0800569 mMagnificationRegion.op(windowBounds, Region.Op.UNION);
570 mMagnificationRegion.op(availableBounds, Region.Op.INTERSECT);
Svetoslav8e3feb12014-02-24 13:46:47 -0800571 } else {
Svetoslav8e3feb12014-02-24 13:46:47 -0800572 nonMagnifiedBounds.op(windowBounds, Region.Op.UNION);
Phil Weaver70439242016-03-10 15:15:49 -0800573 availableBounds.op(windowBounds, Region.Op.DIFFERENCE);
Svetoslav8e3feb12014-02-24 13:46:47 -0800574 }
575
Phil Weaver65c06702016-03-15 15:33:46 -0700576 // Update accounted bounds
Svetoslav8e3feb12014-02-24 13:46:47 -0800577 Region accountedBounds = mTempRegion2;
Phil Weaver70439242016-03-10 15:15:49 -0800578 accountedBounds.set(mMagnificationRegion);
Svetoslav8e3feb12014-02-24 13:46:47 -0800579 accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION);
580 accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT);
581
582 if (accountedBounds.isRect()) {
583 Rect accountedFrame = mTempRect1;
584 accountedBounds.getBounds(accountedFrame);
585 if (accountedFrame.width() == screenWidth
586 && accountedFrame.height() == screenHeight) {
587 break;
588 }
589 }
590 }
591
592 visibleWindows.clear();
593
Phil Weaver70439242016-03-10 15:15:49 -0800594 mMagnificationRegion.op(mDrawBorderInset, mDrawBorderInset,
Svetoslav7505e332014-08-22 12:14:28 -0700595 screenWidth - mDrawBorderInset, screenHeight - mDrawBorderInset,
Svetoslav8e3feb12014-02-24 13:46:47 -0800596 Region.Op.INTERSECT);
597
Phil Weaver70439242016-03-10 15:15:49 -0800598 final boolean magnifiedChanged =
599 !mOldMagnificationRegion.equals(mMagnificationRegion);
600 if (magnifiedChanged) {
601 mWindow.setBounds(mMagnificationRegion);
602 final Rect dirtyRect = mTempRect1;
603 if (mFullRedrawNeeded) {
604 mFullRedrawNeeded = false;
605 dirtyRect.set(mDrawBorderInset, mDrawBorderInset,
606 screenWidth - mDrawBorderInset,
607 screenHeight - mDrawBorderInset);
608 mWindow.invalidate(dirtyRect);
609 } else {
610 final Region dirtyRegion = mTempRegion3;
611 dirtyRegion.set(mMagnificationRegion);
612 dirtyRegion.op(mOldMagnificationRegion, Region.Op.UNION);
613 dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT);
614 dirtyRegion.getBounds(dirtyRect);
615 mWindow.invalidate(dirtyRect);
Svetoslav8e3feb12014-02-24 13:46:47 -0800616 }
617
Phil Weaver70439242016-03-10 15:15:49 -0800618 mOldMagnificationRegion.set(mMagnificationRegion);
Alan Viverette214fb682015-11-17 09:47:11 -0500619 final SomeArgs args = SomeArgs.obtain();
Phil Weaver70439242016-03-10 15:15:49 -0800620 args.arg1 = Region.obtain(mMagnificationRegion);
Alan Viverette214fb682015-11-17 09:47:11 -0500621 mHandler.obtainMessage(
Phil Weaver70439242016-03-10 15:15:49 -0800622 MyHandler.MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED, args)
623 .sendToTarget();
Svetoslav8e3feb12014-02-24 13:46:47 -0800624 }
625 }
626
627 public void onRotationChangedLocked() {
Casey Burkhardt74922c62017-02-13 12:43:16 -0800628 // If we are showing the magnification border, hide it immediately so
Svetoslav8e3feb12014-02-24 13:46:47 -0800629 // the user does not see strange artifacts during rotation. The screenshot
Casey Burkhardt74922c62017-02-13 12:43:16 -0800630 // used for rotation already has the border. After the rotation is complete
Svetoslav8e3feb12014-02-24 13:46:47 -0800631 // we will show the border.
Casey Burkhardt74922c62017-02-13 12:43:16 -0800632 if (isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked()) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800633 setMagnifiedRegionBorderShownLocked(false, false);
634 final long delay = (long) (mLongAnimationDuration
Dianne Hackborneb94fa72014-06-03 17:48:12 -0700635 * mWindowManagerService.getWindowAnimationScaleLocked());
Svetoslav8e3feb12014-02-24 13:46:47 -0800636 Message message = mHandler.obtainMessage(
637 MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED);
638 mHandler.sendMessageDelayed(message, delay);
639 }
640 recomputeBoundsLocked();
641 mWindow.updateSize();
642 }
643
644 public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) {
645 if (shown) {
646 mFullRedrawNeeded = true;
Phil Weaver70439242016-03-10 15:15:49 -0800647 mOldMagnificationRegion.set(0, 0, 0, 0);
Svetoslav8e3feb12014-02-24 13:46:47 -0800648 }
649 mWindow.setShown(shown, animate);
650 }
651
652 public void getMagnifiedFrameInContentCoordsLocked(Rect rect) {
653 MagnificationSpec spec = mMagnificationSpec;
Phil Weaver70439242016-03-10 15:15:49 -0800654 mMagnificationRegion.getBounds(rect);
Svetoslav8e3feb12014-02-24 13:46:47 -0800655 rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
656 rect.scale(1.0f / spec.scale);
657 }
658
659 public boolean isMagnifyingLocked() {
660 return mMagnificationSpec.scale > 1.0f;
661 }
662
663 public MagnificationSpec getMagnificationSpecLocked() {
664 return mMagnificationSpec;
665 }
666
667 /** NOTE: This has to be called within a surface transaction. */
668 public void drawWindowIfNeededLocked() {
669 recomputeBoundsLocked();
670 mWindow.drawIfNeeded();
671 }
672
673 public void destroyWindow() {
674 mWindow.releaseSurface();
675 }
676
677 private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
Wale Ogunwalef7cab102016-10-25 15:25:14 -0700678 final DisplayContent dc = mWindowManagerService.getDefaultDisplayContentLocked();
Wale Ogunwaled1880962016-11-08 10:31:59 -0800679 dc.forAllWindows((w) -> {
680 if (w.isOnScreen() && w.isVisibleLw()
681 && !w.mWinAnimator.mEnterAnimationPending) {
682 outWindows.put(w.mLayer, w);
Svetoslav8e3feb12014-02-24 13:46:47 -0800683 }
Wale Ogunwaled1880962016-11-08 10:31:59 -0800684 }, false /* traverseTopToBottom */ );
Svetoslav8e3feb12014-02-24 13:46:47 -0800685 }
686
687 private final class ViewportWindow {
688 private static final String SURFACE_TITLE = "Magnification Overlay";
689
Svetoslav8e3feb12014-02-24 13:46:47 -0800690 private final Region mBounds = new Region();
691 private final Rect mDirtyRect = new Rect();
692 private final Paint mPaint = new Paint();
693
Svetoslav8e3feb12014-02-24 13:46:47 -0800694 private final SurfaceControl mSurfaceControl;
695 private final Surface mSurface = new Surface();
696
Svet Ganovb21df802014-09-01 19:06:33 -0700697 private final AnimationController mAnimationController;
698
Svetoslav8e3feb12014-02-24 13:46:47 -0800699 private boolean mShown;
700 private int mAlpha;
701
702 private boolean mInvalidated;
703
704 public ViewportWindow(Context context) {
705 SurfaceControl surfaceControl = null;
706 try {
707 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
708 surfaceControl = new SurfaceControl(mWindowManagerService.mFxSession,
709 SURFACE_TITLE, mTempPoint.x, mTempPoint.y, PixelFormat.TRANSLUCENT,
710 SurfaceControl.HIDDEN);
711 } catch (OutOfResourcesException oore) {
712 /* ignore */
713 }
714 mSurfaceControl = surfaceControl;
715 mSurfaceControl.setLayerStack(mWindowManager.getDefaultDisplay()
716 .getLayerStack());
Wale Ogunwale5cd907d2017-01-26 14:14:08 -0800717 mSurfaceControl.setLayer(mWindowManagerService.mPolicy.getWindowLayerFromTypeLw(
Svetoslav8e3feb12014-02-24 13:46:47 -0800718 WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY)
719 * WindowManagerService.TYPE_LAYER_MULTIPLIER);
720 mSurfaceControl.setPosition(0, 0);
721 mSurface.copyFrom(mSurfaceControl);
722
Svet Ganovb21df802014-09-01 19:06:33 -0700723 mAnimationController = new AnimationController(context,
724 mWindowManagerService.mH.getLooper());
725
Svetoslav8e3feb12014-02-24 13:46:47 -0800726 TypedValue typedValue = new TypedValue();
727 context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight,
728 typedValue, true);
Alan Viverette4a357cd2015-03-18 18:37:18 -0700729 final int borderColor = context.getColor(typedValue.resourceId);
Svetoslav8e3feb12014-02-24 13:46:47 -0800730
731 mPaint.setStyle(Paint.Style.STROKE);
732 mPaint.setStrokeWidth(mBorderWidth);
733 mPaint.setColor(borderColor);
734
Svetoslav8e3feb12014-02-24 13:46:47 -0800735 mInvalidated = true;
736 }
737
738 public void setShown(boolean shown, boolean animate) {
739 synchronized (mWindowManagerService.mWindowMap) {
740 if (mShown == shown) {
741 return;
742 }
743 mShown = shown;
Svet Ganovb21df802014-09-01 19:06:33 -0700744 mAnimationController.onFrameShownStateChanged(shown, animate);
Svetoslav8e3feb12014-02-24 13:46:47 -0800745 if (DEBUG_VIEWPORT_WINDOW) {
746 Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown);
747 }
748 }
749 }
750
751 @SuppressWarnings("unused")
752 // Called reflectively from an animator.
753 public int getAlpha() {
754 synchronized (mWindowManagerService.mWindowMap) {
755 return mAlpha;
756 }
757 }
758
759 public void setAlpha(int alpha) {
760 synchronized (mWindowManagerService.mWindowMap) {
761 if (mAlpha == alpha) {
762 return;
763 }
764 mAlpha = alpha;
765 invalidate(null);
766 if (DEBUG_VIEWPORT_WINDOW) {
767 Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha);
768 }
769 }
770 }
771
772 public void setBounds(Region bounds) {
773 synchronized (mWindowManagerService.mWindowMap) {
774 if (mBounds.equals(bounds)) {
775 return;
776 }
777 mBounds.set(bounds);
778 invalidate(mDirtyRect);
779 if (DEBUG_VIEWPORT_WINDOW) {
780 Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds);
781 }
782 }
783 }
784
785 public void updateSize() {
786 synchronized (mWindowManagerService.mWindowMap) {
787 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
788 mSurfaceControl.setSize(mTempPoint.x, mTempPoint.y);
789 invalidate(mDirtyRect);
790 }
791 }
792
793 public void invalidate(Rect dirtyRect) {
794 if (dirtyRect != null) {
795 mDirtyRect.set(dirtyRect);
796 } else {
797 mDirtyRect.setEmpty();
798 }
799 mInvalidated = true;
800 mWindowManagerService.scheduleAnimationLocked();
801 }
802
803 /** NOTE: This has to be called within a surface transaction. */
804 public void drawIfNeeded() {
805 synchronized (mWindowManagerService.mWindowMap) {
806 if (!mInvalidated) {
807 return;
808 }
809 mInvalidated = false;
810 Canvas canvas = null;
811 try {
812 // Empty dirty rectangle means unspecified.
813 if (mDirtyRect.isEmpty()) {
814 mBounds.getBounds(mDirtyRect);
815 }
816 mDirtyRect.inset(- mHalfBorderWidth, - mHalfBorderWidth);
817 canvas = mSurface.lockCanvas(mDirtyRect);
818 if (DEBUG_VIEWPORT_WINDOW) {
819 Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect);
820 }
821 } catch (IllegalArgumentException iae) {
822 /* ignore */
823 } catch (Surface.OutOfResourcesException oore) {
824 /* ignore */
825 }
826 if (canvas == null) {
827 return;
828 }
829 if (DEBUG_VIEWPORT_WINDOW) {
830 Slog.i(LOG_TAG, "Bounds: " + mBounds);
831 }
832 canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
833 mPaint.setAlpha(mAlpha);
834 Path path = mBounds.getBoundaryPath();
835 canvas.drawPath(path, mPaint);
836
837 mSurface.unlockCanvasAndPost(canvas);
838
839 if (mAlpha > 0) {
840 mSurfaceControl.show();
841 } else {
842 mSurfaceControl.hide();
843 }
844 }
845 }
846
847 public void releaseSurface() {
848 mSurfaceControl.release();
849 mSurface.release();
850 }
Svet Ganovb21df802014-09-01 19:06:33 -0700851
852 private final class AnimationController extends Handler {
853 private static final String PROPERTY_NAME_ALPHA = "alpha";
854
855 private static final int MIN_ALPHA = 0;
856 private static final int MAX_ALPHA = 255;
857
858 private static final int MSG_FRAME_SHOWN_STATE_CHANGED = 1;
859
860 private final ValueAnimator mShowHideFrameAnimator;
861
862 public AnimationController(Context context, Looper looper) {
863 super(looper);
864 mShowHideFrameAnimator = ObjectAnimator.ofInt(ViewportWindow.this,
865 PROPERTY_NAME_ALPHA, MIN_ALPHA, MAX_ALPHA);
866
867 Interpolator interpolator = new DecelerateInterpolator(2.5f);
868 final long longAnimationDuration = context.getResources().getInteger(
869 com.android.internal.R.integer.config_longAnimTime);
870
871 mShowHideFrameAnimator.setInterpolator(interpolator);
872 mShowHideFrameAnimator.setDuration(longAnimationDuration);
873 }
874
875 public void onFrameShownStateChanged(boolean shown, boolean animate) {
876 obtainMessage(MSG_FRAME_SHOWN_STATE_CHANGED,
877 shown ? 1 : 0, animate ? 1 : 0).sendToTarget();
878 }
879
880 @Override
881 public void handleMessage(Message message) {
882 switch (message.what) {
883 case MSG_FRAME_SHOWN_STATE_CHANGED: {
884 final boolean shown = message.arg1 == 1;
885 final boolean animate = message.arg2 == 1;
886
887 if (animate) {
888 if (mShowHideFrameAnimator.isRunning()) {
889 mShowHideFrameAnimator.reverse();
890 } else {
891 if (shown) {
892 mShowHideFrameAnimator.start();
893 } else {
894 mShowHideFrameAnimator.reverse();
895 }
896 }
897 } else {
898 mShowHideFrameAnimator.cancel();
899 if (shown) {
900 setAlpha(MAX_ALPHA);
901 } else {
902 setAlpha(MIN_ALPHA);
903 }
904 }
905 } break;
906 }
907 }
908 }
Svetoslav8e3feb12014-02-24 13:46:47 -0800909 }
910 }
911
912 private class MyHandler extends Handler {
Phil Weaver70439242016-03-10 15:15:49 -0800913 public static final int MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED = 1;
Svetoslav8e3feb12014-02-24 13:46:47 -0800914 public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2;
915 public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
916 public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
917 public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
918
919 public MyHandler(Looper looper) {
920 super(looper);
921 }
922
923 @Override
924 public void handleMessage(Message message) {
925 switch (message.what) {
Phil Weaver70439242016-03-10 15:15:49 -0800926 case MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED: {
Alan Viverette214fb682015-11-17 09:47:11 -0500927 final SomeArgs args = (SomeArgs) message.obj;
928 final Region magnifiedBounds = (Region) args.arg1;
Phil Weaver70439242016-03-10 15:15:49 -0800929 mCallbacks.onMagnificationRegionChanged(magnifiedBounds);
Alan Viverette214fb682015-11-17 09:47:11 -0500930 magnifiedBounds.recycle();
Svetoslav8e3feb12014-02-24 13:46:47 -0800931 } break;
932
933 case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: {
934 SomeArgs args = (SomeArgs) message.obj;
935 final int left = args.argi1;
936 final int top = args.argi2;
937 final int right = args.argi3;
938 final int bottom = args.argi4;
939 mCallbacks.onRectangleOnScreenRequested(left, top, right, bottom);
940 args.recycle();
941 } break;
942
943 case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: {
944 mCallbacks.onUserContextChanged();
945 } break;
946
947 case MESSAGE_NOTIFY_ROTATION_CHANGED: {
948 final int rotation = message.arg1;
949 mCallbacks.onRotationChanged(rotation);
950 } break;
951
952 case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
953 synchronized (mWindowManagerService.mWindowMap) {
Casey Burkhardt74922c62017-02-13 12:43:16 -0800954 if (mMagnifedViewport.isMagnifyingLocked()
955 || isForceShowingMagnifiableBoundsLocked()) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800956 mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true);
957 mWindowManagerService.scheduleAnimationLocked();
958 }
959 }
960 } break;
961 }
962 }
963 }
964 }
965
966 /**
967 * This class encapsulates the functionality related to computing the windows
968 * reported for accessibility purposes. These windows are all windows a sighted
969 * user can see on the screen.
970 */
971 private static final class WindowsForAccessibilityObserver {
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -0800972 private static final String LOG_TAG = TAG_WITH_CLASS_NAME ?
973 "WindowsForAccessibilityObserver" : TAG_WM;
Svetoslav8e3feb12014-02-24 13:46:47 -0800974
975 private static final boolean DEBUG = false;
976
977 private final SparseArray<WindowState> mTempWindowStates =
978 new SparseArray<WindowState>();
979
980 private final List<WindowInfo> mOldWindows = new ArrayList<WindowInfo>();
981
982 private final Set<IBinder> mTempBinderSet = new ArraySet<IBinder>();
983
984 private final RectF mTempRectF = new RectF();
985
986 private final Matrix mTempMatrix = new Matrix();
987
988 private final Point mTempPoint = new Point();
989
990 private final Rect mTempRect = new Rect();
991
992 private final Region mTempRegion = new Region();
993
994 private final Region mTempRegion1 = new Region();
995
996 private final Context mContext;
997
998 private final WindowManagerService mWindowManagerService;
999
1000 private final Handler mHandler;
1001
1002 private final WindowsForAccessibilityCallback mCallback;
1003
Svetoslavf7174e82014-06-12 11:29:35 -07001004 private final long mRecurringAccessibilityEventsIntervalMillis;
1005
Svetoslav8e3feb12014-02-24 13:46:47 -08001006 public WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
1007 WindowsForAccessibilityCallback callback) {
1008 mContext = windowManagerService.mContext;
1009 mWindowManagerService = windowManagerService;
1010 mCallback = callback;
1011 mHandler = new MyHandler(mWindowManagerService.mH.getLooper());
Svetoslavf7174e82014-06-12 11:29:35 -07001012 mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration
1013 .getSendRecurringAccessibilityEventsInterval();
Svetoslav8e3feb12014-02-24 13:46:47 -08001014 computeChangedWindows();
1015 }
1016
Svetoslav3a0d8782014-12-04 12:50:11 -08001017 public void performComputeChangedWindowsNotLocked() {
1018 mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS);
1019 computeChangedWindows();
1020 }
1021
Svetoslavf7174e82014-06-12 11:29:35 -07001022 public void scheduleComputeChangedWindowsLocked() {
Svetoslav3a0d8782014-12-04 12:50:11 -08001023 if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) {
Svetoslavf7174e82014-06-12 11:29:35 -07001024 mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS,
1025 mRecurringAccessibilityEventsIntervalMillis);
1026 }
1027 }
1028
Svetoslav8e3feb12014-02-24 13:46:47 -08001029 public void computeChangedWindows() {
1030 if (DEBUG) {
1031 Slog.i(LOG_TAG, "computeChangedWindows()");
1032 }
1033
Svetoslav3a0d8782014-12-04 12:50:11 -08001034 boolean windowsChanged = false;
1035 List<WindowInfo> windows = new ArrayList<WindowInfo>();
1036
Svetoslav8e3feb12014-02-24 13:46:47 -08001037 synchronized (mWindowManagerService.mWindowMap) {
Svetoslavf7174e82014-06-12 11:29:35 -07001038 // Do not send the windows if there is no current focus as
1039 // the window manager is still looking for where to put it.
1040 // We will do the work when we get a focus change callback.
1041 if (mWindowManagerService.mCurrentFocus == null) {
1042 return;
1043 }
1044
Svetoslav8e3feb12014-02-24 13:46:47 -08001045 WindowManager windowManager = (WindowManager)
1046 mContext.getSystemService(Context.WINDOW_SERVICE);
1047 windowManager.getDefaultDisplay().getRealSize(mTempPoint);
1048 final int screenWidth = mTempPoint.x;
1049 final int screenHeight = mTempPoint.y;
1050
1051 Region unaccountedSpace = mTempRegion;
1052 unaccountedSpace.set(0, 0, screenWidth, screenHeight);
1053
Wale Ogunwalef7cab102016-10-25 15:25:14 -07001054 final SparseArray<WindowState> visibleWindows = mTempWindowStates;
Svetoslav8e3feb12014-02-24 13:46:47 -08001055 populateVisibleWindowsOnScreenLocked(visibleWindows);
Svetoslav8e3feb12014-02-24 13:46:47 -08001056 Set<IBinder> addedWindows = mTempBinderSet;
1057 addedWindows.clear();
1058
Svetoslavf7174e82014-06-12 11:29:35 -07001059 boolean focusedWindowAdded = false;
1060
Svetoslav8e3feb12014-02-24 13:46:47 -08001061 final int visibleWindowCount = visibleWindows.size();
Allen Hairf20ac2c2016-02-11 17:42:59 -08001062 HashSet<Integer> skipRemainingWindowsForTasks = new HashSet<>();
Svetoslav8e3feb12014-02-24 13:46:47 -08001063 for (int i = visibleWindowCount - 1; i >= 0; i--) {
Alan Viverette9538eea2014-11-13 14:49:20 -08001064 final WindowState windowState = visibleWindows.valueAt(i);
Svetoslav8e3feb12014-02-24 13:46:47 -08001065 final int flags = windowState.mAttrs.flags;
Allen Hairf20ac2c2016-02-11 17:42:59 -08001066 final Task task = windowState.getTask();
1067
1068 // If the window is part of a task that we're finished with - ignore.
1069 if (task != null && skipRemainingWindowsForTasks.contains(task.mTaskId)) {
1070 continue;
1071 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001072
Svetoslav3a5c7212014-10-14 09:54:26 -07001073 // If the window is not touchable - ignore.
Svetoslavf7174e82014-06-12 11:29:35 -07001074 if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001075 continue;
1076 }
1077
Alan Viverette9538eea2014-11-13 14:49:20 -08001078 // Compute the bounds in the screen.
1079 final Rect boundsInScreen = mTempRect;
1080 computeWindowBoundsInScreen(windowState, boundsInScreen);
1081
Svetoslav8e3feb12014-02-24 13:46:47 -08001082 // If the window is completely covered by other windows - ignore.
1083 if (unaccountedSpace.quickReject(boundsInScreen)) {
1084 continue;
1085 }
1086
1087 // Add windows of certain types not covered by modal windows.
1088 if (isReportedWindowType(windowState.mAttrs.type)) {
1089 // Add the window to the ones to be reported.
Svetoslavf7174e82014-06-12 11:29:35 -07001090 WindowInfo window = obtainPopulatedWindowInfo(windowState, boundsInScreen);
Svetoslav8e3feb12014-02-24 13:46:47 -08001091 addedWindows.add(window.token);
Svetoslav8e3feb12014-02-24 13:46:47 -08001092 windows.add(window);
Svetoslavf7174e82014-06-12 11:29:35 -07001093 if (windowState.isFocused()) {
1094 focusedWindowAdded = true;
1095 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001096 }
1097
Alan Viveretted0c73f42014-11-18 10:25:04 -08001098 // Account for the space this window takes if the window
1099 // is not an accessibility overlay which does not change
1100 // the reported windows.
1101 if (windowState.mAttrs.type !=
1102 WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
1103 unaccountedSpace.op(boundsInScreen, unaccountedSpace,
1104 Region.Op.REVERSE_DIFFERENCE);
1105 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001106
Allen Hairf20ac2c2016-02-11 17:42:59 -08001107 // If a window is modal it prevents other windows from being touched
Svetoslav8e3feb12014-02-24 13:46:47 -08001108 if ((flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1109 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
Phil Weaverac9ad702016-07-27 18:03:10 -07001110 // Account for all space in the task, whether the windows in it are
1111 // touchable or not. The modal window blocks all touches from the task's
1112 // area.
1113 unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace,
1114 Region.Op.REVERSE_DIFFERENCE);
1115
Allen Hairf20ac2c2016-02-11 17:42:59 -08001116 if (task != null) {
1117 // If the window is associated with a particular task, we can skip the
1118 // rest of the windows for that task.
1119 skipRemainingWindowsForTasks.add(task.mTaskId);
1120 continue;
1121 } else {
1122 // If the window is not associated with a particular task, then it is
1123 // globally modal. In this case we can skip all remaining windows.
1124 break;
1125 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001126 }
Phil Weaverac9ad702016-07-27 18:03:10 -07001127 // We figured out what is touchable for the entire screen - done.
1128 if (unaccountedSpace.isEmpty()) {
1129 break;
1130 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001131 }
1132
Svetoslavf7174e82014-06-12 11:29:35 -07001133 // Always report the focused window.
1134 if (!focusedWindowAdded) {
1135 for (int i = visibleWindowCount - 1; i >= 0; i--) {
1136 WindowState windowState = visibleWindows.valueAt(i);
1137 if (windowState.isFocused()) {
1138 // Compute the bounds in the screen.
1139 Rect boundsInScreen = mTempRect;
1140 computeWindowBoundsInScreen(windowState, boundsInScreen);
1141
1142 // Add the window to the ones to be reported.
1143 WindowInfo window = obtainPopulatedWindowInfo(windowState,
1144 boundsInScreen);
1145 addedWindows.add(window.token);
1146 windows.add(window);
1147 break;
1148 }
1149 }
1150 }
1151
Svetoslav8e3feb12014-02-24 13:46:47 -08001152 // Remove child/parent references to windows that were not added.
1153 final int windowCount = windows.size();
1154 for (int i = 0; i < windowCount; i++) {
1155 WindowInfo window = windows.get(i);
1156 if (!addedWindows.contains(window.parentToken)) {
1157 window.parentToken = null;
1158 }
1159 if (window.childTokens != null) {
1160 final int childTokenCount = window.childTokens.size();
1161 for (int j = childTokenCount - 1; j >= 0; j--) {
1162 if (!addedWindows.contains(window.childTokens.get(j))) {
1163 window.childTokens.remove(j);
1164 }
1165 }
1166 // Leave the child token list if empty.
1167 }
1168 }
1169
1170 visibleWindows.clear();
1171 addedWindows.clear();
1172
1173 // We computed the windows and if they changed notify the client.
Svetoslav8e3feb12014-02-24 13:46:47 -08001174 if (mOldWindows.size() != windows.size()) {
1175 // Different size means something changed.
1176 windowsChanged = true;
1177 } else if (!mOldWindows.isEmpty() || !windows.isEmpty()) {
1178 // Since we always traverse windows from high to low layer
1179 // the old and new windows at the same index should be the
1180 // same, otherwise something changed.
1181 for (int i = 0; i < windowCount; i++) {
1182 WindowInfo oldWindow = mOldWindows.get(i);
1183 WindowInfo newWindow = windows.get(i);
1184 // We do not care for layer changes given the window
1185 // order does not change. This brings no new information
1186 // to the clients.
1187 if (windowChangedNoLayer(oldWindow, newWindow)) {
1188 windowsChanged = true;
1189 break;
1190 }
1191 }
1192 }
1193
1194 if (windowsChanged) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001195 cacheWindows(windows);
Svetoslav8e3feb12014-02-24 13:46:47 -08001196 }
1197 }
Svetoslav3a0d8782014-12-04 12:50:11 -08001198
1199 // Now we do not hold the lock, so send the windows over.
1200 if (windowsChanged) {
1201 if (DEBUG) {
1202 Log.i(LOG_TAG, "Windows changed:" + windows);
1203 }
1204 mCallback.onWindowsForAccessibilityChanged(windows);
1205 } else {
1206 if (DEBUG) {
1207 Log.i(LOG_TAG, "No windows changed.");
1208 }
1209 }
1210
1211 // Recycle the windows as we do not need them.
1212 clearAndRecycleWindows(windows);
Svetoslav8e3feb12014-02-24 13:46:47 -08001213 }
1214
Svetoslavf7174e82014-06-12 11:29:35 -07001215 private void computeWindowBoundsInScreen(WindowState windowState, Rect outBounds) {
1216 // Get the touchable frame.
1217 Region touchableRegion = mTempRegion1;
1218 windowState.getTouchableRegion(touchableRegion);
1219 Rect touchableFrame = mTempRect;
1220 touchableRegion.getBounds(touchableFrame);
1221
1222 // Move to origin as all transforms are captured by the matrix.
1223 RectF windowFrame = mTempRectF;
1224 windowFrame.set(touchableFrame);
1225 windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
1226
1227 // Map the frame to get what appears on the screen.
1228 Matrix matrix = mTempMatrix;
1229 populateTransformationMatrixLocked(windowState, matrix);
1230 matrix.mapRect(windowFrame);
1231
1232 // Got the bounds.
1233 outBounds.set((int) windowFrame.left, (int) windowFrame.top,
1234 (int) windowFrame.right, (int) windowFrame.bottom);
1235 }
1236
Wale Ogunwaleadde52e2016-07-16 13:11:55 -07001237 private static WindowInfo obtainPopulatedWindowInfo(
1238 WindowState windowState, Rect boundsInScreen) {
1239 final WindowInfo window = windowState.getWindowInfo();
Svetoslavf7174e82014-06-12 11:29:35 -07001240 window.boundsInScreen.set(boundsInScreen);
Svetoslavf7174e82014-06-12 11:29:35 -07001241 return window;
1242 }
1243
Svetoslav8e3feb12014-02-24 13:46:47 -08001244 private void cacheWindows(List<WindowInfo> windows) {
1245 final int oldWindowCount = mOldWindows.size();
1246 for (int i = oldWindowCount - 1; i >= 0; i--) {
1247 mOldWindows.remove(i).recycle();
1248 }
1249 final int newWindowCount = windows.size();
1250 for (int i = 0; i < newWindowCount; i++) {
1251 WindowInfo newWindow = windows.get(i);
1252 mOldWindows.add(WindowInfo.obtain(newWindow));
1253 }
1254 }
1255
1256 private boolean windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow) {
1257 if (oldWindow == newWindow) {
1258 return false;
1259 }
Svetoslavf7174e82014-06-12 11:29:35 -07001260 if (oldWindow == null) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001261 return true;
1262 }
Svetoslavf7174e82014-06-12 11:29:35 -07001263 if (newWindow == null) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001264 return true;
1265 }
1266 if (oldWindow.type != newWindow.type) {
1267 return true;
1268 }
1269 if (oldWindow.focused != newWindow.focused) {
1270 return true;
1271 }
1272 if (oldWindow.token == null) {
1273 if (newWindow.token != null) {
1274 return true;
1275 }
1276 } else if (!oldWindow.token.equals(newWindow.token)) {
1277 return true;
1278 }
1279 if (oldWindow.parentToken == null) {
1280 if (newWindow.parentToken != null) {
1281 return true;
1282 }
1283 } else if (!oldWindow.parentToken.equals(newWindow.parentToken)) {
1284 return true;
1285 }
1286 if (!oldWindow.boundsInScreen.equals(newWindow.boundsInScreen)) {
1287 return true;
1288 }
1289 if (oldWindow.childTokens != null && newWindow.childTokens != null
1290 && !oldWindow.childTokens.equals(newWindow.childTokens)) {
1291 return true;
1292 }
Phil Weaver396d5492016-03-22 17:53:50 -07001293 if (!TextUtils.equals(oldWindow.title, newWindow.title)) {
1294 return true;
1295 }
1296 if (oldWindow.accessibilityIdOfAnchor != newWindow.accessibilityIdOfAnchor) {
1297 return true;
1298 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001299 return false;
1300 }
1301
Svetoslav3a0d8782014-12-04 12:50:11 -08001302 private static void clearAndRecycleWindows(List<WindowInfo> windows) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001303 final int windowCount = windows.size();
1304 for (int i = windowCount - 1; i >= 0; i--) {
1305 windows.remove(i).recycle();
1306 }
1307 }
1308
1309 private static boolean isReportedWindowType(int windowType) {
Jorim Jaggi73294b62016-10-26 18:02:36 -07001310 return (windowType != WindowManager.LayoutParams.TYPE_WALLPAPER
Svetoslav8e3feb12014-02-24 13:46:47 -08001311 && windowType != WindowManager.LayoutParams.TYPE_BOOT_PROGRESS
1312 && windowType != WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY
1313 && windowType != WindowManager.LayoutParams.TYPE_DRAG
Selim Cinekf83e8242015-05-19 18:08:14 -07001314 && windowType != WindowManager.LayoutParams.TYPE_INPUT_CONSUMER
Svetoslav8e3feb12014-02-24 13:46:47 -08001315 && windowType != WindowManager.LayoutParams.TYPE_POINTER
Svetoslav8e3feb12014-02-24 13:46:47 -08001316 && windowType != WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY
1317 && windowType != WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
1318 && windowType != WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY
1319 && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
1320 }
1321
1322 private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
Wale Ogunwalef7cab102016-10-25 15:25:14 -07001323 final DisplayContent dc = mWindowManagerService.getDefaultDisplayContentLocked();
Wale Ogunwaled1880962016-11-08 10:31:59 -08001324 dc.forAllWindows((w) -> {
1325 if (w.isVisibleLw()) {
1326 outWindows.put(w.mLayer, 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}