blob: 88d1e5560bcc0581955f3e67c9b5762a13dd3a3b [file] [log] [blame]
Svetoslav8e3feb12014-02-24 13:46:47 -08001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wm;
18
Phil Weaverd321075e2017-06-13 09:13:35 -070019import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
Robert Carr132c9f52017-07-31 17:02:30 -070020import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
Phil Weaverd321075e2017-06-13 09:13:35 -070021
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -080022import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
23import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
24
Svetoslav8e3feb12014-02-24 13:46:47 -080025import android.animation.ObjectAnimator;
26import android.animation.ValueAnimator;
Alan Viverette59e53a12016-03-28 13:41:32 -040027import android.annotation.NonNull;
Svetoslav8e3feb12014-02-24 13:46:47 -080028import android.app.Service;
29import android.content.Context;
30import android.graphics.Canvas;
31import android.graphics.Color;
32import android.graphics.Matrix;
33import android.graphics.Paint;
34import android.graphics.Path;
35import android.graphics.PixelFormat;
36import android.graphics.Point;
37import android.graphics.PorterDuff.Mode;
38import android.graphics.Rect;
39import android.graphics.RectF;
40import android.graphics.Region;
41import android.os.Handler;
42import android.os.IBinder;
43import android.os.Looper;
44import android.os.Message;
Phil Weaver396d5492016-03-22 17:53:50 -070045import android.text.TextUtils;
Svetoslav8e3feb12014-02-24 13:46:47 -080046import android.util.ArraySet;
47import android.util.Log;
48import android.util.Slog;
49import android.util.SparseArray;
50import android.util.TypedValue;
51import android.view.MagnificationSpec;
52import android.view.Surface;
53import android.view.Surface.OutOfResourcesException;
54import android.view.SurfaceControl;
Svetoslavf7174e82014-06-12 11:29:35 -070055import android.view.ViewConfiguration;
Svetoslav8e3feb12014-02-24 13:46:47 -080056import android.view.WindowInfo;
57import android.view.WindowManager;
58import android.view.WindowManagerInternal.MagnificationCallbacks;
59import android.view.WindowManagerInternal.WindowsForAccessibilityCallback;
60import android.view.WindowManagerPolicy;
61import android.view.animation.DecelerateInterpolator;
62import android.view.animation.Interpolator;
63
64import com.android.internal.R;
65import com.android.internal.os.SomeArgs;
66
67import java.util.ArrayList;
Allen Hairf20ac2c2016-02-11 17:42:59 -080068import java.util.HashSet;
Svetoslav8e3feb12014-02-24 13:46:47 -080069import java.util.List;
70import java.util.Set;
71
72/**
73 * This class contains the accessibility related logic of the window manger.
74 */
75final class AccessibilityController {
76
Robert Carre625fcf2017-09-01 12:36:28 -070077 private final WindowManagerService mService;
Svetoslav8e3feb12014-02-24 13:46:47 -080078
79 private static final float[] sTempFloats = new float[9];
80
81 public AccessibilityController(WindowManagerService service) {
Robert Carre625fcf2017-09-01 12:36:28 -070082 mService = service;
Svetoslav8e3feb12014-02-24 13:46:47 -080083 }
84
85 private DisplayMagnifier mDisplayMagnifier;
86
87 private WindowsForAccessibilityObserver mWindowsForAccessibilityObserver;
88
89 public void setMagnificationCallbacksLocked(MagnificationCallbacks callbacks) {
90 if (callbacks != null) {
91 if (mDisplayMagnifier != null) {
92 throw new IllegalStateException("Magnification callbacks already set!");
93 }
Robert Carre625fcf2017-09-01 12:36:28 -070094 mDisplayMagnifier = new DisplayMagnifier(mService, callbacks);
Svetoslav8e3feb12014-02-24 13:46:47 -080095 } else {
96 if (mDisplayMagnifier == null) {
97 throw new IllegalStateException("Magnification callbacks already cleared!");
98 }
99 mDisplayMagnifier.destroyLocked();
100 mDisplayMagnifier = null;
101 }
102 }
103
104 public void setWindowsForAccessibilityCallback(WindowsForAccessibilityCallback callback) {
105 if (callback != null) {
106 if (mWindowsForAccessibilityObserver != null) {
107 throw new IllegalStateException(
108 "Windows for accessibility callback already set!");
109 }
110 mWindowsForAccessibilityObserver = new WindowsForAccessibilityObserver(
Robert Carre625fcf2017-09-01 12:36:28 -0700111 mService, callback);
Svetoslav8e3feb12014-02-24 13:46:47 -0800112 } else {
113 if (mWindowsForAccessibilityObserver == null) {
114 throw new IllegalStateException(
115 "Windows for accessibility callback already cleared!");
116 }
117 mWindowsForAccessibilityObserver = null;
118 }
119 }
120
Svetoslav Ganovfd138892016-07-13 18:20:42 -0700121 public void performComputeChangedWindowsNotLocked() {
122 WindowsForAccessibilityObserver observer = null;
Robert Carre625fcf2017-09-01 12:36:28 -0700123 synchronized (mService) {
Svetoslav Ganovfd138892016-07-13 18:20:42 -0700124 observer = mWindowsForAccessibilityObserver;
125 }
126 if (observer != null) {
127 observer.performComputeChangedWindowsNotLocked();
128 }
129 }
130
Svetoslav8e3feb12014-02-24 13:46:47 -0800131 public void setMagnificationSpecLocked(MagnificationSpec spec) {
132 if (mDisplayMagnifier != null) {
133 mDisplayMagnifier.setMagnificationSpecLocked(spec);
134 }
135 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700136 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800137 }
138 }
139
Phil Weaver70439242016-03-10 15:15:49 -0800140 public void getMagnificationRegionLocked(Region outMagnificationRegion) {
Alan Viverette59e53a12016-03-28 13:41:32 -0400141 if (mDisplayMagnifier != null) {
Phil Weaver70439242016-03-10 15:15:49 -0800142 mDisplayMagnifier.getMagnificationRegionLocked(outMagnificationRegion);
Alan Viverette59e53a12016-03-28 13:41:32 -0400143 }
144 }
145
Svetoslavf7174e82014-06-12 11:29:35 -0700146 public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800147 if (mDisplayMagnifier != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700148 mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle);
Svetoslav8e3feb12014-02-24 13:46:47 -0800149 }
150 // Not relevant for the window observer.
151 }
152
153 public void onWindowLayersChangedLocked() {
154 if (mDisplayMagnifier != null) {
155 mDisplayMagnifier.onWindowLayersChangedLocked();
156 }
157 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700158 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800159 }
160 }
161
Andrii Kulian8ee72852017-03-10 10:36:45 -0800162 public void onRotationChangedLocked(DisplayContent displayContent) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800163 if (mDisplayMagnifier != null) {
Andrii Kulian8ee72852017-03-10 10:36:45 -0800164 mDisplayMagnifier.onRotationChangedLocked(displayContent);
Svetoslav8e3feb12014-02-24 13:46:47 -0800165 }
166 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700167 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800168 }
169 }
170
171 public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
172 if (mDisplayMagnifier != null) {
173 mDisplayMagnifier.onAppWindowTransitionLocked(windowState, transition);
174 }
175 // Not relevant for the window observer.
176 }
177
178 public void onWindowTransitionLocked(WindowState windowState, int transition) {
179 if (mDisplayMagnifier != null) {
180 mDisplayMagnifier.onWindowTransitionLocked(windowState, transition);
181 }
182 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700183 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800184 }
185 }
186
Svetoslav3a0d8782014-12-04 12:50:11 -0800187 public void onWindowFocusChangedNotLocked() {
Svetoslav8e3feb12014-02-24 13:46:47 -0800188 // Not relevant for the display magnifier.
189
Svetoslav3a0d8782014-12-04 12:50:11 -0800190 WindowsForAccessibilityObserver observer = null;
Robert Carre625fcf2017-09-01 12:36:28 -0700191 synchronized (mService) {
Svetoslav3a0d8782014-12-04 12:50:11 -0800192 observer = mWindowsForAccessibilityObserver;
193 }
194 if (observer != null) {
195 observer.performComputeChangedWindowsNotLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800196 }
197 }
198
Svetoslav4604abc2014-06-10 18:59:30 -0700199
Svetoslavf7174e82014-06-12 11:29:35 -0700200 public void onSomeWindowResizedOrMovedLocked() {
Svetoslav4604abc2014-06-10 18:59:30 -0700201 // Not relevant for the display magnifier.
202
203 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700204 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav4604abc2014-06-10 18:59:30 -0700205 }
206 }
207
Svetoslav8e3feb12014-02-24 13:46:47 -0800208 /** NOTE: This has to be called within a surface transaction. */
209 public void drawMagnifiedRegionBorderIfNeededLocked() {
210 if (mDisplayMagnifier != null) {
211 mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked();
212 }
213 // Not relevant for the window observer.
214 }
215
216 public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
217 if (mDisplayMagnifier != null) {
218 return mDisplayMagnifier.getMagnificationSpecForWindowLocked(windowState);
219 }
220 return null;
221 }
222
223 public boolean hasCallbacksLocked() {
224 return (mDisplayMagnifier != null
225 || mWindowsForAccessibilityObserver != null);
226 }
227
Casey Burkhardt74922c62017-02-13 12:43:16 -0800228 public void setForceShowMagnifiableBoundsLocked(boolean show) {
229 if (mDisplayMagnifier != null) {
230 mDisplayMagnifier.setForceShowMagnifiableBoundsLocked(show);
Phil Weaver251db072017-03-28 08:35:38 -0700231 mDisplayMagnifier.showMagnificationBoundsIfNeeded();
Casey Burkhardt74922c62017-02-13 12:43:16 -0800232 }
233 }
234
Svetoslav8e3feb12014-02-24 13:46:47 -0800235 private static void populateTransformationMatrixLocked(WindowState windowState,
236 Matrix outMatrix) {
237 sTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx;
238 sTempFloats[Matrix.MSKEW_Y] = windowState.mWinAnimator.mDtDx;
Robert Carr0edf18f2017-02-21 20:01:47 -0800239 sTempFloats[Matrix.MSKEW_X] = windowState.mWinAnimator.mDtDy;
240 sTempFloats[Matrix.MSCALE_Y] = windowState.mWinAnimator.mDsDy;
Filip Gruszczynski2a6a2c22015-10-14 12:00:53 -0700241 sTempFloats[Matrix.MTRANS_X] = windowState.mShownPosition.x;
242 sTempFloats[Matrix.MTRANS_Y] = windowState.mShownPosition.y;
Svetoslav8e3feb12014-02-24 13:46:47 -0800243 sTempFloats[Matrix.MPERSP_0] = 0;
244 sTempFloats[Matrix.MPERSP_1] = 0;
245 sTempFloats[Matrix.MPERSP_2] = 1;
246 outMatrix.setValues(sTempFloats);
247 }
248
249 /**
250 * This class encapsulates the functionality related to display magnification.
251 */
252 private static final class DisplayMagnifier {
253
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -0800254 private static final String LOG_TAG = TAG_WITH_CLASS_NAME ? "DisplayMagnifier" : TAG_WM;
Svetoslav8e3feb12014-02-24 13:46:47 -0800255
256 private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
257 private static final boolean DEBUG_ROTATION = false;
258 private static final boolean DEBUG_LAYERS = false;
259 private static final boolean DEBUG_RECTANGLE_REQUESTED = false;
260 private static final boolean DEBUG_VIEWPORT_WINDOW = false;
261
262 private final Rect mTempRect1 = new Rect();
263 private final Rect mTempRect2 = new Rect();
264
265 private final Region mTempRegion1 = new Region();
266 private final Region mTempRegion2 = new Region();
267 private final Region mTempRegion3 = new Region();
268 private final Region mTempRegion4 = new Region();
269
270 private final Context mContext;
Robert Carre625fcf2017-09-01 12:36:28 -0700271 private final WindowManagerService mService;
Svetoslav8e3feb12014-02-24 13:46:47 -0800272 private final MagnifiedViewport mMagnifedViewport;
273 private final Handler mHandler;
274
275 private final MagnificationCallbacks mCallbacks;
276
277 private final long mLongAnimationDuration;
278
Casey Burkhardt74922c62017-02-13 12:43:16 -0800279 private boolean mForceShowMagnifiableBounds = false;
280
Svetoslav8e3feb12014-02-24 13:46:47 -0800281 public DisplayMagnifier(WindowManagerService windowManagerService,
282 MagnificationCallbacks callbacks) {
283 mContext = windowManagerService.mContext;
Robert Carre625fcf2017-09-01 12:36:28 -0700284 mService = windowManagerService;
Svetoslav8e3feb12014-02-24 13:46:47 -0800285 mCallbacks = callbacks;
Robert Carre625fcf2017-09-01 12:36:28 -0700286 mHandler = new MyHandler(mService.mH.getLooper());
Svetoslav8e3feb12014-02-24 13:46:47 -0800287 mMagnifedViewport = new MagnifiedViewport();
288 mLongAnimationDuration = mContext.getResources().getInteger(
289 com.android.internal.R.integer.config_longAnimTime);
290 }
291
292 public void setMagnificationSpecLocked(MagnificationSpec spec) {
293 mMagnifedViewport.updateMagnificationSpecLocked(spec);
294 mMagnifedViewport.recomputeBoundsLocked();
Robert Carrb1579c82017-09-05 14:54:47 -0700295
296 mService.applyMagnificationSpec(spec);
Robert Carre625fcf2017-09-01 12:36:28 -0700297 mService.scheduleAnimationLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800298 }
299
Casey Burkhardt74922c62017-02-13 12:43:16 -0800300 public void setForceShowMagnifiableBoundsLocked(boolean show) {
301 mForceShowMagnifiableBounds = show;
302 mMagnifedViewport.setMagnifiedRegionBorderShownLocked(show, true);
303 }
304
305 public boolean isForceShowingMagnifiableBoundsLocked() {
306 return mForceShowMagnifiableBounds;
307 }
308
Svetoslavf7174e82014-06-12 11:29:35 -0700309 public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800310 if (DEBUG_RECTANGLE_REQUESTED) {
311 Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
312 }
313 if (!mMagnifedViewport.isMagnifyingLocked()) {
314 return;
315 }
316 Rect magnifiedRegionBounds = mTempRect2;
317 mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds);
318 if (magnifiedRegionBounds.contains(rectangle)) {
319 return;
320 }
321 SomeArgs args = SomeArgs.obtain();
322 args.argi1 = rectangle.left;
323 args.argi2 = rectangle.top;
324 args.argi3 = rectangle.right;
325 args.argi4 = rectangle.bottom;
326 mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED,
327 args).sendToTarget();
328 }
329
330 public void onWindowLayersChangedLocked() {
331 if (DEBUG_LAYERS) {
332 Slog.i(LOG_TAG, "Layers changed.");
333 }
334 mMagnifedViewport.recomputeBoundsLocked();
Robert Carre625fcf2017-09-01 12:36:28 -0700335 mService.scheduleAnimationLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800336 }
337
Andrii Kulian8ee72852017-03-10 10:36:45 -0800338 public void onRotationChangedLocked(DisplayContent displayContent) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800339 if (DEBUG_ROTATION) {
Andrii Kulian8ee72852017-03-10 10:36:45 -0800340 final int rotation = displayContent.getRotation();
341 Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation)
Svetoslav8e3feb12014-02-24 13:46:47 -0800342 + " displayId: " + displayContent.getDisplayId());
343 }
344 mMagnifedViewport.onRotationChangedLocked();
345 mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
346 }
347
348 public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
349 if (DEBUG_WINDOW_TRANSITIONS) {
350 Slog.i(LOG_TAG, "Window transition: "
351 + AppTransition.appTransitionToString(transition)
352 + " displayId: " + windowState.getDisplayId());
353 }
354 final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
355 if (magnifying) {
356 switch (transition) {
357 case AppTransition.TRANSIT_ACTIVITY_OPEN:
358 case AppTransition.TRANSIT_TASK_OPEN:
359 case AppTransition.TRANSIT_TASK_TO_FRONT:
360 case AppTransition.TRANSIT_WALLPAPER_OPEN:
361 case AppTransition.TRANSIT_WALLPAPER_CLOSE:
362 case AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN: {
363 mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED);
364 }
365 }
366 }
367 }
368
369 public void onWindowTransitionLocked(WindowState windowState, int transition) {
370 if (DEBUG_WINDOW_TRANSITIONS) {
371 Slog.i(LOG_TAG, "Window transition: "
372 + AppTransition.appTransitionToString(transition)
373 + " displayId: " + windowState.getDisplayId());
374 }
375 final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
376 final int type = windowState.mAttrs.type;
377 switch (transition) {
378 case WindowManagerPolicy.TRANSIT_ENTER:
379 case WindowManagerPolicy.TRANSIT_SHOW: {
380 if (!magnifying) {
381 break;
382 }
383 switch (type) {
384 case WindowManager.LayoutParams.TYPE_APPLICATION:
Chong Zhangfea963e2016-08-15 17:14:16 -0700385 case WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION:
Svetoslav8e3feb12014-02-24 13:46:47 -0800386 case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
387 case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
388 case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
Wale Ogunwale0a4dc222015-04-14 12:58:42 -0700389 case WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL:
Svetoslav8e3feb12014-02-24 13:46:47 -0800390 case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
391 case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
392 case WindowManager.LayoutParams.TYPE_PHONE:
393 case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
394 case WindowManager.LayoutParams.TYPE_TOAST:
395 case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
Wale Ogunwale5cd907d2017-01-26 14:14:08 -0800396 case WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY:
Svetoslav8e3feb12014-02-24 13:46:47 -0800397 case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
398 case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
399 case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
400 case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
401 case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
Jason Monk8f7f3182015-11-18 16:35:14 -0500402 case WindowManager.LayoutParams.TYPE_QS_DIALOG:
Adrian Roos9a645132014-10-08 02:59:56 +0200403 case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: {
Svetoslav8e3feb12014-02-24 13:46:47 -0800404 Rect magnifiedRegionBounds = mTempRect2;
405 mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(
406 magnifiedRegionBounds);
407 Rect touchableRegionBounds = mTempRect1;
408 windowState.getTouchableRegion(mTempRegion1);
409 mTempRegion1.getBounds(touchableRegionBounds);
410 if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) {
411 mCallbacks.onRectangleOnScreenRequested(
412 touchableRegionBounds.left,
413 touchableRegionBounds.top,
414 touchableRegionBounds.right,
415 touchableRegionBounds.bottom);
416 }
417 } break;
418 } break;
419 }
420 }
421 }
422
423 public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
424 MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
425 if (spec != null && !spec.isNop()) {
Robert Carrb1579c82017-09-05 14:54:47 -0700426 if (!windowState.shouldMagnify()) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800427 return null;
428 }
429 }
430 return spec;
431 }
432
Phil Weaver70439242016-03-10 15:15:49 -0800433 public void getMagnificationRegionLocked(Region outMagnificationRegion) {
Phil Weaver53b690b2017-08-14 17:42:39 -0700434 // Make sure we're working with the most current bounds
435 mMagnifedViewport.recomputeBoundsLocked();
Phil Weaver70439242016-03-10 15:15:49 -0800436 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
Phil Weaver251db072017-03-28 08:35:38 -0700443 // Can be called outside of a surface transaction
444 public void showMagnificationBoundsIfNeeded() {
445 mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)
446 .sendToTarget();
447 }
448
Svetoslav8e3feb12014-02-24 13:46:47 -0800449 /** NOTE: This has to be called within a surface transaction. */
450 public void drawMagnifiedRegionBorderIfNeededLocked() {
451 mMagnifedViewport.drawWindowIfNeededLocked();
452 }
453
454 private final class MagnifiedViewport {
455
Svetoslav8e3feb12014-02-24 13:46:47 -0800456 private final SparseArray<WindowState> mTempWindowStates =
457 new SparseArray<WindowState>();
458
459 private final RectF mTempRectF = new RectF();
460
461 private final Point mTempPoint = new Point();
462
463 private final Matrix mTempMatrix = new Matrix();
464
Phil Weaver70439242016-03-10 15:15:49 -0800465 private final Region mMagnificationRegion = new Region();
466 private final Region mOldMagnificationRegion = new Region();
Svetoslav8e3feb12014-02-24 13:46:47 -0800467
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800468 private final Path mCircularPath;
469
Svetoslav8e3feb12014-02-24 13:46:47 -0800470 private final MagnificationSpec mMagnificationSpec = MagnificationSpec.obtain();
471
472 private final WindowManager mWindowManager;
473
Svetoslav7505e332014-08-22 12:14:28 -0700474 private final float mBorderWidth;
Svetoslav8e3feb12014-02-24 13:46:47 -0800475 private final int mHalfBorderWidth;
Svetoslav7505e332014-08-22 12:14:28 -0700476 private final int mDrawBorderInset;
Svetoslav8e3feb12014-02-24 13:46:47 -0800477
478 private final ViewportWindow mWindow;
479
480 private boolean mFullRedrawNeeded;
Robert Carrb1579c82017-09-05 14:54:47 -0700481 private int mTempLayer = 0;
Svetoslav8e3feb12014-02-24 13:46:47 -0800482
483 public MagnifiedViewport() {
484 mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE);
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800485 mBorderWidth = mContext.getResources().getDimension(
486 com.android.internal.R.dimen.accessibility_magnification_indicator_width);
Svetoslav7505e332014-08-22 12:14:28 -0700487 mHalfBorderWidth = (int) Math.ceil(mBorderWidth / 2);
488 mDrawBorderInset = (int) mBorderWidth / 2;
Svetoslav8e3feb12014-02-24 13:46:47 -0800489 mWindow = new ViewportWindow(mContext);
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800490
Adam Powell01f280d2015-05-18 16:07:42 -0700491 if (mContext.getResources().getConfiguration().isScreenRound()) {
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800492 mCircularPath = new Path();
493 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
494 final int centerXY = mTempPoint.x / 2;
495 mCircularPath.addCircle(centerXY, centerXY, centerXY, Path.Direction.CW);
496 } else {
497 mCircularPath = null;
498 }
499
Svetoslav8e3feb12014-02-24 13:46:47 -0800500 recomputeBoundsLocked();
501 }
502
Phil Weaver70439242016-03-10 15:15:49 -0800503 public void getMagnificationRegionLocked(@NonNull Region outMagnificationRegion) {
504 outMagnificationRegion.set(mMagnificationRegion);
Alan Viverette59e53a12016-03-28 13:41:32 -0400505 }
506
Svetoslav8e3feb12014-02-24 13:46:47 -0800507 public void updateMagnificationSpecLocked(MagnificationSpec spec) {
508 if (spec != null) {
509 mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY);
510 } else {
511 mMagnificationSpec.clear();
512 }
513 // If this message is pending we are in a rotation animation and do not want
514 // to show the border. We will do so when the pending message is handled.
Svetoslav7505e332014-08-22 12:14:28 -0700515 if (!mHandler.hasMessages(
516 MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
Casey Burkhardt74922c62017-02-13 12:43:16 -0800517 setMagnifiedRegionBorderShownLocked(
518 isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked(), true);
Svetoslav8e3feb12014-02-24 13:46:47 -0800519 }
520 }
521
522 public void recomputeBoundsLocked() {
523 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
524 final int screenWidth = mTempPoint.x;
525 final int screenHeight = mTempPoint.y;
526
Phil Weaver70439242016-03-10 15:15:49 -0800527 mMagnificationRegion.set(0, 0, 0, 0);
528 final Region availableBounds = mTempRegion1;
529 availableBounds.set(0, 0, screenWidth, screenHeight);
Svetoslav8e3feb12014-02-24 13:46:47 -0800530
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800531 if (mCircularPath != null) {
Phil Weaver70439242016-03-10 15:15:49 -0800532 availableBounds.setPath(mCircularPath, availableBounds);
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800533 }
534
Svetoslav8e3feb12014-02-24 13:46:47 -0800535 Region nonMagnifiedBounds = mTempRegion4;
Svet Ganovb21df802014-09-01 19:06:33 -0700536 nonMagnifiedBounds.set(0, 0, 0, 0);
Svetoslav8e3feb12014-02-24 13:46:47 -0800537
538 SparseArray<WindowState> visibleWindows = mTempWindowStates;
539 visibleWindows.clear();
540 populateWindowsOnScreenLocked(visibleWindows);
541
542 final int visibleWindowCount = visibleWindows.size();
543 for (int i = visibleWindowCount - 1; i >= 0; i--) {
544 WindowState windowState = visibleWindows.valueAt(i);
Phil Weaverd321075e2017-06-13 09:13:35 -0700545 if ((windowState.mAttrs.type == TYPE_MAGNIFICATION_OVERLAY)
546 || ((windowState.mAttrs.privateFlags
Robert Carr132c9f52017-07-31 17:02:30 -0700547 & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0)) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800548 continue;
549 }
550
Phil Weaver65c06702016-03-15 15:33:46 -0700551 // Consider the touchable portion of the window
Svetoslav8e3feb12014-02-24 13:46:47 -0800552 Matrix matrix = mTempMatrix;
553 populateTransformationMatrixLocked(windowState, matrix);
Phil Weaver65c06702016-03-15 15:33:46 -0700554 Region touchableRegion = mTempRegion3;
555 windowState.getTouchableRegion(touchableRegion);
556 Rect touchableFrame = mTempRect1;
557 touchableRegion.getBounds(touchableFrame);
Svetoslav8e3feb12014-02-24 13:46:47 -0800558 RectF windowFrame = mTempRectF;
Phil Weaver65c06702016-03-15 15:33:46 -0700559 windowFrame.set(touchableFrame);
560 windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
561 matrix.mapRect(windowFrame);
562 Region windowBounds = mTempRegion2;
563 windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
564 (int) windowFrame.right, (int) windowFrame.bottom);
565 // Only update new regions
566 Region portionOfWindowAlreadyAccountedFor = mTempRegion3;
Phil Weaver70439242016-03-10 15:15:49 -0800567 portionOfWindowAlreadyAccountedFor.set(mMagnificationRegion);
Phil Weaver65c06702016-03-15 15:33:46 -0700568 portionOfWindowAlreadyAccountedFor.op(nonMagnifiedBounds, Region.Op.UNION);
569 windowBounds.op(portionOfWindowAlreadyAccountedFor, Region.Op.DIFFERENCE);
Svetoslav8e3feb12014-02-24 13:46:47 -0800570
Robert Carrb1579c82017-09-05 14:54:47 -0700571 if (windowState.shouldMagnify()) {
Phil Weaver70439242016-03-10 15:15:49 -0800572 mMagnificationRegion.op(windowBounds, Region.Op.UNION);
573 mMagnificationRegion.op(availableBounds, Region.Op.INTERSECT);
Svetoslav8e3feb12014-02-24 13:46:47 -0800574 } else {
Svetoslav8e3feb12014-02-24 13:46:47 -0800575 nonMagnifiedBounds.op(windowBounds, Region.Op.UNION);
Phil Weaver70439242016-03-10 15:15:49 -0800576 availableBounds.op(windowBounds, Region.Op.DIFFERENCE);
Svetoslav8e3feb12014-02-24 13:46:47 -0800577 }
578
Phil Weaver65c06702016-03-15 15:33:46 -0700579 // Update accounted bounds
Svetoslav8e3feb12014-02-24 13:46:47 -0800580 Region accountedBounds = mTempRegion2;
Phil Weaver70439242016-03-10 15:15:49 -0800581 accountedBounds.set(mMagnificationRegion);
Svetoslav8e3feb12014-02-24 13:46:47 -0800582 accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION);
583 accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT);
584
585 if (accountedBounds.isRect()) {
586 Rect accountedFrame = mTempRect1;
587 accountedBounds.getBounds(accountedFrame);
588 if (accountedFrame.width() == screenWidth
589 && accountedFrame.height() == screenHeight) {
590 break;
591 }
592 }
593 }
594
595 visibleWindows.clear();
596
Phil Weaver70439242016-03-10 15:15:49 -0800597 mMagnificationRegion.op(mDrawBorderInset, mDrawBorderInset,
Svetoslav7505e332014-08-22 12:14:28 -0700598 screenWidth - mDrawBorderInset, screenHeight - mDrawBorderInset,
Svetoslav8e3feb12014-02-24 13:46:47 -0800599 Region.Op.INTERSECT);
600
Phil Weaver70439242016-03-10 15:15:49 -0800601 final boolean magnifiedChanged =
602 !mOldMagnificationRegion.equals(mMagnificationRegion);
603 if (magnifiedChanged) {
604 mWindow.setBounds(mMagnificationRegion);
605 final Rect dirtyRect = mTempRect1;
606 if (mFullRedrawNeeded) {
607 mFullRedrawNeeded = false;
608 dirtyRect.set(mDrawBorderInset, mDrawBorderInset,
609 screenWidth - mDrawBorderInset,
610 screenHeight - mDrawBorderInset);
611 mWindow.invalidate(dirtyRect);
612 } else {
613 final Region dirtyRegion = mTempRegion3;
614 dirtyRegion.set(mMagnificationRegion);
615 dirtyRegion.op(mOldMagnificationRegion, Region.Op.UNION);
616 dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT);
617 dirtyRegion.getBounds(dirtyRect);
618 mWindow.invalidate(dirtyRect);
Svetoslav8e3feb12014-02-24 13:46:47 -0800619 }
620
Phil Weaver70439242016-03-10 15:15:49 -0800621 mOldMagnificationRegion.set(mMagnificationRegion);
Alan Viverette214fb682015-11-17 09:47:11 -0500622 final SomeArgs args = SomeArgs.obtain();
Phil Weaver70439242016-03-10 15:15:49 -0800623 args.arg1 = Region.obtain(mMagnificationRegion);
Alan Viverette214fb682015-11-17 09:47:11 -0500624 mHandler.obtainMessage(
Phil Weaver70439242016-03-10 15:15:49 -0800625 MyHandler.MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED, args)
626 .sendToTarget();
Svetoslav8e3feb12014-02-24 13:46:47 -0800627 }
628 }
629
630 public void onRotationChangedLocked() {
Casey Burkhardt74922c62017-02-13 12:43:16 -0800631 // If we are showing the magnification border, hide it immediately so
Svetoslav8e3feb12014-02-24 13:46:47 -0800632 // the user does not see strange artifacts during rotation. The screenshot
Casey Burkhardt74922c62017-02-13 12:43:16 -0800633 // used for rotation already has the border. After the rotation is complete
Svetoslav8e3feb12014-02-24 13:46:47 -0800634 // we will show the border.
Casey Burkhardt74922c62017-02-13 12:43:16 -0800635 if (isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked()) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800636 setMagnifiedRegionBorderShownLocked(false, false);
637 final long delay = (long) (mLongAnimationDuration
Robert Carre625fcf2017-09-01 12:36:28 -0700638 * mService.getWindowAnimationScaleLocked());
Svetoslav8e3feb12014-02-24 13:46:47 -0800639 Message message = mHandler.obtainMessage(
640 MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED);
641 mHandler.sendMessageDelayed(message, delay);
642 }
643 recomputeBoundsLocked();
644 mWindow.updateSize();
645 }
646
647 public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) {
648 if (shown) {
649 mFullRedrawNeeded = true;
Phil Weaver70439242016-03-10 15:15:49 -0800650 mOldMagnificationRegion.set(0, 0, 0, 0);
Svetoslav8e3feb12014-02-24 13:46:47 -0800651 }
652 mWindow.setShown(shown, animate);
653 }
654
655 public void getMagnifiedFrameInContentCoordsLocked(Rect rect) {
656 MagnificationSpec spec = mMagnificationSpec;
Phil Weaver70439242016-03-10 15:15:49 -0800657 mMagnificationRegion.getBounds(rect);
Svetoslav8e3feb12014-02-24 13:46:47 -0800658 rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
659 rect.scale(1.0f / spec.scale);
660 }
661
662 public boolean isMagnifyingLocked() {
663 return mMagnificationSpec.scale > 1.0f;
664 }
665
666 public MagnificationSpec getMagnificationSpecLocked() {
667 return mMagnificationSpec;
668 }
669
670 /** NOTE: This has to be called within a surface transaction. */
671 public void drawWindowIfNeededLocked() {
672 recomputeBoundsLocked();
673 mWindow.drawIfNeeded();
674 }
675
676 public void destroyWindow() {
677 mWindow.releaseSurface();
678 }
679
680 private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
Robert Carre625fcf2017-09-01 12:36:28 -0700681 final DisplayContent dc = mService.getDefaultDisplayContentLocked();
Robert Carrb1579c82017-09-05 14:54:47 -0700682 mTempLayer = 0;
Wale Ogunwaled1880962016-11-08 10:31:59 -0800683 dc.forAllWindows((w) -> {
684 if (w.isOnScreen() && w.isVisibleLw()
685 && !w.mWinAnimator.mEnterAnimationPending) {
Robert Carrb1579c82017-09-05 14:54:47 -0700686 mTempLayer++;
687 outWindows.put(mTempLayer, w);
Svetoslav8e3feb12014-02-24 13:46:47 -0800688 }
Wale Ogunwaled1880962016-11-08 10:31:59 -0800689 }, false /* traverseTopToBottom */ );
Svetoslav8e3feb12014-02-24 13:46:47 -0800690 }
691
692 private final class ViewportWindow {
693 private static final String SURFACE_TITLE = "Magnification Overlay";
694
Svetoslav8e3feb12014-02-24 13:46:47 -0800695 private final Region mBounds = new Region();
696 private final Rect mDirtyRect = new Rect();
697 private final Paint mPaint = new Paint();
698
Svetoslav8e3feb12014-02-24 13:46:47 -0800699 private final SurfaceControl mSurfaceControl;
700 private final Surface mSurface = new Surface();
701
Svet Ganovb21df802014-09-01 19:06:33 -0700702 private final AnimationController mAnimationController;
703
Svetoslav8e3feb12014-02-24 13:46:47 -0800704 private boolean mShown;
705 private int mAlpha;
706
707 private boolean mInvalidated;
708
709 public ViewportWindow(Context context) {
710 SurfaceControl surfaceControl = null;
711 try {
712 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
Robert Carrb1579c82017-09-05 14:54:47 -0700713 surfaceControl = mService.getDefaultDisplayContentLocked().makeOverlay()
Robert Carre625fcf2017-09-01 12:36:28 -0700714 .setName(SURFACE_TITLE)
715 .setSize(mTempPoint.x, mTempPoint.y) // not a typo
716 .setFormat(PixelFormat.TRANSLUCENT)
717 .build();
Svetoslav8e3feb12014-02-24 13:46:47 -0800718 } catch (OutOfResourcesException oore) {
719 /* ignore */
720 }
721 mSurfaceControl = surfaceControl;
Robert Carre625fcf2017-09-01 12:36:28 -0700722 mSurfaceControl.setLayer(mService.mPolicy.getWindowLayerFromTypeLw(
Phil Weaverd321075e2017-06-13 09:13:35 -0700723 TYPE_MAGNIFICATION_OVERLAY)
Svetoslav8e3feb12014-02-24 13:46:47 -0800724 * WindowManagerService.TYPE_LAYER_MULTIPLIER);
725 mSurfaceControl.setPosition(0, 0);
726 mSurface.copyFrom(mSurfaceControl);
727
Svet Ganovb21df802014-09-01 19:06:33 -0700728 mAnimationController = new AnimationController(context,
Robert Carre625fcf2017-09-01 12:36:28 -0700729 mService.mH.getLooper());
Svet Ganovb21df802014-09-01 19:06:33 -0700730
Svetoslav8e3feb12014-02-24 13:46:47 -0800731 TypedValue typedValue = new TypedValue();
732 context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight,
733 typedValue, true);
Alan Viverette4a357cd2015-03-18 18:37:18 -0700734 final int borderColor = context.getColor(typedValue.resourceId);
Svetoslav8e3feb12014-02-24 13:46:47 -0800735
736 mPaint.setStyle(Paint.Style.STROKE);
737 mPaint.setStrokeWidth(mBorderWidth);
738 mPaint.setColor(borderColor);
739
Svetoslav8e3feb12014-02-24 13:46:47 -0800740 mInvalidated = true;
741 }
742
743 public void setShown(boolean shown, boolean animate) {
Robert Carre625fcf2017-09-01 12:36:28 -0700744 synchronized (mService.mWindowMap) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800745 if (mShown == shown) {
746 return;
747 }
748 mShown = shown;
Svet Ganovb21df802014-09-01 19:06:33 -0700749 mAnimationController.onFrameShownStateChanged(shown, animate);
Svetoslav8e3feb12014-02-24 13:46:47 -0800750 if (DEBUG_VIEWPORT_WINDOW) {
751 Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown);
752 }
753 }
754 }
755
756 @SuppressWarnings("unused")
757 // Called reflectively from an animator.
758 public int getAlpha() {
Robert Carre625fcf2017-09-01 12:36:28 -0700759 synchronized (mService.mWindowMap) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800760 return mAlpha;
761 }
762 }
763
764 public void setAlpha(int alpha) {
Robert Carre625fcf2017-09-01 12:36:28 -0700765 synchronized (mService.mWindowMap) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800766 if (mAlpha == alpha) {
767 return;
768 }
769 mAlpha = alpha;
770 invalidate(null);
771 if (DEBUG_VIEWPORT_WINDOW) {
772 Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha);
773 }
774 }
775 }
776
777 public void setBounds(Region bounds) {
Robert Carre625fcf2017-09-01 12:36:28 -0700778 synchronized (mService.mWindowMap) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800779 if (mBounds.equals(bounds)) {
780 return;
781 }
782 mBounds.set(bounds);
783 invalidate(mDirtyRect);
784 if (DEBUG_VIEWPORT_WINDOW) {
785 Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds);
786 }
787 }
788 }
789
790 public void updateSize() {
Robert Carre625fcf2017-09-01 12:36:28 -0700791 synchronized (mService.mWindowMap) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800792 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
793 mSurfaceControl.setSize(mTempPoint.x, mTempPoint.y);
794 invalidate(mDirtyRect);
795 }
796 }
797
798 public void invalidate(Rect dirtyRect) {
799 if (dirtyRect != null) {
800 mDirtyRect.set(dirtyRect);
801 } else {
802 mDirtyRect.setEmpty();
803 }
804 mInvalidated = true;
Robert Carre625fcf2017-09-01 12:36:28 -0700805 mService.scheduleAnimationLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800806 }
807
808 /** NOTE: This has to be called within a surface transaction. */
809 public void drawIfNeeded() {
Robert Carre625fcf2017-09-01 12:36:28 -0700810 synchronized (mService.mWindowMap) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800811 if (!mInvalidated) {
812 return;
813 }
814 mInvalidated = false;
815 Canvas canvas = null;
816 try {
817 // Empty dirty rectangle means unspecified.
818 if (mDirtyRect.isEmpty()) {
819 mBounds.getBounds(mDirtyRect);
820 }
821 mDirtyRect.inset(- mHalfBorderWidth, - mHalfBorderWidth);
822 canvas = mSurface.lockCanvas(mDirtyRect);
823 if (DEBUG_VIEWPORT_WINDOW) {
824 Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect);
825 }
826 } catch (IllegalArgumentException iae) {
827 /* ignore */
828 } catch (Surface.OutOfResourcesException oore) {
829 /* ignore */
830 }
831 if (canvas == null) {
832 return;
833 }
834 if (DEBUG_VIEWPORT_WINDOW) {
835 Slog.i(LOG_TAG, "Bounds: " + mBounds);
836 }
837 canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
838 mPaint.setAlpha(mAlpha);
839 Path path = mBounds.getBoundaryPath();
840 canvas.drawPath(path, mPaint);
841
842 mSurface.unlockCanvasAndPost(canvas);
843
844 if (mAlpha > 0) {
845 mSurfaceControl.show();
846 } else {
847 mSurfaceControl.hide();
848 }
849 }
850 }
851
852 public void releaseSurface() {
853 mSurfaceControl.release();
854 mSurface.release();
855 }
Svet Ganovb21df802014-09-01 19:06:33 -0700856
857 private final class AnimationController extends Handler {
858 private static final String PROPERTY_NAME_ALPHA = "alpha";
859
860 private static final int MIN_ALPHA = 0;
861 private static final int MAX_ALPHA = 255;
862
863 private static final int MSG_FRAME_SHOWN_STATE_CHANGED = 1;
864
865 private final ValueAnimator mShowHideFrameAnimator;
866
867 public AnimationController(Context context, Looper looper) {
868 super(looper);
869 mShowHideFrameAnimator = ObjectAnimator.ofInt(ViewportWindow.this,
870 PROPERTY_NAME_ALPHA, MIN_ALPHA, MAX_ALPHA);
871
872 Interpolator interpolator = new DecelerateInterpolator(2.5f);
873 final long longAnimationDuration = context.getResources().getInteger(
874 com.android.internal.R.integer.config_longAnimTime);
875
876 mShowHideFrameAnimator.setInterpolator(interpolator);
877 mShowHideFrameAnimator.setDuration(longAnimationDuration);
878 }
879
880 public void onFrameShownStateChanged(boolean shown, boolean animate) {
881 obtainMessage(MSG_FRAME_SHOWN_STATE_CHANGED,
882 shown ? 1 : 0, animate ? 1 : 0).sendToTarget();
883 }
884
885 @Override
886 public void handleMessage(Message message) {
887 switch (message.what) {
888 case MSG_FRAME_SHOWN_STATE_CHANGED: {
889 final boolean shown = message.arg1 == 1;
890 final boolean animate = message.arg2 == 1;
891
892 if (animate) {
893 if (mShowHideFrameAnimator.isRunning()) {
894 mShowHideFrameAnimator.reverse();
895 } else {
896 if (shown) {
897 mShowHideFrameAnimator.start();
898 } else {
899 mShowHideFrameAnimator.reverse();
900 }
901 }
902 } else {
903 mShowHideFrameAnimator.cancel();
904 if (shown) {
905 setAlpha(MAX_ALPHA);
906 } else {
907 setAlpha(MIN_ALPHA);
908 }
909 }
910 } break;
911 }
912 }
913 }
Svetoslav8e3feb12014-02-24 13:46:47 -0800914 }
915 }
916
917 private class MyHandler extends Handler {
Phil Weaver70439242016-03-10 15:15:49 -0800918 public static final int MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED = 1;
Svetoslav8e3feb12014-02-24 13:46:47 -0800919 public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2;
920 public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
921 public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
922 public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
923
924 public MyHandler(Looper looper) {
925 super(looper);
926 }
927
928 @Override
929 public void handleMessage(Message message) {
930 switch (message.what) {
Phil Weaver70439242016-03-10 15:15:49 -0800931 case MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED: {
Alan Viverette214fb682015-11-17 09:47:11 -0500932 final SomeArgs args = (SomeArgs) message.obj;
933 final Region magnifiedBounds = (Region) args.arg1;
Phil Weaver70439242016-03-10 15:15:49 -0800934 mCallbacks.onMagnificationRegionChanged(magnifiedBounds);
Alan Viverette214fb682015-11-17 09:47:11 -0500935 magnifiedBounds.recycle();
Svetoslav8e3feb12014-02-24 13:46:47 -0800936 } break;
937
938 case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: {
939 SomeArgs args = (SomeArgs) message.obj;
940 final int left = args.argi1;
941 final int top = args.argi2;
942 final int right = args.argi3;
943 final int bottom = args.argi4;
944 mCallbacks.onRectangleOnScreenRequested(left, top, right, bottom);
945 args.recycle();
946 } break;
947
948 case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: {
949 mCallbacks.onUserContextChanged();
950 } break;
951
952 case MESSAGE_NOTIFY_ROTATION_CHANGED: {
953 final int rotation = message.arg1;
954 mCallbacks.onRotationChanged(rotation);
955 } break;
956
957 case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
Robert Carre625fcf2017-09-01 12:36:28 -0700958 synchronized (mService.mWindowMap) {
Casey Burkhardt74922c62017-02-13 12:43:16 -0800959 if (mMagnifedViewport.isMagnifyingLocked()
960 || isForceShowingMagnifiableBoundsLocked()) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800961 mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true);
Robert Carre625fcf2017-09-01 12:36:28 -0700962 mService.scheduleAnimationLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800963 }
964 }
965 } break;
966 }
967 }
968 }
969 }
970
971 /**
972 * This class encapsulates the functionality related to computing the windows
973 * reported for accessibility purposes. These windows are all windows a sighted
974 * user can see on the screen.
975 */
976 private static final class WindowsForAccessibilityObserver {
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -0800977 private static final String LOG_TAG = TAG_WITH_CLASS_NAME ?
978 "WindowsForAccessibilityObserver" : TAG_WM;
Svetoslav8e3feb12014-02-24 13:46:47 -0800979
980 private static final boolean DEBUG = false;
981
982 private final SparseArray<WindowState> mTempWindowStates =
983 new SparseArray<WindowState>();
984
985 private final List<WindowInfo> mOldWindows = new ArrayList<WindowInfo>();
986
987 private final Set<IBinder> mTempBinderSet = new ArraySet<IBinder>();
988
989 private final RectF mTempRectF = new RectF();
990
991 private final Matrix mTempMatrix = new Matrix();
992
993 private final Point mTempPoint = new Point();
994
995 private final Rect mTempRect = new Rect();
996
997 private final Region mTempRegion = new Region();
998
999 private final Region mTempRegion1 = new Region();
1000
1001 private final Context mContext;
1002
Robert Carre625fcf2017-09-01 12:36:28 -07001003 private final WindowManagerService mService;
Svetoslav8e3feb12014-02-24 13:46:47 -08001004
1005 private final Handler mHandler;
1006
1007 private final WindowsForAccessibilityCallback mCallback;
1008
Svetoslavf7174e82014-06-12 11:29:35 -07001009 private final long mRecurringAccessibilityEventsIntervalMillis;
1010
Robert Carrb1579c82017-09-05 14:54:47 -07001011 private int mTempLayer = 0;
1012
Svetoslav8e3feb12014-02-24 13:46:47 -08001013 public WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
1014 WindowsForAccessibilityCallback callback) {
1015 mContext = windowManagerService.mContext;
Robert Carre625fcf2017-09-01 12:36:28 -07001016 mService = windowManagerService;
Svetoslav8e3feb12014-02-24 13:46:47 -08001017 mCallback = callback;
Robert Carre625fcf2017-09-01 12:36:28 -07001018 mHandler = new MyHandler(mService.mH.getLooper());
Svetoslavf7174e82014-06-12 11:29:35 -07001019 mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration
1020 .getSendRecurringAccessibilityEventsInterval();
Svetoslav8e3feb12014-02-24 13:46:47 -08001021 computeChangedWindows();
1022 }
1023
Svetoslav3a0d8782014-12-04 12:50:11 -08001024 public void performComputeChangedWindowsNotLocked() {
1025 mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS);
1026 computeChangedWindows();
1027 }
1028
Svetoslavf7174e82014-06-12 11:29:35 -07001029 public void scheduleComputeChangedWindowsLocked() {
Svetoslav3a0d8782014-12-04 12:50:11 -08001030 if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) {
Svetoslavf7174e82014-06-12 11:29:35 -07001031 mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS,
1032 mRecurringAccessibilityEventsIntervalMillis);
1033 }
1034 }
1035
Svetoslav8e3feb12014-02-24 13:46:47 -08001036 public void computeChangedWindows() {
1037 if (DEBUG) {
1038 Slog.i(LOG_TAG, "computeChangedWindows()");
1039 }
1040
Svetoslav3a0d8782014-12-04 12:50:11 -08001041 boolean windowsChanged = false;
1042 List<WindowInfo> windows = new ArrayList<WindowInfo>();
1043
Robert Carre625fcf2017-09-01 12:36:28 -07001044 synchronized (mService.mWindowMap) {
Svetoslavf7174e82014-06-12 11:29:35 -07001045 // Do not send the windows if there is no current focus as
1046 // the window manager is still looking for where to put it.
1047 // We will do the work when we get a focus change callback.
Robert Carre625fcf2017-09-01 12:36:28 -07001048 if (mService.mCurrentFocus == null) {
Svetoslavf7174e82014-06-12 11:29:35 -07001049 return;
1050 }
1051
Svetoslav8e3feb12014-02-24 13:46:47 -08001052 WindowManager windowManager = (WindowManager)
1053 mContext.getSystemService(Context.WINDOW_SERVICE);
1054 windowManager.getDefaultDisplay().getRealSize(mTempPoint);
1055 final int screenWidth = mTempPoint.x;
1056 final int screenHeight = mTempPoint.y;
1057
1058 Region unaccountedSpace = mTempRegion;
1059 unaccountedSpace.set(0, 0, screenWidth, screenHeight);
1060
Wale Ogunwalef7cab102016-10-25 15:25:14 -07001061 final SparseArray<WindowState> visibleWindows = mTempWindowStates;
Svetoslav8e3feb12014-02-24 13:46:47 -08001062 populateVisibleWindowsOnScreenLocked(visibleWindows);
Svetoslav8e3feb12014-02-24 13:46:47 -08001063 Set<IBinder> addedWindows = mTempBinderSet;
1064 addedWindows.clear();
1065
Svetoslavf7174e82014-06-12 11:29:35 -07001066 boolean focusedWindowAdded = false;
1067
Svetoslav8e3feb12014-02-24 13:46:47 -08001068 final int visibleWindowCount = visibleWindows.size();
Allen Hairf20ac2c2016-02-11 17:42:59 -08001069 HashSet<Integer> skipRemainingWindowsForTasks = new HashSet<>();
Svetoslav8e3feb12014-02-24 13:46:47 -08001070 for (int i = visibleWindowCount - 1; i >= 0; i--) {
Alan Viverette9538eea2014-11-13 14:49:20 -08001071 final WindowState windowState = visibleWindows.valueAt(i);
Svetoslav8e3feb12014-02-24 13:46:47 -08001072 final int flags = windowState.mAttrs.flags;
Allen Hairf20ac2c2016-02-11 17:42:59 -08001073 final Task task = windowState.getTask();
1074
1075 // If the window is part of a task that we're finished with - ignore.
1076 if (task != null && skipRemainingWindowsForTasks.contains(task.mTaskId)) {
1077 continue;
1078 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001079
Svetoslav3a5c7212014-10-14 09:54:26 -07001080 // If the window is not touchable - ignore.
Svetoslavf7174e82014-06-12 11:29:35 -07001081 if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001082 continue;
1083 }
1084
Alan Viverette9538eea2014-11-13 14:49:20 -08001085 // Compute the bounds in the screen.
1086 final Rect boundsInScreen = mTempRect;
1087 computeWindowBoundsInScreen(windowState, boundsInScreen);
1088
Svetoslav8e3feb12014-02-24 13:46:47 -08001089 // If the window is completely covered by other windows - ignore.
1090 if (unaccountedSpace.quickReject(boundsInScreen)) {
1091 continue;
1092 }
1093
1094 // Add windows of certain types not covered by modal windows.
1095 if (isReportedWindowType(windowState.mAttrs.type)) {
1096 // Add the window to the ones to be reported.
Svetoslavf7174e82014-06-12 11:29:35 -07001097 WindowInfo window = obtainPopulatedWindowInfo(windowState, boundsInScreen);
Robert Carrb1579c82017-09-05 14:54:47 -07001098 window.layer = addedWindows.size();
Svetoslav8e3feb12014-02-24 13:46:47 -08001099 addedWindows.add(window.token);
Svetoslav8e3feb12014-02-24 13:46:47 -08001100 windows.add(window);
Svetoslavf7174e82014-06-12 11:29:35 -07001101 if (windowState.isFocused()) {
1102 focusedWindowAdded = true;
1103 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001104 }
1105
Alan Viveretted0c73f42014-11-18 10:25:04 -08001106 // Account for the space this window takes if the window
1107 // is not an accessibility overlay which does not change
1108 // the reported windows.
1109 if (windowState.mAttrs.type !=
1110 WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
1111 unaccountedSpace.op(boundsInScreen, unaccountedSpace,
1112 Region.Op.REVERSE_DIFFERENCE);
1113 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001114
Allen Hairf20ac2c2016-02-11 17:42:59 -08001115 // If a window is modal it prevents other windows from being touched
Svetoslav8e3feb12014-02-24 13:46:47 -08001116 if ((flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1117 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
Phil Weaverac9ad702016-07-27 18:03:10 -07001118 // Account for all space in the task, whether the windows in it are
1119 // touchable or not. The modal window blocks all touches from the task's
1120 // area.
1121 unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace,
1122 Region.Op.REVERSE_DIFFERENCE);
1123
Allen Hairf20ac2c2016-02-11 17:42:59 -08001124 if (task != null) {
1125 // If the window is associated with a particular task, we can skip the
1126 // rest of the windows for that task.
1127 skipRemainingWindowsForTasks.add(task.mTaskId);
1128 continue;
1129 } else {
1130 // If the window is not associated with a particular task, then it is
1131 // globally modal. In this case we can skip all remaining windows.
1132 break;
1133 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001134 }
Phil Weaverac9ad702016-07-27 18:03:10 -07001135 // We figured out what is touchable for the entire screen - done.
1136 if (unaccountedSpace.isEmpty()) {
1137 break;
1138 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001139 }
1140
Svetoslavf7174e82014-06-12 11:29:35 -07001141 // Always report the focused window.
1142 if (!focusedWindowAdded) {
1143 for (int i = visibleWindowCount - 1; i >= 0; i--) {
1144 WindowState windowState = visibleWindows.valueAt(i);
1145 if (windowState.isFocused()) {
1146 // Compute the bounds in the screen.
1147 Rect boundsInScreen = mTempRect;
1148 computeWindowBoundsInScreen(windowState, boundsInScreen);
1149
1150 // Add the window to the ones to be reported.
1151 WindowInfo window = obtainPopulatedWindowInfo(windowState,
1152 boundsInScreen);
1153 addedWindows.add(window.token);
1154 windows.add(window);
1155 break;
1156 }
1157 }
1158 }
1159
Svetoslav8e3feb12014-02-24 13:46:47 -08001160 // Remove child/parent references to windows that were not added.
1161 final int windowCount = windows.size();
1162 for (int i = 0; i < windowCount; i++) {
1163 WindowInfo window = windows.get(i);
1164 if (!addedWindows.contains(window.parentToken)) {
1165 window.parentToken = null;
1166 }
1167 if (window.childTokens != null) {
1168 final int childTokenCount = window.childTokens.size();
1169 for (int j = childTokenCount - 1; j >= 0; j--) {
1170 if (!addedWindows.contains(window.childTokens.get(j))) {
1171 window.childTokens.remove(j);
1172 }
1173 }
1174 // Leave the child token list if empty.
1175 }
1176 }
1177
1178 visibleWindows.clear();
1179 addedWindows.clear();
1180
1181 // We computed the windows and if they changed notify the client.
Svetoslav8e3feb12014-02-24 13:46:47 -08001182 if (mOldWindows.size() != windows.size()) {
1183 // Different size means something changed.
1184 windowsChanged = true;
1185 } else if (!mOldWindows.isEmpty() || !windows.isEmpty()) {
1186 // Since we always traverse windows from high to low layer
1187 // the old and new windows at the same index should be the
1188 // same, otherwise something changed.
1189 for (int i = 0; i < windowCount; i++) {
1190 WindowInfo oldWindow = mOldWindows.get(i);
1191 WindowInfo newWindow = windows.get(i);
1192 // We do not care for layer changes given the window
1193 // order does not change. This brings no new information
1194 // to the clients.
1195 if (windowChangedNoLayer(oldWindow, newWindow)) {
1196 windowsChanged = true;
1197 break;
1198 }
1199 }
1200 }
1201
1202 if (windowsChanged) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001203 cacheWindows(windows);
Svetoslav8e3feb12014-02-24 13:46:47 -08001204 }
1205 }
Svetoslav3a0d8782014-12-04 12:50:11 -08001206
1207 // Now we do not hold the lock, so send the windows over.
1208 if (windowsChanged) {
1209 if (DEBUG) {
1210 Log.i(LOG_TAG, "Windows changed:" + windows);
1211 }
1212 mCallback.onWindowsForAccessibilityChanged(windows);
1213 } else {
1214 if (DEBUG) {
1215 Log.i(LOG_TAG, "No windows changed.");
1216 }
1217 }
1218
1219 // Recycle the windows as we do not need them.
1220 clearAndRecycleWindows(windows);
Svetoslav8e3feb12014-02-24 13:46:47 -08001221 }
1222
Svetoslavf7174e82014-06-12 11:29:35 -07001223 private void computeWindowBoundsInScreen(WindowState windowState, Rect outBounds) {
1224 // Get the touchable frame.
1225 Region touchableRegion = mTempRegion1;
1226 windowState.getTouchableRegion(touchableRegion);
1227 Rect touchableFrame = mTempRect;
1228 touchableRegion.getBounds(touchableFrame);
1229
1230 // Move to origin as all transforms are captured by the matrix.
1231 RectF windowFrame = mTempRectF;
1232 windowFrame.set(touchableFrame);
1233 windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
1234
1235 // Map the frame to get what appears on the screen.
1236 Matrix matrix = mTempMatrix;
1237 populateTransformationMatrixLocked(windowState, matrix);
1238 matrix.mapRect(windowFrame);
1239
1240 // Got the bounds.
1241 outBounds.set((int) windowFrame.left, (int) windowFrame.top,
1242 (int) windowFrame.right, (int) windowFrame.bottom);
1243 }
1244
Wale Ogunwaleadde52e2016-07-16 13:11:55 -07001245 private static WindowInfo obtainPopulatedWindowInfo(
1246 WindowState windowState, Rect boundsInScreen) {
1247 final WindowInfo window = windowState.getWindowInfo();
Svetoslavf7174e82014-06-12 11:29:35 -07001248 window.boundsInScreen.set(boundsInScreen);
Svetoslavf7174e82014-06-12 11:29:35 -07001249 return window;
1250 }
1251
Svetoslav8e3feb12014-02-24 13:46:47 -08001252 private void cacheWindows(List<WindowInfo> windows) {
1253 final int oldWindowCount = mOldWindows.size();
1254 for (int i = oldWindowCount - 1; i >= 0; i--) {
1255 mOldWindows.remove(i).recycle();
1256 }
1257 final int newWindowCount = windows.size();
1258 for (int i = 0; i < newWindowCount; i++) {
1259 WindowInfo newWindow = windows.get(i);
1260 mOldWindows.add(WindowInfo.obtain(newWindow));
1261 }
1262 }
1263
1264 private boolean windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow) {
1265 if (oldWindow == newWindow) {
1266 return false;
1267 }
Svetoslavf7174e82014-06-12 11:29:35 -07001268 if (oldWindow == null) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001269 return true;
1270 }
Svetoslavf7174e82014-06-12 11:29:35 -07001271 if (newWindow == null) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001272 return true;
1273 }
1274 if (oldWindow.type != newWindow.type) {
1275 return true;
1276 }
1277 if (oldWindow.focused != newWindow.focused) {
1278 return true;
1279 }
1280 if (oldWindow.token == null) {
1281 if (newWindow.token != null) {
1282 return true;
1283 }
1284 } else if (!oldWindow.token.equals(newWindow.token)) {
1285 return true;
1286 }
1287 if (oldWindow.parentToken == null) {
1288 if (newWindow.parentToken != null) {
1289 return true;
1290 }
1291 } else if (!oldWindow.parentToken.equals(newWindow.parentToken)) {
1292 return true;
1293 }
1294 if (!oldWindow.boundsInScreen.equals(newWindow.boundsInScreen)) {
1295 return true;
1296 }
1297 if (oldWindow.childTokens != null && newWindow.childTokens != null
1298 && !oldWindow.childTokens.equals(newWindow.childTokens)) {
1299 return true;
1300 }
Phil Weaver396d5492016-03-22 17:53:50 -07001301 if (!TextUtils.equals(oldWindow.title, newWindow.title)) {
1302 return true;
1303 }
1304 if (oldWindow.accessibilityIdOfAnchor != newWindow.accessibilityIdOfAnchor) {
1305 return true;
1306 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001307 return false;
1308 }
1309
Svetoslav3a0d8782014-12-04 12:50:11 -08001310 private static void clearAndRecycleWindows(List<WindowInfo> windows) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001311 final int windowCount = windows.size();
1312 for (int i = windowCount - 1; i >= 0; i--) {
1313 windows.remove(i).recycle();
1314 }
1315 }
1316
1317 private static boolean isReportedWindowType(int windowType) {
Jorim Jaggi73294b62016-10-26 18:02:36 -07001318 return (windowType != WindowManager.LayoutParams.TYPE_WALLPAPER
Svetoslav8e3feb12014-02-24 13:46:47 -08001319 && windowType != WindowManager.LayoutParams.TYPE_BOOT_PROGRESS
1320 && windowType != WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY
1321 && windowType != WindowManager.LayoutParams.TYPE_DRAG
Selim Cinekf83e8242015-05-19 18:08:14 -07001322 && windowType != WindowManager.LayoutParams.TYPE_INPUT_CONSUMER
Svetoslav8e3feb12014-02-24 13:46:47 -08001323 && windowType != WindowManager.LayoutParams.TYPE_POINTER
Phil Weaverd321075e2017-06-13 09:13:35 -07001324 && windowType != TYPE_MAGNIFICATION_OVERLAY
Svetoslav8e3feb12014-02-24 13:46:47 -08001325 && windowType != WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
1326 && windowType != WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY
1327 && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
1328 }
1329
1330 private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
Robert Carre625fcf2017-09-01 12:36:28 -07001331 final DisplayContent dc = mService.getDefaultDisplayContentLocked();
Robert Carrb1579c82017-09-05 14:54:47 -07001332 mTempLayer = 0;
Wale Ogunwaled1880962016-11-08 10:31:59 -08001333 dc.forAllWindows((w) -> {
1334 if (w.isVisibleLw()) {
Robert Carrb1579c82017-09-05 14:54:47 -07001335 outWindows.put(mTempLayer++, w);
Svetoslav8e3feb12014-02-24 13:46:47 -08001336 }
Wale Ogunwaled1880962016-11-08 10:31:59 -08001337 }, false /* traverseTopToBottom */ );
Svetoslav8e3feb12014-02-24 13:46:47 -08001338 }
1339
1340 private class MyHandler extends Handler {
Svetoslavf7174e82014-06-12 11:29:35 -07001341 public static final int MESSAGE_COMPUTE_CHANGED_WINDOWS = 1;
Svetoslav8e3feb12014-02-24 13:46:47 -08001342
1343 public MyHandler(Looper looper) {
1344 super(looper, null, false);
1345 }
1346
1347 @Override
1348 @SuppressWarnings("unchecked")
1349 public void handleMessage(Message message) {
1350 switch (message.what) {
Svetoslavf7174e82014-06-12 11:29:35 -07001351 case MESSAGE_COMPUTE_CHANGED_WINDOWS: {
1352 computeChangedWindows();
1353 } break;
Svetoslav8e3feb12014-02-24 13:46:47 -08001354 }
1355 }
1356 }
1357 }
1358}