blob: 5abc4e4d2e565faafe31fd848f501d92779b55ff [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 /** NOTE: This has to be called within a surface transaction. */
226 public void setForceShowMagnifiableBoundsLocked(boolean show) {
227 if (mDisplayMagnifier != null) {
228 mDisplayMagnifier.setForceShowMagnifiableBoundsLocked(show);
229 mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked();
230 }
231 }
232
Svetoslav8e3feb12014-02-24 13:46:47 -0800233 private static void populateTransformationMatrixLocked(WindowState windowState,
234 Matrix outMatrix) {
235 sTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx;
236 sTempFloats[Matrix.MSKEW_Y] = windowState.mWinAnimator.mDtDx;
Robert Carr0edf18f2017-02-21 20:01:47 -0800237 sTempFloats[Matrix.MSKEW_X] = windowState.mWinAnimator.mDtDy;
238 sTempFloats[Matrix.MSCALE_Y] = windowState.mWinAnimator.mDsDy;
Filip Gruszczynski2a6a2c22015-10-14 12:00:53 -0700239 sTempFloats[Matrix.MTRANS_X] = windowState.mShownPosition.x;
240 sTempFloats[Matrix.MTRANS_Y] = windowState.mShownPosition.y;
Svetoslav8e3feb12014-02-24 13:46:47 -0800241 sTempFloats[Matrix.MPERSP_0] = 0;
242 sTempFloats[Matrix.MPERSP_1] = 0;
243 sTempFloats[Matrix.MPERSP_2] = 1;
244 outMatrix.setValues(sTempFloats);
245 }
246
247 /**
248 * This class encapsulates the functionality related to display magnification.
249 */
250 private static final class DisplayMagnifier {
251
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -0800252 private static final String LOG_TAG = TAG_WITH_CLASS_NAME ? "DisplayMagnifier" : TAG_WM;
Svetoslav8e3feb12014-02-24 13:46:47 -0800253
254 private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
255 private static final boolean DEBUG_ROTATION = false;
256 private static final boolean DEBUG_LAYERS = false;
257 private static final boolean DEBUG_RECTANGLE_REQUESTED = false;
258 private static final boolean DEBUG_VIEWPORT_WINDOW = false;
259
260 private final Rect mTempRect1 = new Rect();
261 private final Rect mTempRect2 = new Rect();
262
263 private final Region mTempRegion1 = new Region();
264 private final Region mTempRegion2 = new Region();
265 private final Region mTempRegion3 = new Region();
266 private final Region mTempRegion4 = new Region();
267
268 private final Context mContext;
269 private final WindowManagerService mWindowManagerService;
270 private final MagnifiedViewport mMagnifedViewport;
271 private final Handler mHandler;
272
273 private final MagnificationCallbacks mCallbacks;
274
275 private final long mLongAnimationDuration;
276
Casey Burkhardt74922c62017-02-13 12:43:16 -0800277 private boolean mForceShowMagnifiableBounds = false;
278
Svetoslav8e3feb12014-02-24 13:46:47 -0800279 public DisplayMagnifier(WindowManagerService windowManagerService,
280 MagnificationCallbacks callbacks) {
281 mContext = windowManagerService.mContext;
282 mWindowManagerService = windowManagerService;
283 mCallbacks = callbacks;
284 mHandler = new MyHandler(mWindowManagerService.mH.getLooper());
285 mMagnifedViewport = new MagnifiedViewport();
286 mLongAnimationDuration = mContext.getResources().getInteger(
287 com.android.internal.R.integer.config_longAnimTime);
288 }
289
290 public void setMagnificationSpecLocked(MagnificationSpec spec) {
291 mMagnifedViewport.updateMagnificationSpecLocked(spec);
292 mMagnifedViewport.recomputeBoundsLocked();
293 mWindowManagerService.scheduleAnimationLocked();
294 }
295
Casey Burkhardt74922c62017-02-13 12:43:16 -0800296 public void setForceShowMagnifiableBoundsLocked(boolean show) {
297 mForceShowMagnifiableBounds = show;
298 mMagnifedViewport.setMagnifiedRegionBorderShownLocked(show, true);
299 }
300
301 public boolean isForceShowingMagnifiableBoundsLocked() {
302 return mForceShowMagnifiableBounds;
303 }
304
Svetoslavf7174e82014-06-12 11:29:35 -0700305 public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800306 if (DEBUG_RECTANGLE_REQUESTED) {
307 Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
308 }
309 if (!mMagnifedViewport.isMagnifyingLocked()) {
310 return;
311 }
312 Rect magnifiedRegionBounds = mTempRect2;
313 mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds);
314 if (magnifiedRegionBounds.contains(rectangle)) {
315 return;
316 }
317 SomeArgs args = SomeArgs.obtain();
318 args.argi1 = rectangle.left;
319 args.argi2 = rectangle.top;
320 args.argi3 = rectangle.right;
321 args.argi4 = rectangle.bottom;
322 mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED,
323 args).sendToTarget();
324 }
325
326 public void onWindowLayersChangedLocked() {
327 if (DEBUG_LAYERS) {
328 Slog.i(LOG_TAG, "Layers changed.");
329 }
330 mMagnifedViewport.recomputeBoundsLocked();
331 mWindowManagerService.scheduleAnimationLocked();
332 }
333
Andrii Kulian8ee72852017-03-10 10:36:45 -0800334 public void onRotationChangedLocked(DisplayContent displayContent) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800335 if (DEBUG_ROTATION) {
Andrii Kulian8ee72852017-03-10 10:36:45 -0800336 final int rotation = displayContent.getRotation();
337 Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation)
Svetoslav8e3feb12014-02-24 13:46:47 -0800338 + " displayId: " + displayContent.getDisplayId());
339 }
340 mMagnifedViewport.onRotationChangedLocked();
341 mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
342 }
343
344 public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
345 if (DEBUG_WINDOW_TRANSITIONS) {
346 Slog.i(LOG_TAG, "Window transition: "
347 + AppTransition.appTransitionToString(transition)
348 + " displayId: " + windowState.getDisplayId());
349 }
350 final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
351 if (magnifying) {
352 switch (transition) {
353 case AppTransition.TRANSIT_ACTIVITY_OPEN:
354 case AppTransition.TRANSIT_TASK_OPEN:
355 case AppTransition.TRANSIT_TASK_TO_FRONT:
356 case AppTransition.TRANSIT_WALLPAPER_OPEN:
357 case AppTransition.TRANSIT_WALLPAPER_CLOSE:
358 case AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN: {
359 mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED);
360 }
361 }
362 }
363 }
364
365 public void onWindowTransitionLocked(WindowState windowState, int transition) {
366 if (DEBUG_WINDOW_TRANSITIONS) {
367 Slog.i(LOG_TAG, "Window transition: "
368 + AppTransition.appTransitionToString(transition)
369 + " displayId: " + windowState.getDisplayId());
370 }
371 final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
372 final int type = windowState.mAttrs.type;
373 switch (transition) {
374 case WindowManagerPolicy.TRANSIT_ENTER:
375 case WindowManagerPolicy.TRANSIT_SHOW: {
376 if (!magnifying) {
377 break;
378 }
379 switch (type) {
380 case WindowManager.LayoutParams.TYPE_APPLICATION:
Chong Zhangfea963e2016-08-15 17:14:16 -0700381 case WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION:
Svetoslav8e3feb12014-02-24 13:46:47 -0800382 case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
383 case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
384 case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
Wale Ogunwale0a4dc222015-04-14 12:58:42 -0700385 case WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL:
Svetoslav8e3feb12014-02-24 13:46:47 -0800386 case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
387 case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
388 case WindowManager.LayoutParams.TYPE_PHONE:
389 case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
390 case WindowManager.LayoutParams.TYPE_TOAST:
391 case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
Wale Ogunwale5cd907d2017-01-26 14:14:08 -0800392 case WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY:
Svetoslav8e3feb12014-02-24 13:46:47 -0800393 case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
394 case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
395 case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
396 case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
397 case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
Jason Monk8f7f3182015-11-18 16:35:14 -0500398 case WindowManager.LayoutParams.TYPE_QS_DIALOG:
Adrian Roos9a645132014-10-08 02:59:56 +0200399 case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: {
Svetoslav8e3feb12014-02-24 13:46:47 -0800400 Rect magnifiedRegionBounds = mTempRect2;
401 mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(
402 magnifiedRegionBounds);
403 Rect touchableRegionBounds = mTempRect1;
404 windowState.getTouchableRegion(mTempRegion1);
405 mTempRegion1.getBounds(touchableRegionBounds);
406 if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) {
407 mCallbacks.onRectangleOnScreenRequested(
408 touchableRegionBounds.left,
409 touchableRegionBounds.top,
410 touchableRegionBounds.right,
411 touchableRegionBounds.bottom);
412 }
413 } break;
414 } break;
415 }
416 }
417 }
418
419 public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
420 MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
421 if (spec != null && !spec.isNop()) {
422 WindowManagerPolicy policy = mWindowManagerService.mPolicy;
423 final int windowType = windowState.mAttrs.type;
Wale Ogunwale7ed4d372016-07-09 15:28:55 -0700424 if (!policy.isTopLevelWindow(windowType) && windowState.isChildWindow()
Svetoslav8e3feb12014-02-24 13:46:47 -0800425 && !policy.canMagnifyWindow(windowType)) {
426 return null;
427 }
428 if (!policy.canMagnifyWindow(windowState.mAttrs.type)) {
429 return null;
430 }
431 }
432 return spec;
433 }
434
Phil Weaver70439242016-03-10 15:15:49 -0800435 public void getMagnificationRegionLocked(Region outMagnificationRegion) {
436 mMagnifedViewport.getMagnificationRegionLocked(outMagnificationRegion);
Alan Viverette59e53a12016-03-28 13:41:32 -0400437 }
438
Svetoslav8e3feb12014-02-24 13:46:47 -0800439 public void destroyLocked() {
440 mMagnifedViewport.destroyWindow();
441 }
442
443 /** NOTE: This has to be called within a surface transaction. */
444 public void drawMagnifiedRegionBorderIfNeededLocked() {
445 mMagnifedViewport.drawWindowIfNeededLocked();
446 }
447
448 private final class MagnifiedViewport {
449
Svetoslav8e3feb12014-02-24 13:46:47 -0800450 private final SparseArray<WindowState> mTempWindowStates =
451 new SparseArray<WindowState>();
452
453 private final RectF mTempRectF = new RectF();
454
455 private final Point mTempPoint = new Point();
456
457 private final Matrix mTempMatrix = new Matrix();
458
Phil Weaver70439242016-03-10 15:15:49 -0800459 private final Region mMagnificationRegion = new Region();
460 private final Region mOldMagnificationRegion = new Region();
Svetoslav8e3feb12014-02-24 13:46:47 -0800461
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800462 private final Path mCircularPath;
463
Svetoslav8e3feb12014-02-24 13:46:47 -0800464 private final MagnificationSpec mMagnificationSpec = MagnificationSpec.obtain();
465
466 private final WindowManager mWindowManager;
467
Svetoslav7505e332014-08-22 12:14:28 -0700468 private final float mBorderWidth;
Svetoslav8e3feb12014-02-24 13:46:47 -0800469 private final int mHalfBorderWidth;
Svetoslav7505e332014-08-22 12:14:28 -0700470 private final int mDrawBorderInset;
Svetoslav8e3feb12014-02-24 13:46:47 -0800471
472 private final ViewportWindow mWindow;
473
474 private boolean mFullRedrawNeeded;
475
476 public MagnifiedViewport() {
477 mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE);
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800478 mBorderWidth = mContext.getResources().getDimension(
479 com.android.internal.R.dimen.accessibility_magnification_indicator_width);
Svetoslav7505e332014-08-22 12:14:28 -0700480 mHalfBorderWidth = (int) Math.ceil(mBorderWidth / 2);
481 mDrawBorderInset = (int) mBorderWidth / 2;
Svetoslav8e3feb12014-02-24 13:46:47 -0800482 mWindow = new ViewportWindow(mContext);
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800483
Adam Powell01f280d2015-05-18 16:07:42 -0700484 if (mContext.getResources().getConfiguration().isScreenRound()) {
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800485 mCircularPath = new Path();
486 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
487 final int centerXY = mTempPoint.x / 2;
488 mCircularPath.addCircle(centerXY, centerXY, centerXY, Path.Direction.CW);
489 } else {
490 mCircularPath = null;
491 }
492
Svetoslav8e3feb12014-02-24 13:46:47 -0800493 recomputeBoundsLocked();
494 }
495
Phil Weaver70439242016-03-10 15:15:49 -0800496 public void getMagnificationRegionLocked(@NonNull Region outMagnificationRegion) {
497 outMagnificationRegion.set(mMagnificationRegion);
Alan Viverette59e53a12016-03-28 13:41:32 -0400498 }
499
Svetoslav8e3feb12014-02-24 13:46:47 -0800500 public void updateMagnificationSpecLocked(MagnificationSpec spec) {
501 if (spec != null) {
502 mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY);
503 } else {
504 mMagnificationSpec.clear();
505 }
506 // If this message is pending we are in a rotation animation and do not want
507 // to show the border. We will do so when the pending message is handled.
Svetoslav7505e332014-08-22 12:14:28 -0700508 if (!mHandler.hasMessages(
509 MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
Casey Burkhardt74922c62017-02-13 12:43:16 -0800510 setMagnifiedRegionBorderShownLocked(
511 isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked(), true);
Svetoslav8e3feb12014-02-24 13:46:47 -0800512 }
513 }
514
515 public void recomputeBoundsLocked() {
516 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
517 final int screenWidth = mTempPoint.x;
518 final int screenHeight = mTempPoint.y;
519
Phil Weaver70439242016-03-10 15:15:49 -0800520 mMagnificationRegion.set(0, 0, 0, 0);
521 final Region availableBounds = mTempRegion1;
522 availableBounds.set(0, 0, screenWidth, screenHeight);
Svetoslav8e3feb12014-02-24 13:46:47 -0800523
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800524 if (mCircularPath != null) {
Phil Weaver70439242016-03-10 15:15:49 -0800525 availableBounds.setPath(mCircularPath, availableBounds);
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800526 }
527
Svetoslav8e3feb12014-02-24 13:46:47 -0800528 Region nonMagnifiedBounds = mTempRegion4;
Svet Ganovb21df802014-09-01 19:06:33 -0700529 nonMagnifiedBounds.set(0, 0, 0, 0);
Svetoslav8e3feb12014-02-24 13:46:47 -0800530
531 SparseArray<WindowState> visibleWindows = mTempWindowStates;
532 visibleWindows.clear();
533 populateWindowsOnScreenLocked(visibleWindows);
534
535 final int visibleWindowCount = visibleWindows.size();
536 for (int i = visibleWindowCount - 1; i >= 0; i--) {
537 WindowState windowState = visibleWindows.valueAt(i);
538 if (windowState.mAttrs.type == WindowManager
539 .LayoutParams.TYPE_MAGNIFICATION_OVERLAY) {
540 continue;
541 }
542
Phil Weaver65c06702016-03-15 15:33:46 -0700543 // Consider the touchable portion of the window
Svetoslav8e3feb12014-02-24 13:46:47 -0800544 Matrix matrix = mTempMatrix;
545 populateTransformationMatrixLocked(windowState, matrix);
Phil Weaver65c06702016-03-15 15:33:46 -0700546 Region touchableRegion = mTempRegion3;
547 windowState.getTouchableRegion(touchableRegion);
548 Rect touchableFrame = mTempRect1;
549 touchableRegion.getBounds(touchableFrame);
Svetoslav8e3feb12014-02-24 13:46:47 -0800550 RectF windowFrame = mTempRectF;
Phil Weaver65c06702016-03-15 15:33:46 -0700551 windowFrame.set(touchableFrame);
552 windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
553 matrix.mapRect(windowFrame);
554 Region windowBounds = mTempRegion2;
555 windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
556 (int) windowFrame.right, (int) windowFrame.bottom);
557 // Only update new regions
558 Region portionOfWindowAlreadyAccountedFor = mTempRegion3;
Phil Weaver70439242016-03-10 15:15:49 -0800559 portionOfWindowAlreadyAccountedFor.set(mMagnificationRegion);
Phil Weaver65c06702016-03-15 15:33:46 -0700560 portionOfWindowAlreadyAccountedFor.op(nonMagnifiedBounds, Region.Op.UNION);
561 windowBounds.op(portionOfWindowAlreadyAccountedFor, Region.Op.DIFFERENCE);
Svetoslav8e3feb12014-02-24 13:46:47 -0800562
563 if (mWindowManagerService.mPolicy.canMagnifyWindow(windowState.mAttrs.type)) {
Phil Weaver70439242016-03-10 15:15:49 -0800564 mMagnificationRegion.op(windowBounds, Region.Op.UNION);
565 mMagnificationRegion.op(availableBounds, Region.Op.INTERSECT);
Svetoslav8e3feb12014-02-24 13:46:47 -0800566 } else {
Svetoslav8e3feb12014-02-24 13:46:47 -0800567 nonMagnifiedBounds.op(windowBounds, Region.Op.UNION);
Phil Weaver70439242016-03-10 15:15:49 -0800568 availableBounds.op(windowBounds, Region.Op.DIFFERENCE);
Svetoslav8e3feb12014-02-24 13:46:47 -0800569 }
570
Phil Weaver65c06702016-03-15 15:33:46 -0700571 // Update accounted bounds
Svetoslav8e3feb12014-02-24 13:46:47 -0800572 Region accountedBounds = mTempRegion2;
Phil Weaver70439242016-03-10 15:15:49 -0800573 accountedBounds.set(mMagnificationRegion);
Svetoslav8e3feb12014-02-24 13:46:47 -0800574 accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION);
575 accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT);
576
577 if (accountedBounds.isRect()) {
578 Rect accountedFrame = mTempRect1;
579 accountedBounds.getBounds(accountedFrame);
580 if (accountedFrame.width() == screenWidth
581 && accountedFrame.height() == screenHeight) {
582 break;
583 }
584 }
585 }
586
587 visibleWindows.clear();
588
Phil Weaver70439242016-03-10 15:15:49 -0800589 mMagnificationRegion.op(mDrawBorderInset, mDrawBorderInset,
Svetoslav7505e332014-08-22 12:14:28 -0700590 screenWidth - mDrawBorderInset, screenHeight - mDrawBorderInset,
Svetoslav8e3feb12014-02-24 13:46:47 -0800591 Region.Op.INTERSECT);
592
Phil Weaver70439242016-03-10 15:15:49 -0800593 final boolean magnifiedChanged =
594 !mOldMagnificationRegion.equals(mMagnificationRegion);
595 if (magnifiedChanged) {
596 mWindow.setBounds(mMagnificationRegion);
597 final Rect dirtyRect = mTempRect1;
598 if (mFullRedrawNeeded) {
599 mFullRedrawNeeded = false;
600 dirtyRect.set(mDrawBorderInset, mDrawBorderInset,
601 screenWidth - mDrawBorderInset,
602 screenHeight - mDrawBorderInset);
603 mWindow.invalidate(dirtyRect);
604 } else {
605 final Region dirtyRegion = mTempRegion3;
606 dirtyRegion.set(mMagnificationRegion);
607 dirtyRegion.op(mOldMagnificationRegion, Region.Op.UNION);
608 dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT);
609 dirtyRegion.getBounds(dirtyRect);
610 mWindow.invalidate(dirtyRect);
Svetoslav8e3feb12014-02-24 13:46:47 -0800611 }
612
Phil Weaver70439242016-03-10 15:15:49 -0800613 mOldMagnificationRegion.set(mMagnificationRegion);
Alan Viverette214fb682015-11-17 09:47:11 -0500614 final SomeArgs args = SomeArgs.obtain();
Phil Weaver70439242016-03-10 15:15:49 -0800615 args.arg1 = Region.obtain(mMagnificationRegion);
Alan Viverette214fb682015-11-17 09:47:11 -0500616 mHandler.obtainMessage(
Phil Weaver70439242016-03-10 15:15:49 -0800617 MyHandler.MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED, args)
618 .sendToTarget();
Svetoslav8e3feb12014-02-24 13:46:47 -0800619 }
620 }
621
622 public void onRotationChangedLocked() {
Casey Burkhardt74922c62017-02-13 12:43:16 -0800623 // If we are showing the magnification border, hide it immediately so
Svetoslav8e3feb12014-02-24 13:46:47 -0800624 // the user does not see strange artifacts during rotation. The screenshot
Casey Burkhardt74922c62017-02-13 12:43:16 -0800625 // used for rotation already has the border. After the rotation is complete
Svetoslav8e3feb12014-02-24 13:46:47 -0800626 // we will show the border.
Casey Burkhardt74922c62017-02-13 12:43:16 -0800627 if (isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked()) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800628 setMagnifiedRegionBorderShownLocked(false, false);
629 final long delay = (long) (mLongAnimationDuration
Dianne Hackborneb94fa72014-06-03 17:48:12 -0700630 * mWindowManagerService.getWindowAnimationScaleLocked());
Svetoslav8e3feb12014-02-24 13:46:47 -0800631 Message message = mHandler.obtainMessage(
632 MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED);
633 mHandler.sendMessageDelayed(message, delay);
634 }
635 recomputeBoundsLocked();
636 mWindow.updateSize();
637 }
638
639 public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) {
640 if (shown) {
641 mFullRedrawNeeded = true;
Phil Weaver70439242016-03-10 15:15:49 -0800642 mOldMagnificationRegion.set(0, 0, 0, 0);
Svetoslav8e3feb12014-02-24 13:46:47 -0800643 }
644 mWindow.setShown(shown, animate);
645 }
646
647 public void getMagnifiedFrameInContentCoordsLocked(Rect rect) {
648 MagnificationSpec spec = mMagnificationSpec;
Phil Weaver70439242016-03-10 15:15:49 -0800649 mMagnificationRegion.getBounds(rect);
Svetoslav8e3feb12014-02-24 13:46:47 -0800650 rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
651 rect.scale(1.0f / spec.scale);
652 }
653
654 public boolean isMagnifyingLocked() {
655 return mMagnificationSpec.scale > 1.0f;
656 }
657
658 public MagnificationSpec getMagnificationSpecLocked() {
659 return mMagnificationSpec;
660 }
661
662 /** NOTE: This has to be called within a surface transaction. */
663 public void drawWindowIfNeededLocked() {
664 recomputeBoundsLocked();
665 mWindow.drawIfNeeded();
666 }
667
668 public void destroyWindow() {
669 mWindow.releaseSurface();
670 }
671
672 private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
Wale Ogunwalef7cab102016-10-25 15:25:14 -0700673 final DisplayContent dc = mWindowManagerService.getDefaultDisplayContentLocked();
Wale Ogunwaled1880962016-11-08 10:31:59 -0800674 dc.forAllWindows((w) -> {
675 if (w.isOnScreen() && w.isVisibleLw()
676 && !w.mWinAnimator.mEnterAnimationPending) {
677 outWindows.put(w.mLayer, w);
Svetoslav8e3feb12014-02-24 13:46:47 -0800678 }
Wale Ogunwaled1880962016-11-08 10:31:59 -0800679 }, false /* traverseTopToBottom */ );
Svetoslav8e3feb12014-02-24 13:46:47 -0800680 }
681
682 private final class ViewportWindow {
683 private static final String SURFACE_TITLE = "Magnification Overlay";
684
Svetoslav8e3feb12014-02-24 13:46:47 -0800685 private final Region mBounds = new Region();
686 private final Rect mDirtyRect = new Rect();
687 private final Paint mPaint = new Paint();
688
Svetoslav8e3feb12014-02-24 13:46:47 -0800689 private final SurfaceControl mSurfaceControl;
690 private final Surface mSurface = new Surface();
691
Svet Ganovb21df802014-09-01 19:06:33 -0700692 private final AnimationController mAnimationController;
693
Svetoslav8e3feb12014-02-24 13:46:47 -0800694 private boolean mShown;
695 private int mAlpha;
696
697 private boolean mInvalidated;
698
699 public ViewportWindow(Context context) {
700 SurfaceControl surfaceControl = null;
701 try {
702 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
703 surfaceControl = new SurfaceControl(mWindowManagerService.mFxSession,
704 SURFACE_TITLE, mTempPoint.x, mTempPoint.y, PixelFormat.TRANSLUCENT,
705 SurfaceControl.HIDDEN);
706 } catch (OutOfResourcesException oore) {
707 /* ignore */
708 }
709 mSurfaceControl = surfaceControl;
710 mSurfaceControl.setLayerStack(mWindowManager.getDefaultDisplay()
711 .getLayerStack());
Wale Ogunwale5cd907d2017-01-26 14:14:08 -0800712 mSurfaceControl.setLayer(mWindowManagerService.mPolicy.getWindowLayerFromTypeLw(
Svetoslav8e3feb12014-02-24 13:46:47 -0800713 WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY)
714 * WindowManagerService.TYPE_LAYER_MULTIPLIER);
715 mSurfaceControl.setPosition(0, 0);
716 mSurface.copyFrom(mSurfaceControl);
717
Svet Ganovb21df802014-09-01 19:06:33 -0700718 mAnimationController = new AnimationController(context,
719 mWindowManagerService.mH.getLooper());
720
Svetoslav8e3feb12014-02-24 13:46:47 -0800721 TypedValue typedValue = new TypedValue();
722 context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight,
723 typedValue, true);
Alan Viverette4a357cd2015-03-18 18:37:18 -0700724 final int borderColor = context.getColor(typedValue.resourceId);
Svetoslav8e3feb12014-02-24 13:46:47 -0800725
726 mPaint.setStyle(Paint.Style.STROKE);
727 mPaint.setStrokeWidth(mBorderWidth);
728 mPaint.setColor(borderColor);
729
Svetoslav8e3feb12014-02-24 13:46:47 -0800730 mInvalidated = true;
731 }
732
733 public void setShown(boolean shown, boolean animate) {
734 synchronized (mWindowManagerService.mWindowMap) {
735 if (mShown == shown) {
736 return;
737 }
738 mShown = shown;
Svet Ganovb21df802014-09-01 19:06:33 -0700739 mAnimationController.onFrameShownStateChanged(shown, animate);
Svetoslav8e3feb12014-02-24 13:46:47 -0800740 if (DEBUG_VIEWPORT_WINDOW) {
741 Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown);
742 }
743 }
744 }
745
746 @SuppressWarnings("unused")
747 // Called reflectively from an animator.
748 public int getAlpha() {
749 synchronized (mWindowManagerService.mWindowMap) {
750 return mAlpha;
751 }
752 }
753
754 public void setAlpha(int alpha) {
755 synchronized (mWindowManagerService.mWindowMap) {
756 if (mAlpha == alpha) {
757 return;
758 }
759 mAlpha = alpha;
760 invalidate(null);
761 if (DEBUG_VIEWPORT_WINDOW) {
762 Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha);
763 }
764 }
765 }
766
767 public void setBounds(Region bounds) {
768 synchronized (mWindowManagerService.mWindowMap) {
769 if (mBounds.equals(bounds)) {
770 return;
771 }
772 mBounds.set(bounds);
773 invalidate(mDirtyRect);
774 if (DEBUG_VIEWPORT_WINDOW) {
775 Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds);
776 }
777 }
778 }
779
780 public void updateSize() {
781 synchronized (mWindowManagerService.mWindowMap) {
782 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
783 mSurfaceControl.setSize(mTempPoint.x, mTempPoint.y);
784 invalidate(mDirtyRect);
785 }
786 }
787
788 public void invalidate(Rect dirtyRect) {
789 if (dirtyRect != null) {
790 mDirtyRect.set(dirtyRect);
791 } else {
792 mDirtyRect.setEmpty();
793 }
794 mInvalidated = true;
795 mWindowManagerService.scheduleAnimationLocked();
796 }
797
798 /** NOTE: This has to be called within a surface transaction. */
799 public void drawIfNeeded() {
800 synchronized (mWindowManagerService.mWindowMap) {
801 if (!mInvalidated) {
802 return;
803 }
804 mInvalidated = false;
805 Canvas canvas = null;
806 try {
807 // Empty dirty rectangle means unspecified.
808 if (mDirtyRect.isEmpty()) {
809 mBounds.getBounds(mDirtyRect);
810 }
811 mDirtyRect.inset(- mHalfBorderWidth, - mHalfBorderWidth);
812 canvas = mSurface.lockCanvas(mDirtyRect);
813 if (DEBUG_VIEWPORT_WINDOW) {
814 Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect);
815 }
816 } catch (IllegalArgumentException iae) {
817 /* ignore */
818 } catch (Surface.OutOfResourcesException oore) {
819 /* ignore */
820 }
821 if (canvas == null) {
822 return;
823 }
824 if (DEBUG_VIEWPORT_WINDOW) {
825 Slog.i(LOG_TAG, "Bounds: " + mBounds);
826 }
827 canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
828 mPaint.setAlpha(mAlpha);
829 Path path = mBounds.getBoundaryPath();
830 canvas.drawPath(path, mPaint);
831
832 mSurface.unlockCanvasAndPost(canvas);
833
834 if (mAlpha > 0) {
835 mSurfaceControl.show();
836 } else {
837 mSurfaceControl.hide();
838 }
839 }
840 }
841
842 public void releaseSurface() {
843 mSurfaceControl.release();
844 mSurface.release();
845 }
Svet Ganovb21df802014-09-01 19:06:33 -0700846
847 private final class AnimationController extends Handler {
848 private static final String PROPERTY_NAME_ALPHA = "alpha";
849
850 private static final int MIN_ALPHA = 0;
851 private static final int MAX_ALPHA = 255;
852
853 private static final int MSG_FRAME_SHOWN_STATE_CHANGED = 1;
854
855 private final ValueAnimator mShowHideFrameAnimator;
856
857 public AnimationController(Context context, Looper looper) {
858 super(looper);
859 mShowHideFrameAnimator = ObjectAnimator.ofInt(ViewportWindow.this,
860 PROPERTY_NAME_ALPHA, MIN_ALPHA, MAX_ALPHA);
861
862 Interpolator interpolator = new DecelerateInterpolator(2.5f);
863 final long longAnimationDuration = context.getResources().getInteger(
864 com.android.internal.R.integer.config_longAnimTime);
865
866 mShowHideFrameAnimator.setInterpolator(interpolator);
867 mShowHideFrameAnimator.setDuration(longAnimationDuration);
868 }
869
870 public void onFrameShownStateChanged(boolean shown, boolean animate) {
871 obtainMessage(MSG_FRAME_SHOWN_STATE_CHANGED,
872 shown ? 1 : 0, animate ? 1 : 0).sendToTarget();
873 }
874
875 @Override
876 public void handleMessage(Message message) {
877 switch (message.what) {
878 case MSG_FRAME_SHOWN_STATE_CHANGED: {
879 final boolean shown = message.arg1 == 1;
880 final boolean animate = message.arg2 == 1;
881
882 if (animate) {
883 if (mShowHideFrameAnimator.isRunning()) {
884 mShowHideFrameAnimator.reverse();
885 } else {
886 if (shown) {
887 mShowHideFrameAnimator.start();
888 } else {
889 mShowHideFrameAnimator.reverse();
890 }
891 }
892 } else {
893 mShowHideFrameAnimator.cancel();
894 if (shown) {
895 setAlpha(MAX_ALPHA);
896 } else {
897 setAlpha(MIN_ALPHA);
898 }
899 }
900 } break;
901 }
902 }
903 }
Svetoslav8e3feb12014-02-24 13:46:47 -0800904 }
905 }
906
907 private class MyHandler extends Handler {
Phil Weaver70439242016-03-10 15:15:49 -0800908 public static final int MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED = 1;
Svetoslav8e3feb12014-02-24 13:46:47 -0800909 public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2;
910 public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
911 public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
912 public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
913
914 public MyHandler(Looper looper) {
915 super(looper);
916 }
917
918 @Override
919 public void handleMessage(Message message) {
920 switch (message.what) {
Phil Weaver70439242016-03-10 15:15:49 -0800921 case MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED: {
Alan Viverette214fb682015-11-17 09:47:11 -0500922 final SomeArgs args = (SomeArgs) message.obj;
923 final Region magnifiedBounds = (Region) args.arg1;
Phil Weaver70439242016-03-10 15:15:49 -0800924 mCallbacks.onMagnificationRegionChanged(magnifiedBounds);
Alan Viverette214fb682015-11-17 09:47:11 -0500925 magnifiedBounds.recycle();
Svetoslav8e3feb12014-02-24 13:46:47 -0800926 } break;
927
928 case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: {
929 SomeArgs args = (SomeArgs) message.obj;
930 final int left = args.argi1;
931 final int top = args.argi2;
932 final int right = args.argi3;
933 final int bottom = args.argi4;
934 mCallbacks.onRectangleOnScreenRequested(left, top, right, bottom);
935 args.recycle();
936 } break;
937
938 case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: {
939 mCallbacks.onUserContextChanged();
940 } break;
941
942 case MESSAGE_NOTIFY_ROTATION_CHANGED: {
943 final int rotation = message.arg1;
944 mCallbacks.onRotationChanged(rotation);
945 } break;
946
947 case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
948 synchronized (mWindowManagerService.mWindowMap) {
Casey Burkhardt74922c62017-02-13 12:43:16 -0800949 if (mMagnifedViewport.isMagnifyingLocked()
950 || isForceShowingMagnifiableBoundsLocked()) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800951 mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true);
952 mWindowManagerService.scheduleAnimationLocked();
953 }
954 }
955 } break;
956 }
957 }
958 }
959 }
960
961 /**
962 * This class encapsulates the functionality related to computing the windows
963 * reported for accessibility purposes. These windows are all windows a sighted
964 * user can see on the screen.
965 */
966 private static final class WindowsForAccessibilityObserver {
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -0800967 private static final String LOG_TAG = TAG_WITH_CLASS_NAME ?
968 "WindowsForAccessibilityObserver" : TAG_WM;
Svetoslav8e3feb12014-02-24 13:46:47 -0800969
970 private static final boolean DEBUG = false;
971
972 private final SparseArray<WindowState> mTempWindowStates =
973 new SparseArray<WindowState>();
974
975 private final List<WindowInfo> mOldWindows = new ArrayList<WindowInfo>();
976
977 private final Set<IBinder> mTempBinderSet = new ArraySet<IBinder>();
978
979 private final RectF mTempRectF = new RectF();
980
981 private final Matrix mTempMatrix = new Matrix();
982
983 private final Point mTempPoint = new Point();
984
985 private final Rect mTempRect = new Rect();
986
987 private final Region mTempRegion = new Region();
988
989 private final Region mTempRegion1 = new Region();
990
991 private final Context mContext;
992
993 private final WindowManagerService mWindowManagerService;
994
995 private final Handler mHandler;
996
997 private final WindowsForAccessibilityCallback mCallback;
998
Svetoslavf7174e82014-06-12 11:29:35 -0700999 private final long mRecurringAccessibilityEventsIntervalMillis;
1000
Svetoslav8e3feb12014-02-24 13:46:47 -08001001 public WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
1002 WindowsForAccessibilityCallback callback) {
1003 mContext = windowManagerService.mContext;
1004 mWindowManagerService = windowManagerService;
1005 mCallback = callback;
1006 mHandler = new MyHandler(mWindowManagerService.mH.getLooper());
Svetoslavf7174e82014-06-12 11:29:35 -07001007 mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration
1008 .getSendRecurringAccessibilityEventsInterval();
Svetoslav8e3feb12014-02-24 13:46:47 -08001009 computeChangedWindows();
1010 }
1011
Svetoslav3a0d8782014-12-04 12:50:11 -08001012 public void performComputeChangedWindowsNotLocked() {
1013 mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS);
1014 computeChangedWindows();
1015 }
1016
Svetoslavf7174e82014-06-12 11:29:35 -07001017 public void scheduleComputeChangedWindowsLocked() {
Svetoslav3a0d8782014-12-04 12:50:11 -08001018 if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) {
Svetoslavf7174e82014-06-12 11:29:35 -07001019 mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS,
1020 mRecurringAccessibilityEventsIntervalMillis);
1021 }
1022 }
1023
Svetoslav8e3feb12014-02-24 13:46:47 -08001024 public void computeChangedWindows() {
1025 if (DEBUG) {
1026 Slog.i(LOG_TAG, "computeChangedWindows()");
1027 }
1028
Svetoslav3a0d8782014-12-04 12:50:11 -08001029 boolean windowsChanged = false;
1030 List<WindowInfo> windows = new ArrayList<WindowInfo>();
1031
Svetoslav8e3feb12014-02-24 13:46:47 -08001032 synchronized (mWindowManagerService.mWindowMap) {
Svetoslavf7174e82014-06-12 11:29:35 -07001033 // Do not send the windows if there is no current focus as
1034 // the window manager is still looking for where to put it.
1035 // We will do the work when we get a focus change callback.
1036 if (mWindowManagerService.mCurrentFocus == null) {
1037 return;
1038 }
1039
Svetoslav8e3feb12014-02-24 13:46:47 -08001040 WindowManager windowManager = (WindowManager)
1041 mContext.getSystemService(Context.WINDOW_SERVICE);
1042 windowManager.getDefaultDisplay().getRealSize(mTempPoint);
1043 final int screenWidth = mTempPoint.x;
1044 final int screenHeight = mTempPoint.y;
1045
1046 Region unaccountedSpace = mTempRegion;
1047 unaccountedSpace.set(0, 0, screenWidth, screenHeight);
1048
Wale Ogunwalef7cab102016-10-25 15:25:14 -07001049 final SparseArray<WindowState> visibleWindows = mTempWindowStates;
Svetoslav8e3feb12014-02-24 13:46:47 -08001050 populateVisibleWindowsOnScreenLocked(visibleWindows);
Svetoslav8e3feb12014-02-24 13:46:47 -08001051 Set<IBinder> addedWindows = mTempBinderSet;
1052 addedWindows.clear();
1053
Svetoslavf7174e82014-06-12 11:29:35 -07001054 boolean focusedWindowAdded = false;
1055
Svetoslav8e3feb12014-02-24 13:46:47 -08001056 final int visibleWindowCount = visibleWindows.size();
Allen Hairf20ac2c2016-02-11 17:42:59 -08001057 HashSet<Integer> skipRemainingWindowsForTasks = new HashSet<>();
Svetoslav8e3feb12014-02-24 13:46:47 -08001058 for (int i = visibleWindowCount - 1; i >= 0; i--) {
Alan Viverette9538eea2014-11-13 14:49:20 -08001059 final WindowState windowState = visibleWindows.valueAt(i);
Svetoslav8e3feb12014-02-24 13:46:47 -08001060 final int flags = windowState.mAttrs.flags;
Allen Hairf20ac2c2016-02-11 17:42:59 -08001061 final Task task = windowState.getTask();
1062
1063 // If the window is part of a task that we're finished with - ignore.
1064 if (task != null && skipRemainingWindowsForTasks.contains(task.mTaskId)) {
1065 continue;
1066 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001067
Svetoslav3a5c7212014-10-14 09:54:26 -07001068 // If the window is not touchable - ignore.
Svetoslavf7174e82014-06-12 11:29:35 -07001069 if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001070 continue;
1071 }
1072
Alan Viverette9538eea2014-11-13 14:49:20 -08001073 // Compute the bounds in the screen.
1074 final Rect boundsInScreen = mTempRect;
1075 computeWindowBoundsInScreen(windowState, boundsInScreen);
1076
Svetoslav8e3feb12014-02-24 13:46:47 -08001077 // If the window is completely covered by other windows - ignore.
1078 if (unaccountedSpace.quickReject(boundsInScreen)) {
1079 continue;
1080 }
1081
1082 // Add windows of certain types not covered by modal windows.
1083 if (isReportedWindowType(windowState.mAttrs.type)) {
1084 // Add the window to the ones to be reported.
Svetoslavf7174e82014-06-12 11:29:35 -07001085 WindowInfo window = obtainPopulatedWindowInfo(windowState, boundsInScreen);
Svetoslav8e3feb12014-02-24 13:46:47 -08001086 addedWindows.add(window.token);
Svetoslav8e3feb12014-02-24 13:46:47 -08001087 windows.add(window);
Svetoslavf7174e82014-06-12 11:29:35 -07001088 if (windowState.isFocused()) {
1089 focusedWindowAdded = true;
1090 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001091 }
1092
Alan Viveretted0c73f42014-11-18 10:25:04 -08001093 // Account for the space this window takes if the window
1094 // is not an accessibility overlay which does not change
1095 // the reported windows.
1096 if (windowState.mAttrs.type !=
1097 WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
1098 unaccountedSpace.op(boundsInScreen, unaccountedSpace,
1099 Region.Op.REVERSE_DIFFERENCE);
1100 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001101
Allen Hairf20ac2c2016-02-11 17:42:59 -08001102 // If a window is modal it prevents other windows from being touched
Svetoslav8e3feb12014-02-24 13:46:47 -08001103 if ((flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1104 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
Phil Weaverac9ad702016-07-27 18:03:10 -07001105 // Account for all space in the task, whether the windows in it are
1106 // touchable or not. The modal window blocks all touches from the task's
1107 // area.
1108 unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace,
1109 Region.Op.REVERSE_DIFFERENCE);
1110
Allen Hairf20ac2c2016-02-11 17:42:59 -08001111 if (task != null) {
1112 // If the window is associated with a particular task, we can skip the
1113 // rest of the windows for that task.
1114 skipRemainingWindowsForTasks.add(task.mTaskId);
1115 continue;
1116 } else {
1117 // If the window is not associated with a particular task, then it is
1118 // globally modal. In this case we can skip all remaining windows.
1119 break;
1120 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001121 }
Phil Weaverac9ad702016-07-27 18:03:10 -07001122 // We figured out what is touchable for the entire screen - done.
1123 if (unaccountedSpace.isEmpty()) {
1124 break;
1125 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001126 }
1127
Svetoslavf7174e82014-06-12 11:29:35 -07001128 // Always report the focused window.
1129 if (!focusedWindowAdded) {
1130 for (int i = visibleWindowCount - 1; i >= 0; i--) {
1131 WindowState windowState = visibleWindows.valueAt(i);
1132 if (windowState.isFocused()) {
1133 // Compute the bounds in the screen.
1134 Rect boundsInScreen = mTempRect;
1135 computeWindowBoundsInScreen(windowState, boundsInScreen);
1136
1137 // Add the window to the ones to be reported.
1138 WindowInfo window = obtainPopulatedWindowInfo(windowState,
1139 boundsInScreen);
1140 addedWindows.add(window.token);
1141 windows.add(window);
1142 break;
1143 }
1144 }
1145 }
1146
Svetoslav8e3feb12014-02-24 13:46:47 -08001147 // Remove child/parent references to windows that were not added.
1148 final int windowCount = windows.size();
1149 for (int i = 0; i < windowCount; i++) {
1150 WindowInfo window = windows.get(i);
1151 if (!addedWindows.contains(window.parentToken)) {
1152 window.parentToken = null;
1153 }
1154 if (window.childTokens != null) {
1155 final int childTokenCount = window.childTokens.size();
1156 for (int j = childTokenCount - 1; j >= 0; j--) {
1157 if (!addedWindows.contains(window.childTokens.get(j))) {
1158 window.childTokens.remove(j);
1159 }
1160 }
1161 // Leave the child token list if empty.
1162 }
1163 }
1164
1165 visibleWindows.clear();
1166 addedWindows.clear();
1167
1168 // We computed the windows and if they changed notify the client.
Svetoslav8e3feb12014-02-24 13:46:47 -08001169 if (mOldWindows.size() != windows.size()) {
1170 // Different size means something changed.
1171 windowsChanged = true;
1172 } else if (!mOldWindows.isEmpty() || !windows.isEmpty()) {
1173 // Since we always traverse windows from high to low layer
1174 // the old and new windows at the same index should be the
1175 // same, otherwise something changed.
1176 for (int i = 0; i < windowCount; i++) {
1177 WindowInfo oldWindow = mOldWindows.get(i);
1178 WindowInfo newWindow = windows.get(i);
1179 // We do not care for layer changes given the window
1180 // order does not change. This brings no new information
1181 // to the clients.
1182 if (windowChangedNoLayer(oldWindow, newWindow)) {
1183 windowsChanged = true;
1184 break;
1185 }
1186 }
1187 }
1188
1189 if (windowsChanged) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001190 cacheWindows(windows);
Svetoslav8e3feb12014-02-24 13:46:47 -08001191 }
1192 }
Svetoslav3a0d8782014-12-04 12:50:11 -08001193
1194 // Now we do not hold the lock, so send the windows over.
1195 if (windowsChanged) {
1196 if (DEBUG) {
1197 Log.i(LOG_TAG, "Windows changed:" + windows);
1198 }
1199 mCallback.onWindowsForAccessibilityChanged(windows);
1200 } else {
1201 if (DEBUG) {
1202 Log.i(LOG_TAG, "No windows changed.");
1203 }
1204 }
1205
1206 // Recycle the windows as we do not need them.
1207 clearAndRecycleWindows(windows);
Svetoslav8e3feb12014-02-24 13:46:47 -08001208 }
1209
Svetoslavf7174e82014-06-12 11:29:35 -07001210 private void computeWindowBoundsInScreen(WindowState windowState, Rect outBounds) {
1211 // Get the touchable frame.
1212 Region touchableRegion = mTempRegion1;
1213 windowState.getTouchableRegion(touchableRegion);
1214 Rect touchableFrame = mTempRect;
1215 touchableRegion.getBounds(touchableFrame);
1216
1217 // Move to origin as all transforms are captured by the matrix.
1218 RectF windowFrame = mTempRectF;
1219 windowFrame.set(touchableFrame);
1220 windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
1221
1222 // Map the frame to get what appears on the screen.
1223 Matrix matrix = mTempMatrix;
1224 populateTransformationMatrixLocked(windowState, matrix);
1225 matrix.mapRect(windowFrame);
1226
1227 // Got the bounds.
1228 outBounds.set((int) windowFrame.left, (int) windowFrame.top,
1229 (int) windowFrame.right, (int) windowFrame.bottom);
1230 }
1231
Wale Ogunwaleadde52e2016-07-16 13:11:55 -07001232 private static WindowInfo obtainPopulatedWindowInfo(
1233 WindowState windowState, Rect boundsInScreen) {
1234 final WindowInfo window = windowState.getWindowInfo();
Svetoslavf7174e82014-06-12 11:29:35 -07001235 window.boundsInScreen.set(boundsInScreen);
Svetoslavf7174e82014-06-12 11:29:35 -07001236 return window;
1237 }
1238
Svetoslav8e3feb12014-02-24 13:46:47 -08001239 private void cacheWindows(List<WindowInfo> windows) {
1240 final int oldWindowCount = mOldWindows.size();
1241 for (int i = oldWindowCount - 1; i >= 0; i--) {
1242 mOldWindows.remove(i).recycle();
1243 }
1244 final int newWindowCount = windows.size();
1245 for (int i = 0; i < newWindowCount; i++) {
1246 WindowInfo newWindow = windows.get(i);
1247 mOldWindows.add(WindowInfo.obtain(newWindow));
1248 }
1249 }
1250
1251 private boolean windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow) {
1252 if (oldWindow == newWindow) {
1253 return false;
1254 }
Svetoslavf7174e82014-06-12 11:29:35 -07001255 if (oldWindow == null) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001256 return true;
1257 }
Svetoslavf7174e82014-06-12 11:29:35 -07001258 if (newWindow == null) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001259 return true;
1260 }
1261 if (oldWindow.type != newWindow.type) {
1262 return true;
1263 }
1264 if (oldWindow.focused != newWindow.focused) {
1265 return true;
1266 }
1267 if (oldWindow.token == null) {
1268 if (newWindow.token != null) {
1269 return true;
1270 }
1271 } else if (!oldWindow.token.equals(newWindow.token)) {
1272 return true;
1273 }
1274 if (oldWindow.parentToken == null) {
1275 if (newWindow.parentToken != null) {
1276 return true;
1277 }
1278 } else if (!oldWindow.parentToken.equals(newWindow.parentToken)) {
1279 return true;
1280 }
1281 if (!oldWindow.boundsInScreen.equals(newWindow.boundsInScreen)) {
1282 return true;
1283 }
1284 if (oldWindow.childTokens != null && newWindow.childTokens != null
1285 && !oldWindow.childTokens.equals(newWindow.childTokens)) {
1286 return true;
1287 }
Phil Weaver396d5492016-03-22 17:53:50 -07001288 if (!TextUtils.equals(oldWindow.title, newWindow.title)) {
1289 return true;
1290 }
1291 if (oldWindow.accessibilityIdOfAnchor != newWindow.accessibilityIdOfAnchor) {
1292 return true;
1293 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001294 return false;
1295 }
1296
Svetoslav3a0d8782014-12-04 12:50:11 -08001297 private static void clearAndRecycleWindows(List<WindowInfo> windows) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001298 final int windowCount = windows.size();
1299 for (int i = windowCount - 1; i >= 0; i--) {
1300 windows.remove(i).recycle();
1301 }
1302 }
1303
1304 private static boolean isReportedWindowType(int windowType) {
Jorim Jaggi73294b62016-10-26 18:02:36 -07001305 return (windowType != WindowManager.LayoutParams.TYPE_WALLPAPER
Svetoslav8e3feb12014-02-24 13:46:47 -08001306 && windowType != WindowManager.LayoutParams.TYPE_BOOT_PROGRESS
1307 && windowType != WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY
1308 && windowType != WindowManager.LayoutParams.TYPE_DRAG
Selim Cinekf83e8242015-05-19 18:08:14 -07001309 && windowType != WindowManager.LayoutParams.TYPE_INPUT_CONSUMER
Svetoslav8e3feb12014-02-24 13:46:47 -08001310 && windowType != WindowManager.LayoutParams.TYPE_POINTER
Svetoslav8e3feb12014-02-24 13:46:47 -08001311 && windowType != WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY
1312 && windowType != WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
1313 && windowType != WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY
1314 && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
1315 }
1316
1317 private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
Wale Ogunwalef7cab102016-10-25 15:25:14 -07001318 final DisplayContent dc = mWindowManagerService.getDefaultDisplayContentLocked();
Wale Ogunwaled1880962016-11-08 10:31:59 -08001319 dc.forAllWindows((w) -> {
1320 if (w.isVisibleLw()) {
1321 outWindows.put(w.mLayer, w);
Svetoslav8e3feb12014-02-24 13:46:47 -08001322 }
Wale Ogunwaled1880962016-11-08 10:31:59 -08001323 }, false /* traverseTopToBottom */ );
Svetoslav8e3feb12014-02-24 13:46:47 -08001324 }
1325
1326 private class MyHandler extends Handler {
Svetoslavf7174e82014-06-12 11:29:35 -07001327 public static final int MESSAGE_COMPUTE_CHANGED_WINDOWS = 1;
Svetoslav8e3feb12014-02-24 13:46:47 -08001328
1329 public MyHandler(Looper looper) {
1330 super(looper, null, false);
1331 }
1332
1333 @Override
1334 @SuppressWarnings("unchecked")
1335 public void handleMessage(Message message) {
1336 switch (message.what) {
Svetoslavf7174e82014-06-12 11:29:35 -07001337 case MESSAGE_COMPUTE_CHANGED_WINDOWS: {
1338 computeChangedWindows();
1339 } break;
Svetoslav8e3feb12014-02-24 13:46:47 -08001340 }
1341 }
1342 }
1343 }
1344}