blob: 659253f9d603b20db2a7327be0411a918f66ff57 [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 Weaverb2779532017-12-18 17:09:32 -080019import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
Phil Weaverd321075e2017-06-13 09:13:35 -070020import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
Robert Carr132c9f52017-07-31 17:02:30 -070021import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
Phil Weaverd321075e2017-06-13 09:13:35 -070022
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -080023import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
24import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
25
Svetoslav8e3feb12014-02-24 13:46:47 -080026import android.animation.ObjectAnimator;
27import android.animation.ValueAnimator;
Alan Viverette59e53a12016-03-28 13:41:32 -040028import android.annotation.NonNull;
Svetoslav8e3feb12014-02-24 13:46:47 -080029import android.app.Service;
30import android.content.Context;
31import android.graphics.Canvas;
32import android.graphics.Color;
33import android.graphics.Matrix;
34import android.graphics.Paint;
35import android.graphics.Path;
36import android.graphics.PixelFormat;
37import android.graphics.Point;
38import android.graphics.PorterDuff.Mode;
39import android.graphics.Rect;
40import android.graphics.RectF;
41import android.graphics.Region;
42import android.os.Handler;
43import android.os.IBinder;
44import android.os.Looper;
45import android.os.Message;
Phil Weaver396d5492016-03-22 17:53:50 -070046import android.text.TextUtils;
Svetoslav8e3feb12014-02-24 13:46:47 -080047import android.util.ArraySet;
48import android.util.Log;
49import android.util.Slog;
50import android.util.SparseArray;
51import android.util.TypedValue;
52import android.view.MagnificationSpec;
53import android.view.Surface;
54import android.view.Surface.OutOfResourcesException;
55import android.view.SurfaceControl;
Svetoslavf7174e82014-06-12 11:29:35 -070056import android.view.ViewConfiguration;
Svetoslav8e3feb12014-02-24 13:46:47 -080057import android.view.WindowInfo;
58import android.view.WindowManager;
Svetoslav8e3feb12014-02-24 13:46:47 -080059import android.view.animation.DecelerateInterpolator;
60import android.view.animation.Interpolator;
61
62import com.android.internal.R;
63import com.android.internal.os.SomeArgs;
Adrian Roose99bc052017-11-20 17:55:31 +010064import com.android.server.policy.WindowManagerPolicy;
65import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks;
66import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback;
Svetoslav8e3feb12014-02-24 13:46:47 -080067
68import java.util.ArrayList;
Allen Hairf20ac2c2016-02-11 17:42:59 -080069import java.util.HashSet;
Svetoslav8e3feb12014-02-24 13:46:47 -080070import java.util.List;
71import java.util.Set;
72
73/**
74 * This class contains the accessibility related logic of the window manger.
75 */
76final class AccessibilityController {
77
Robert Carre625fcf2017-09-01 12:36:28 -070078 private final WindowManagerService mService;
Svetoslav8e3feb12014-02-24 13:46:47 -080079
80 private static final float[] sTempFloats = new float[9];
81
82 public AccessibilityController(WindowManagerService service) {
Robert Carre625fcf2017-09-01 12:36:28 -070083 mService = service;
Svetoslav8e3feb12014-02-24 13:46:47 -080084 }
85
86 private DisplayMagnifier mDisplayMagnifier;
87
88 private WindowsForAccessibilityObserver mWindowsForAccessibilityObserver;
89
90 public void setMagnificationCallbacksLocked(MagnificationCallbacks callbacks) {
91 if (callbacks != null) {
92 if (mDisplayMagnifier != null) {
93 throw new IllegalStateException("Magnification callbacks already set!");
94 }
Robert Carre625fcf2017-09-01 12:36:28 -070095 mDisplayMagnifier = new DisplayMagnifier(mService, callbacks);
Svetoslav8e3feb12014-02-24 13:46:47 -080096 } else {
97 if (mDisplayMagnifier == null) {
98 throw new IllegalStateException("Magnification callbacks already cleared!");
99 }
100 mDisplayMagnifier.destroyLocked();
101 mDisplayMagnifier = null;
102 }
103 }
104
105 public void setWindowsForAccessibilityCallback(WindowsForAccessibilityCallback callback) {
106 if (callback != null) {
107 if (mWindowsForAccessibilityObserver != null) {
108 throw new IllegalStateException(
109 "Windows for accessibility callback already set!");
110 }
111 mWindowsForAccessibilityObserver = new WindowsForAccessibilityObserver(
Robert Carre625fcf2017-09-01 12:36:28 -0700112 mService, callback);
Svetoslav8e3feb12014-02-24 13:46:47 -0800113 } else {
114 if (mWindowsForAccessibilityObserver == null) {
115 throw new IllegalStateException(
116 "Windows for accessibility callback already cleared!");
117 }
118 mWindowsForAccessibilityObserver = null;
119 }
120 }
121
Svetoslav Ganovfd138892016-07-13 18:20:42 -0700122 public void performComputeChangedWindowsNotLocked() {
123 WindowsForAccessibilityObserver observer = null;
Robert Carre625fcf2017-09-01 12:36:28 -0700124 synchronized (mService) {
Svetoslav Ganovfd138892016-07-13 18:20:42 -0700125 observer = mWindowsForAccessibilityObserver;
126 }
127 if (observer != null) {
128 observer.performComputeChangedWindowsNotLocked();
129 }
130 }
131
Svetoslav8e3feb12014-02-24 13:46:47 -0800132 public void setMagnificationSpecLocked(MagnificationSpec spec) {
133 if (mDisplayMagnifier != null) {
134 mDisplayMagnifier.setMagnificationSpecLocked(spec);
135 }
136 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700137 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800138 }
139 }
140
Phil Weaver70439242016-03-10 15:15:49 -0800141 public void getMagnificationRegionLocked(Region outMagnificationRegion) {
Alan Viverette59e53a12016-03-28 13:41:32 -0400142 if (mDisplayMagnifier != null) {
Phil Weaver70439242016-03-10 15:15:49 -0800143 mDisplayMagnifier.getMagnificationRegionLocked(outMagnificationRegion);
Alan Viverette59e53a12016-03-28 13:41:32 -0400144 }
145 }
146
Svetoslavf7174e82014-06-12 11:29:35 -0700147 public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800148 if (mDisplayMagnifier != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700149 mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle);
Svetoslav8e3feb12014-02-24 13:46:47 -0800150 }
151 // Not relevant for the window observer.
152 }
153
154 public void onWindowLayersChangedLocked() {
155 if (mDisplayMagnifier != null) {
156 mDisplayMagnifier.onWindowLayersChangedLocked();
157 }
158 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700159 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800160 }
161 }
162
Andrii Kulian8ee72852017-03-10 10:36:45 -0800163 public void onRotationChangedLocked(DisplayContent displayContent) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800164 if (mDisplayMagnifier != null) {
Andrii Kulian8ee72852017-03-10 10:36:45 -0800165 mDisplayMagnifier.onRotationChangedLocked(displayContent);
Svetoslav8e3feb12014-02-24 13:46:47 -0800166 }
167 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700168 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800169 }
170 }
171
172 public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
173 if (mDisplayMagnifier != null) {
174 mDisplayMagnifier.onAppWindowTransitionLocked(windowState, transition);
175 }
176 // Not relevant for the window observer.
177 }
178
179 public void onWindowTransitionLocked(WindowState windowState, int transition) {
180 if (mDisplayMagnifier != null) {
181 mDisplayMagnifier.onWindowTransitionLocked(windowState, transition);
182 }
183 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700184 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800185 }
186 }
187
Svetoslav3a0d8782014-12-04 12:50:11 -0800188 public void onWindowFocusChangedNotLocked() {
Svetoslav8e3feb12014-02-24 13:46:47 -0800189 // Not relevant for the display magnifier.
190
Svetoslav3a0d8782014-12-04 12:50:11 -0800191 WindowsForAccessibilityObserver observer = null;
Robert Carre625fcf2017-09-01 12:36:28 -0700192 synchronized (mService) {
Svetoslav3a0d8782014-12-04 12:50:11 -0800193 observer = mWindowsForAccessibilityObserver;
194 }
195 if (observer != null) {
196 observer.performComputeChangedWindowsNotLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800197 }
198 }
199
Svetoslav4604abc2014-06-10 18:59:30 -0700200
Svetoslavf7174e82014-06-12 11:29:35 -0700201 public void onSomeWindowResizedOrMovedLocked() {
Svetoslav4604abc2014-06-10 18:59:30 -0700202 // Not relevant for the display magnifier.
203
204 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700205 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav4604abc2014-06-10 18:59:30 -0700206 }
207 }
208
Svetoslav8e3feb12014-02-24 13:46:47 -0800209 /** NOTE: This has to be called within a surface transaction. */
210 public void drawMagnifiedRegionBorderIfNeededLocked() {
211 if (mDisplayMagnifier != null) {
212 mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked();
213 }
214 // Not relevant for the window observer.
215 }
216
217 public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
218 if (mDisplayMagnifier != null) {
219 return mDisplayMagnifier.getMagnificationSpecForWindowLocked(windowState);
220 }
221 return null;
222 }
223
224 public boolean hasCallbacksLocked() {
225 return (mDisplayMagnifier != null
226 || mWindowsForAccessibilityObserver != null);
227 }
228
Casey Burkhardt74922c62017-02-13 12:43:16 -0800229 public void setForceShowMagnifiableBoundsLocked(boolean show) {
230 if (mDisplayMagnifier != null) {
231 mDisplayMagnifier.setForceShowMagnifiableBoundsLocked(show);
Phil Weaver251db072017-03-28 08:35:38 -0700232 mDisplayMagnifier.showMagnificationBoundsIfNeeded();
Casey Burkhardt74922c62017-02-13 12:43:16 -0800233 }
234 }
235
Svetoslav8e3feb12014-02-24 13:46:47 -0800236 private static void populateTransformationMatrixLocked(WindowState windowState,
237 Matrix outMatrix) {
Jorim Jaggieb0d3bc2017-12-15 14:56:19 +0100238 windowState.getTransformationMatrix(sTempFloats, outMatrix);
Svetoslav8e3feb12014-02-24 13:46:47 -0800239 }
240
241 /**
242 * This class encapsulates the functionality related to display magnification.
243 */
244 private static final class DisplayMagnifier {
245
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -0800246 private static final String LOG_TAG = TAG_WITH_CLASS_NAME ? "DisplayMagnifier" : TAG_WM;
Svetoslav8e3feb12014-02-24 13:46:47 -0800247
248 private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
249 private static final boolean DEBUG_ROTATION = false;
250 private static final boolean DEBUG_LAYERS = false;
251 private static final boolean DEBUG_RECTANGLE_REQUESTED = false;
252 private static final boolean DEBUG_VIEWPORT_WINDOW = false;
253
254 private final Rect mTempRect1 = new Rect();
255 private final Rect mTempRect2 = new Rect();
256
257 private final Region mTempRegion1 = new Region();
258 private final Region mTempRegion2 = new Region();
259 private final Region mTempRegion3 = new Region();
260 private final Region mTempRegion4 = new Region();
261
262 private final Context mContext;
Robert Carre625fcf2017-09-01 12:36:28 -0700263 private final WindowManagerService mService;
Svetoslav8e3feb12014-02-24 13:46:47 -0800264 private final MagnifiedViewport mMagnifedViewport;
265 private final Handler mHandler;
266
267 private final MagnificationCallbacks mCallbacks;
268
269 private final long mLongAnimationDuration;
270
Casey Burkhardt74922c62017-02-13 12:43:16 -0800271 private boolean mForceShowMagnifiableBounds = false;
272
Svetoslav8e3feb12014-02-24 13:46:47 -0800273 public DisplayMagnifier(WindowManagerService windowManagerService,
274 MagnificationCallbacks callbacks) {
275 mContext = windowManagerService.mContext;
Robert Carre625fcf2017-09-01 12:36:28 -0700276 mService = windowManagerService;
Svetoslav8e3feb12014-02-24 13:46:47 -0800277 mCallbacks = callbacks;
Robert Carre625fcf2017-09-01 12:36:28 -0700278 mHandler = new MyHandler(mService.mH.getLooper());
Svetoslav8e3feb12014-02-24 13:46:47 -0800279 mMagnifedViewport = new MagnifiedViewport();
280 mLongAnimationDuration = mContext.getResources().getInteger(
281 com.android.internal.R.integer.config_longAnimTime);
282 }
283
284 public void setMagnificationSpecLocked(MagnificationSpec spec) {
285 mMagnifedViewport.updateMagnificationSpecLocked(spec);
286 mMagnifedViewport.recomputeBoundsLocked();
Robert Carrb1579c82017-09-05 14:54:47 -0700287
288 mService.applyMagnificationSpec(spec);
Robert Carre625fcf2017-09-01 12:36:28 -0700289 mService.scheduleAnimationLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800290 }
291
Casey Burkhardt74922c62017-02-13 12:43:16 -0800292 public void setForceShowMagnifiableBoundsLocked(boolean show) {
293 mForceShowMagnifiableBounds = show;
294 mMagnifedViewport.setMagnifiedRegionBorderShownLocked(show, true);
295 }
296
297 public boolean isForceShowingMagnifiableBoundsLocked() {
298 return mForceShowMagnifiableBounds;
299 }
300
Svetoslavf7174e82014-06-12 11:29:35 -0700301 public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800302 if (DEBUG_RECTANGLE_REQUESTED) {
303 Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
304 }
305 if (!mMagnifedViewport.isMagnifyingLocked()) {
306 return;
307 }
308 Rect magnifiedRegionBounds = mTempRect2;
309 mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds);
310 if (magnifiedRegionBounds.contains(rectangle)) {
311 return;
312 }
313 SomeArgs args = SomeArgs.obtain();
314 args.argi1 = rectangle.left;
315 args.argi2 = rectangle.top;
316 args.argi3 = rectangle.right;
317 args.argi4 = rectangle.bottom;
318 mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED,
319 args).sendToTarget();
320 }
321
322 public void onWindowLayersChangedLocked() {
323 if (DEBUG_LAYERS) {
324 Slog.i(LOG_TAG, "Layers changed.");
325 }
326 mMagnifedViewport.recomputeBoundsLocked();
Robert Carre625fcf2017-09-01 12:36:28 -0700327 mService.scheduleAnimationLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800328 }
329
Andrii Kulian8ee72852017-03-10 10:36:45 -0800330 public void onRotationChangedLocked(DisplayContent displayContent) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800331 if (DEBUG_ROTATION) {
Andrii Kulian8ee72852017-03-10 10:36:45 -0800332 final int rotation = displayContent.getRotation();
333 Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation)
Svetoslav8e3feb12014-02-24 13:46:47 -0800334 + " displayId: " + displayContent.getDisplayId());
335 }
336 mMagnifedViewport.onRotationChangedLocked();
337 mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
338 }
339
340 public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
341 if (DEBUG_WINDOW_TRANSITIONS) {
342 Slog.i(LOG_TAG, "Window transition: "
343 + AppTransition.appTransitionToString(transition)
344 + " displayId: " + windowState.getDisplayId());
345 }
346 final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
347 if (magnifying) {
348 switch (transition) {
Jorim Jaggif84e2f62018-01-16 14:17:59 +0100349 case WindowManager.TRANSIT_ACTIVITY_OPEN:
350 case WindowManager.TRANSIT_TASK_OPEN:
351 case WindowManager.TRANSIT_TASK_TO_FRONT:
352 case WindowManager.TRANSIT_WALLPAPER_OPEN:
353 case WindowManager.TRANSIT_WALLPAPER_CLOSE:
354 case WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN: {
Svetoslav8e3feb12014-02-24 13:46:47 -0800355 mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED);
356 }
357 }
358 }
359 }
360
361 public void onWindowTransitionLocked(WindowState windowState, int transition) {
362 if (DEBUG_WINDOW_TRANSITIONS) {
363 Slog.i(LOG_TAG, "Window transition: "
364 + AppTransition.appTransitionToString(transition)
365 + " displayId: " + windowState.getDisplayId());
366 }
367 final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
368 final int type = windowState.mAttrs.type;
369 switch (transition) {
370 case WindowManagerPolicy.TRANSIT_ENTER:
371 case WindowManagerPolicy.TRANSIT_SHOW: {
372 if (!magnifying) {
373 break;
374 }
375 switch (type) {
376 case WindowManager.LayoutParams.TYPE_APPLICATION:
Chong Zhangfea963e2016-08-15 17:14:16 -0700377 case WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION:
Svetoslav8e3feb12014-02-24 13:46:47 -0800378 case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
379 case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
380 case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
Wale Ogunwale0a4dc222015-04-14 12:58:42 -0700381 case WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL:
Svetoslav8e3feb12014-02-24 13:46:47 -0800382 case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
383 case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
384 case WindowManager.LayoutParams.TYPE_PHONE:
385 case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
386 case WindowManager.LayoutParams.TYPE_TOAST:
387 case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
Wale Ogunwale5cd907d2017-01-26 14:14:08 -0800388 case WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY:
Svetoslav8e3feb12014-02-24 13:46:47 -0800389 case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
390 case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
391 case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
392 case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
393 case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
Jason Monk8f7f3182015-11-18 16:35:14 -0500394 case WindowManager.LayoutParams.TYPE_QS_DIALOG:
Adrian Roos9a645132014-10-08 02:59:56 +0200395 case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: {
Svetoslav8e3feb12014-02-24 13:46:47 -0800396 Rect magnifiedRegionBounds = mTempRect2;
397 mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(
398 magnifiedRegionBounds);
399 Rect touchableRegionBounds = mTempRect1;
400 windowState.getTouchableRegion(mTempRegion1);
401 mTempRegion1.getBounds(touchableRegionBounds);
402 if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) {
403 mCallbacks.onRectangleOnScreenRequested(
404 touchableRegionBounds.left,
405 touchableRegionBounds.top,
406 touchableRegionBounds.right,
407 touchableRegionBounds.bottom);
408 }
409 } break;
410 } break;
411 }
412 }
413 }
414
415 public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
416 MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
417 if (spec != null && !spec.isNop()) {
Robert Carrb1579c82017-09-05 14:54:47 -0700418 if (!windowState.shouldMagnify()) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800419 return null;
420 }
421 }
422 return spec;
423 }
424
Phil Weaver70439242016-03-10 15:15:49 -0800425 public void getMagnificationRegionLocked(Region outMagnificationRegion) {
Phil Weaver53b690b2017-08-14 17:42:39 -0700426 // Make sure we're working with the most current bounds
427 mMagnifedViewport.recomputeBoundsLocked();
Phil Weaver70439242016-03-10 15:15:49 -0800428 mMagnifedViewport.getMagnificationRegionLocked(outMagnificationRegion);
Alan Viverette59e53a12016-03-28 13:41:32 -0400429 }
430
Svetoslav8e3feb12014-02-24 13:46:47 -0800431 public void destroyLocked() {
432 mMagnifedViewport.destroyWindow();
433 }
434
Phil Weaver251db072017-03-28 08:35:38 -0700435 // Can be called outside of a surface transaction
436 public void showMagnificationBoundsIfNeeded() {
437 mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)
438 .sendToTarget();
439 }
440
Svetoslav8e3feb12014-02-24 13:46:47 -0800441 /** NOTE: This has to be called within a surface transaction. */
442 public void drawMagnifiedRegionBorderIfNeededLocked() {
443 mMagnifedViewport.drawWindowIfNeededLocked();
444 }
445
446 private final class MagnifiedViewport {
447
Svetoslav8e3feb12014-02-24 13:46:47 -0800448 private final SparseArray<WindowState> mTempWindowStates =
449 new SparseArray<WindowState>();
450
451 private final RectF mTempRectF = new RectF();
452
453 private final Point mTempPoint = new Point();
454
455 private final Matrix mTempMatrix = new Matrix();
456
Phil Weaver70439242016-03-10 15:15:49 -0800457 private final Region mMagnificationRegion = new Region();
458 private final Region mOldMagnificationRegion = new Region();
Svetoslav8e3feb12014-02-24 13:46:47 -0800459
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800460 private final Path mCircularPath;
461
Svetoslav8e3feb12014-02-24 13:46:47 -0800462 private final MagnificationSpec mMagnificationSpec = MagnificationSpec.obtain();
463
464 private final WindowManager mWindowManager;
465
Svetoslav7505e332014-08-22 12:14:28 -0700466 private final float mBorderWidth;
Svetoslav8e3feb12014-02-24 13:46:47 -0800467 private final int mHalfBorderWidth;
Svetoslav7505e332014-08-22 12:14:28 -0700468 private final int mDrawBorderInset;
Svetoslav8e3feb12014-02-24 13:46:47 -0800469
470 private final ViewportWindow mWindow;
471
472 private boolean mFullRedrawNeeded;
Robert Carrb1579c82017-09-05 14:54:47 -0700473 private int mTempLayer = 0;
Svetoslav8e3feb12014-02-24 13:46:47 -0800474
475 public MagnifiedViewport() {
476 mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE);
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800477 mBorderWidth = mContext.getResources().getDimension(
478 com.android.internal.R.dimen.accessibility_magnification_indicator_width);
Svetoslav7505e332014-08-22 12:14:28 -0700479 mHalfBorderWidth = (int) Math.ceil(mBorderWidth / 2);
480 mDrawBorderInset = (int) mBorderWidth / 2;
Svetoslav8e3feb12014-02-24 13:46:47 -0800481 mWindow = new ViewportWindow(mContext);
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800482
Adam Powell01f280d2015-05-18 16:07:42 -0700483 if (mContext.getResources().getConfiguration().isScreenRound()) {
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800484 mCircularPath = new Path();
485 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
486 final int centerXY = mTempPoint.x / 2;
487 mCircularPath.addCircle(centerXY, centerXY, centerXY, Path.Direction.CW);
488 } else {
489 mCircularPath = null;
490 }
491
Svetoslav8e3feb12014-02-24 13:46:47 -0800492 recomputeBoundsLocked();
493 }
494
Phil Weaver70439242016-03-10 15:15:49 -0800495 public void getMagnificationRegionLocked(@NonNull Region outMagnificationRegion) {
496 outMagnificationRegion.set(mMagnificationRegion);
Alan Viverette59e53a12016-03-28 13:41:32 -0400497 }
498
Svetoslav8e3feb12014-02-24 13:46:47 -0800499 public void updateMagnificationSpecLocked(MagnificationSpec spec) {
500 if (spec != null) {
501 mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY);
502 } else {
503 mMagnificationSpec.clear();
504 }
505 // If this message is pending we are in a rotation animation and do not want
506 // to show the border. We will do so when the pending message is handled.
Svetoslav7505e332014-08-22 12:14:28 -0700507 if (!mHandler.hasMessages(
508 MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
Casey Burkhardt74922c62017-02-13 12:43:16 -0800509 setMagnifiedRegionBorderShownLocked(
510 isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked(), true);
Svetoslav8e3feb12014-02-24 13:46:47 -0800511 }
512 }
513
514 public void recomputeBoundsLocked() {
515 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
516 final int screenWidth = mTempPoint.x;
517 final int screenHeight = mTempPoint.y;
518
Phil Weaver70439242016-03-10 15:15:49 -0800519 mMagnificationRegion.set(0, 0, 0, 0);
520 final Region availableBounds = mTempRegion1;
521 availableBounds.set(0, 0, screenWidth, screenHeight);
Svetoslav8e3feb12014-02-24 13:46:47 -0800522
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800523 if (mCircularPath != null) {
Phil Weaver70439242016-03-10 15:15:49 -0800524 availableBounds.setPath(mCircularPath, availableBounds);
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800525 }
526
Svetoslav8e3feb12014-02-24 13:46:47 -0800527 Region nonMagnifiedBounds = mTempRegion4;
Svet Ganovb21df802014-09-01 19:06:33 -0700528 nonMagnifiedBounds.set(0, 0, 0, 0);
Svetoslav8e3feb12014-02-24 13:46:47 -0800529
530 SparseArray<WindowState> visibleWindows = mTempWindowStates;
531 visibleWindows.clear();
532 populateWindowsOnScreenLocked(visibleWindows);
533
534 final int visibleWindowCount = visibleWindows.size();
535 for (int i = visibleWindowCount - 1; i >= 0; i--) {
536 WindowState windowState = visibleWindows.valueAt(i);
Phil Weaverd321075e2017-06-13 09:13:35 -0700537 if ((windowState.mAttrs.type == TYPE_MAGNIFICATION_OVERLAY)
538 || ((windowState.mAttrs.privateFlags
Robert Carr132c9f52017-07-31 17:02:30 -0700539 & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0)) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800540 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
Robert Carrb1579c82017-09-05 14:54:47 -0700563 if (windowState.shouldMagnify()) {
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
Robert Carre625fcf2017-09-01 12:36:28 -0700630 * mService.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) {
Robert Carre625fcf2017-09-01 12:36:28 -0700673 final DisplayContent dc = mService.getDefaultDisplayContentLocked();
Robert Carrb1579c82017-09-05 14:54:47 -0700674 mTempLayer = 0;
Wale Ogunwaled1880962016-11-08 10:31:59 -0800675 dc.forAllWindows((w) -> {
676 if (w.isOnScreen() && w.isVisibleLw()
677 && !w.mWinAnimator.mEnterAnimationPending) {
Robert Carrb1579c82017-09-05 14:54:47 -0700678 mTempLayer++;
679 outWindows.put(mTempLayer, w);
Svetoslav8e3feb12014-02-24 13:46:47 -0800680 }
Wale Ogunwaled1880962016-11-08 10:31:59 -0800681 }, false /* traverseTopToBottom */ );
Svetoslav8e3feb12014-02-24 13:46:47 -0800682 }
683
684 private final class ViewportWindow {
685 private static final String SURFACE_TITLE = "Magnification Overlay";
686
Svetoslav8e3feb12014-02-24 13:46:47 -0800687 private final Region mBounds = new Region();
688 private final Rect mDirtyRect = new Rect();
689 private final Paint mPaint = new Paint();
690
Svetoslav8e3feb12014-02-24 13:46:47 -0800691 private final SurfaceControl mSurfaceControl;
692 private final Surface mSurface = new Surface();
693
Svet Ganovb21df802014-09-01 19:06:33 -0700694 private final AnimationController mAnimationController;
695
Svetoslav8e3feb12014-02-24 13:46:47 -0800696 private boolean mShown;
697 private int mAlpha;
698
699 private boolean mInvalidated;
700
701 public ViewportWindow(Context context) {
702 SurfaceControl surfaceControl = null;
703 try {
704 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
Robert Carrb1579c82017-09-05 14:54:47 -0700705 surfaceControl = mService.getDefaultDisplayContentLocked().makeOverlay()
Robert Carre625fcf2017-09-01 12:36:28 -0700706 .setName(SURFACE_TITLE)
707 .setSize(mTempPoint.x, mTempPoint.y) // not a typo
708 .setFormat(PixelFormat.TRANSLUCENT)
709 .build();
Svetoslav8e3feb12014-02-24 13:46:47 -0800710 } catch (OutOfResourcesException oore) {
711 /* ignore */
712 }
713 mSurfaceControl = surfaceControl;
Robert Carre625fcf2017-09-01 12:36:28 -0700714 mSurfaceControl.setLayer(mService.mPolicy.getWindowLayerFromTypeLw(
Phil Weaverd321075e2017-06-13 09:13:35 -0700715 TYPE_MAGNIFICATION_OVERLAY)
Svetoslav8e3feb12014-02-24 13:46:47 -0800716 * WindowManagerService.TYPE_LAYER_MULTIPLIER);
717 mSurfaceControl.setPosition(0, 0);
718 mSurface.copyFrom(mSurfaceControl);
719
Svet Ganovb21df802014-09-01 19:06:33 -0700720 mAnimationController = new AnimationController(context,
Robert Carre625fcf2017-09-01 12:36:28 -0700721 mService.mH.getLooper());
Svet Ganovb21df802014-09-01 19:06:33 -0700722
Svetoslav8e3feb12014-02-24 13:46:47 -0800723 TypedValue typedValue = new TypedValue();
724 context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight,
725 typedValue, true);
Alan Viverette4a357cd2015-03-18 18:37:18 -0700726 final int borderColor = context.getColor(typedValue.resourceId);
Svetoslav8e3feb12014-02-24 13:46:47 -0800727
728 mPaint.setStyle(Paint.Style.STROKE);
729 mPaint.setStrokeWidth(mBorderWidth);
730 mPaint.setColor(borderColor);
731
Svetoslav8e3feb12014-02-24 13:46:47 -0800732 mInvalidated = true;
733 }
734
735 public void setShown(boolean shown, boolean animate) {
Robert Carre625fcf2017-09-01 12:36:28 -0700736 synchronized (mService.mWindowMap) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800737 if (mShown == shown) {
738 return;
739 }
740 mShown = shown;
Svet Ganovb21df802014-09-01 19:06:33 -0700741 mAnimationController.onFrameShownStateChanged(shown, animate);
Svetoslav8e3feb12014-02-24 13:46:47 -0800742 if (DEBUG_VIEWPORT_WINDOW) {
743 Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown);
744 }
745 }
746 }
747
748 @SuppressWarnings("unused")
749 // Called reflectively from an animator.
750 public int getAlpha() {
Robert Carre625fcf2017-09-01 12:36:28 -0700751 synchronized (mService.mWindowMap) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800752 return mAlpha;
753 }
754 }
755
756 public void setAlpha(int alpha) {
Robert Carre625fcf2017-09-01 12:36:28 -0700757 synchronized (mService.mWindowMap) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800758 if (mAlpha == alpha) {
759 return;
760 }
761 mAlpha = alpha;
762 invalidate(null);
763 if (DEBUG_VIEWPORT_WINDOW) {
764 Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha);
765 }
766 }
767 }
768
769 public void setBounds(Region bounds) {
Robert Carre625fcf2017-09-01 12:36:28 -0700770 synchronized (mService.mWindowMap) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800771 if (mBounds.equals(bounds)) {
772 return;
773 }
774 mBounds.set(bounds);
775 invalidate(mDirtyRect);
776 if (DEBUG_VIEWPORT_WINDOW) {
777 Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds);
778 }
779 }
780 }
781
782 public void updateSize() {
Robert Carre625fcf2017-09-01 12:36:28 -0700783 synchronized (mService.mWindowMap) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800784 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
785 mSurfaceControl.setSize(mTempPoint.x, mTempPoint.y);
786 invalidate(mDirtyRect);
787 }
788 }
789
790 public void invalidate(Rect dirtyRect) {
791 if (dirtyRect != null) {
792 mDirtyRect.set(dirtyRect);
793 } else {
794 mDirtyRect.setEmpty();
795 }
796 mInvalidated = true;
Robert Carre625fcf2017-09-01 12:36:28 -0700797 mService.scheduleAnimationLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800798 }
799
800 /** NOTE: This has to be called within a surface transaction. */
801 public void drawIfNeeded() {
Robert Carre625fcf2017-09-01 12:36:28 -0700802 synchronized (mService.mWindowMap) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800803 if (!mInvalidated) {
804 return;
805 }
806 mInvalidated = false;
807 Canvas canvas = null;
808 try {
809 // Empty dirty rectangle means unspecified.
810 if (mDirtyRect.isEmpty()) {
811 mBounds.getBounds(mDirtyRect);
812 }
813 mDirtyRect.inset(- mHalfBorderWidth, - mHalfBorderWidth);
814 canvas = mSurface.lockCanvas(mDirtyRect);
815 if (DEBUG_VIEWPORT_WINDOW) {
816 Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect);
817 }
818 } catch (IllegalArgumentException iae) {
819 /* ignore */
820 } catch (Surface.OutOfResourcesException oore) {
821 /* ignore */
822 }
823 if (canvas == null) {
824 return;
825 }
826 if (DEBUG_VIEWPORT_WINDOW) {
827 Slog.i(LOG_TAG, "Bounds: " + mBounds);
828 }
829 canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
830 mPaint.setAlpha(mAlpha);
831 Path path = mBounds.getBoundaryPath();
832 canvas.drawPath(path, mPaint);
833
834 mSurface.unlockCanvasAndPost(canvas);
835
836 if (mAlpha > 0) {
837 mSurfaceControl.show();
838 } else {
839 mSurfaceControl.hide();
840 }
841 }
842 }
843
844 public void releaseSurface() {
845 mSurfaceControl.release();
846 mSurface.release();
847 }
Svet Ganovb21df802014-09-01 19:06:33 -0700848
849 private final class AnimationController extends Handler {
850 private static final String PROPERTY_NAME_ALPHA = "alpha";
851
852 private static final int MIN_ALPHA = 0;
853 private static final int MAX_ALPHA = 255;
854
855 private static final int MSG_FRAME_SHOWN_STATE_CHANGED = 1;
856
857 private final ValueAnimator mShowHideFrameAnimator;
858
859 public AnimationController(Context context, Looper looper) {
860 super(looper);
861 mShowHideFrameAnimator = ObjectAnimator.ofInt(ViewportWindow.this,
862 PROPERTY_NAME_ALPHA, MIN_ALPHA, MAX_ALPHA);
863
864 Interpolator interpolator = new DecelerateInterpolator(2.5f);
865 final long longAnimationDuration = context.getResources().getInteger(
866 com.android.internal.R.integer.config_longAnimTime);
867
868 mShowHideFrameAnimator.setInterpolator(interpolator);
869 mShowHideFrameAnimator.setDuration(longAnimationDuration);
870 }
871
872 public void onFrameShownStateChanged(boolean shown, boolean animate) {
873 obtainMessage(MSG_FRAME_SHOWN_STATE_CHANGED,
874 shown ? 1 : 0, animate ? 1 : 0).sendToTarget();
875 }
876
877 @Override
878 public void handleMessage(Message message) {
879 switch (message.what) {
880 case MSG_FRAME_SHOWN_STATE_CHANGED: {
881 final boolean shown = message.arg1 == 1;
882 final boolean animate = message.arg2 == 1;
883
884 if (animate) {
885 if (mShowHideFrameAnimator.isRunning()) {
886 mShowHideFrameAnimator.reverse();
887 } else {
888 if (shown) {
889 mShowHideFrameAnimator.start();
890 } else {
891 mShowHideFrameAnimator.reverse();
892 }
893 }
894 } else {
895 mShowHideFrameAnimator.cancel();
896 if (shown) {
897 setAlpha(MAX_ALPHA);
898 } else {
899 setAlpha(MIN_ALPHA);
900 }
901 }
902 } break;
903 }
904 }
905 }
Svetoslav8e3feb12014-02-24 13:46:47 -0800906 }
907 }
908
909 private class MyHandler extends Handler {
Phil Weaver70439242016-03-10 15:15:49 -0800910 public static final int MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED = 1;
Svetoslav8e3feb12014-02-24 13:46:47 -0800911 public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2;
912 public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
913 public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
914 public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
915
916 public MyHandler(Looper looper) {
917 super(looper);
918 }
919
920 @Override
921 public void handleMessage(Message message) {
922 switch (message.what) {
Phil Weaver70439242016-03-10 15:15:49 -0800923 case MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED: {
Alan Viverette214fb682015-11-17 09:47:11 -0500924 final SomeArgs args = (SomeArgs) message.obj;
925 final Region magnifiedBounds = (Region) args.arg1;
Phil Weaver70439242016-03-10 15:15:49 -0800926 mCallbacks.onMagnificationRegionChanged(magnifiedBounds);
Alan Viverette214fb682015-11-17 09:47:11 -0500927 magnifiedBounds.recycle();
Svetoslav8e3feb12014-02-24 13:46:47 -0800928 } break;
929
930 case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: {
931 SomeArgs args = (SomeArgs) message.obj;
932 final int left = args.argi1;
933 final int top = args.argi2;
934 final int right = args.argi3;
935 final int bottom = args.argi4;
936 mCallbacks.onRectangleOnScreenRequested(left, top, right, bottom);
937 args.recycle();
938 } break;
939
940 case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: {
941 mCallbacks.onUserContextChanged();
942 } break;
943
944 case MESSAGE_NOTIFY_ROTATION_CHANGED: {
945 final int rotation = message.arg1;
946 mCallbacks.onRotationChanged(rotation);
947 } break;
948
949 case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
Robert Carre625fcf2017-09-01 12:36:28 -0700950 synchronized (mService.mWindowMap) {
Casey Burkhardt74922c62017-02-13 12:43:16 -0800951 if (mMagnifedViewport.isMagnifyingLocked()
952 || isForceShowingMagnifiableBoundsLocked()) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800953 mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true);
Robert Carre625fcf2017-09-01 12:36:28 -0700954 mService.scheduleAnimationLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800955 }
956 }
957 } break;
958 }
959 }
960 }
961 }
962
963 /**
964 * This class encapsulates the functionality related to computing the windows
965 * reported for accessibility purposes. These windows are all windows a sighted
966 * user can see on the screen.
967 */
968 private static final class WindowsForAccessibilityObserver {
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -0800969 private static final String LOG_TAG = TAG_WITH_CLASS_NAME ?
970 "WindowsForAccessibilityObserver" : TAG_WM;
Svetoslav8e3feb12014-02-24 13:46:47 -0800971
972 private static final boolean DEBUG = false;
973
974 private final SparseArray<WindowState> mTempWindowStates =
975 new SparseArray<WindowState>();
976
977 private final List<WindowInfo> mOldWindows = new ArrayList<WindowInfo>();
978
979 private final Set<IBinder> mTempBinderSet = new ArraySet<IBinder>();
980
981 private final RectF mTempRectF = new RectF();
982
983 private final Matrix mTempMatrix = new Matrix();
984
985 private final Point mTempPoint = new Point();
986
987 private final Rect mTempRect = new Rect();
988
989 private final Region mTempRegion = new Region();
990
991 private final Region mTempRegion1 = new Region();
992
993 private final Context mContext;
994
Robert Carre625fcf2017-09-01 12:36:28 -0700995 private final WindowManagerService mService;
Svetoslav8e3feb12014-02-24 13:46:47 -0800996
997 private final Handler mHandler;
998
999 private final WindowsForAccessibilityCallback mCallback;
1000
Svetoslavf7174e82014-06-12 11:29:35 -07001001 private final long mRecurringAccessibilityEventsIntervalMillis;
1002
Robert Carrb1579c82017-09-05 14:54:47 -07001003 private int mTempLayer = 0;
1004
Svetoslav8e3feb12014-02-24 13:46:47 -08001005 public WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
1006 WindowsForAccessibilityCallback callback) {
1007 mContext = windowManagerService.mContext;
Robert Carre625fcf2017-09-01 12:36:28 -07001008 mService = windowManagerService;
Svetoslav8e3feb12014-02-24 13:46:47 -08001009 mCallback = callback;
Robert Carre625fcf2017-09-01 12:36:28 -07001010 mHandler = new MyHandler(mService.mH.getLooper());
Svetoslavf7174e82014-06-12 11:29:35 -07001011 mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration
1012 .getSendRecurringAccessibilityEventsInterval();
Svetoslav8e3feb12014-02-24 13:46:47 -08001013 computeChangedWindows();
1014 }
1015
Svetoslav3a0d8782014-12-04 12:50:11 -08001016 public void performComputeChangedWindowsNotLocked() {
1017 mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS);
1018 computeChangedWindows();
1019 }
1020
Svetoslavf7174e82014-06-12 11:29:35 -07001021 public void scheduleComputeChangedWindowsLocked() {
Svetoslav3a0d8782014-12-04 12:50:11 -08001022 if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) {
Svetoslavf7174e82014-06-12 11:29:35 -07001023 mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS,
1024 mRecurringAccessibilityEventsIntervalMillis);
1025 }
1026 }
1027
Svetoslav8e3feb12014-02-24 13:46:47 -08001028 public void computeChangedWindows() {
1029 if (DEBUG) {
1030 Slog.i(LOG_TAG, "computeChangedWindows()");
1031 }
1032
Svetoslav3a0d8782014-12-04 12:50:11 -08001033 boolean windowsChanged = false;
1034 List<WindowInfo> windows = new ArrayList<WindowInfo>();
1035
Robert Carre625fcf2017-09-01 12:36:28 -07001036 synchronized (mService.mWindowMap) {
Svetoslavf7174e82014-06-12 11:29:35 -07001037 // Do not send the windows if there is no current focus as
1038 // the window manager is still looking for where to put it.
1039 // We will do the work when we get a focus change callback.
Robert Carre625fcf2017-09-01 12:36:28 -07001040 if (mService.mCurrentFocus == null) {
Svetoslavf7174e82014-06-12 11:29:35 -07001041 return;
1042 }
1043
Svetoslav8e3feb12014-02-24 13:46:47 -08001044 WindowManager windowManager = (WindowManager)
1045 mContext.getSystemService(Context.WINDOW_SERVICE);
1046 windowManager.getDefaultDisplay().getRealSize(mTempPoint);
1047 final int screenWidth = mTempPoint.x;
1048 final int screenHeight = mTempPoint.y;
1049
1050 Region unaccountedSpace = mTempRegion;
1051 unaccountedSpace.set(0, 0, screenWidth, screenHeight);
1052
Wale Ogunwalef7cab102016-10-25 15:25:14 -07001053 final SparseArray<WindowState> visibleWindows = mTempWindowStates;
Svetoslav8e3feb12014-02-24 13:46:47 -08001054 populateVisibleWindowsOnScreenLocked(visibleWindows);
Svetoslav8e3feb12014-02-24 13:46:47 -08001055 Set<IBinder> addedWindows = mTempBinderSet;
1056 addedWindows.clear();
1057
Svetoslavf7174e82014-06-12 11:29:35 -07001058 boolean focusedWindowAdded = false;
1059
Svetoslav8e3feb12014-02-24 13:46:47 -08001060 final int visibleWindowCount = visibleWindows.size();
Allen Hairf20ac2c2016-02-11 17:42:59 -08001061 HashSet<Integer> skipRemainingWindowsForTasks = new HashSet<>();
Svetoslav8e3feb12014-02-24 13:46:47 -08001062 for (int i = visibleWindowCount - 1; i >= 0; i--) {
Alan Viverette9538eea2014-11-13 14:49:20 -08001063 final WindowState windowState = visibleWindows.valueAt(i);
Svetoslav8e3feb12014-02-24 13:46:47 -08001064 final int flags = windowState.mAttrs.flags;
Allen Hairf20ac2c2016-02-11 17:42:59 -08001065 final Task task = windowState.getTask();
1066
1067 // If the window is part of a task that we're finished with - ignore.
1068 if (task != null && skipRemainingWindowsForTasks.contains(task.mTaskId)) {
1069 continue;
1070 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001071
Phil Weaverb2779532017-12-18 17:09:32 -08001072 // Ignore non-touchable windows, except the split-screen divider, which is
1073 // occasionally non-touchable but still useful for identifying split-screen
1074 // mode.
1075 if (((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0)
1076 && (windowState.mAttrs.type != TYPE_DOCK_DIVIDER)) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001077 continue;
1078 }
1079
Alan Viverette9538eea2014-11-13 14:49:20 -08001080 // Compute the bounds in the screen.
1081 final Rect boundsInScreen = mTempRect;
1082 computeWindowBoundsInScreen(windowState, boundsInScreen);
1083
Svetoslav8e3feb12014-02-24 13:46:47 -08001084 // If the window is completely covered by other windows - ignore.
1085 if (unaccountedSpace.quickReject(boundsInScreen)) {
1086 continue;
1087 }
1088
1089 // Add windows of certain types not covered by modal windows.
1090 if (isReportedWindowType(windowState.mAttrs.type)) {
1091 // Add the window to the ones to be reported.
Svetoslavf7174e82014-06-12 11:29:35 -07001092 WindowInfo window = obtainPopulatedWindowInfo(windowState, boundsInScreen);
Robert Carrb1579c82017-09-05 14:54:47 -07001093 window.layer = addedWindows.size();
Svetoslav8e3feb12014-02-24 13:46:47 -08001094 addedWindows.add(window.token);
Svetoslav8e3feb12014-02-24 13:46:47 -08001095 windows.add(window);
Svetoslavf7174e82014-06-12 11:29:35 -07001096 if (windowState.isFocused()) {
1097 focusedWindowAdded = true;
1098 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001099 }
1100
Alan Viveretted0c73f42014-11-18 10:25:04 -08001101 // Account for the space this window takes if the window
1102 // is not an accessibility overlay which does not change
1103 // the reported windows.
1104 if (windowState.mAttrs.type !=
1105 WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
1106 unaccountedSpace.op(boundsInScreen, unaccountedSpace,
1107 Region.Op.REVERSE_DIFFERENCE);
1108 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001109
Allen Hairf20ac2c2016-02-11 17:42:59 -08001110 // If a window is modal it prevents other windows from being touched
Svetoslav8e3feb12014-02-24 13:46:47 -08001111 if ((flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1112 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
Phil Weaverac9ad702016-07-27 18:03:10 -07001113 // Account for all space in the task, whether the windows in it are
1114 // touchable or not. The modal window blocks all touches from the task's
1115 // area.
1116 unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace,
1117 Region.Op.REVERSE_DIFFERENCE);
1118
Allen Hairf20ac2c2016-02-11 17:42:59 -08001119 if (task != null) {
1120 // If the window is associated with a particular task, we can skip the
1121 // rest of the windows for that task.
1122 skipRemainingWindowsForTasks.add(task.mTaskId);
1123 continue;
1124 } else {
1125 // If the window is not associated with a particular task, then it is
1126 // globally modal. In this case we can skip all remaining windows.
1127 break;
1128 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001129 }
Phil Weaverac9ad702016-07-27 18:03:10 -07001130 // We figured out what is touchable for the entire screen - done.
1131 if (unaccountedSpace.isEmpty()) {
1132 break;
1133 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001134 }
1135
Svetoslavf7174e82014-06-12 11:29:35 -07001136 // Always report the focused window.
1137 if (!focusedWindowAdded) {
1138 for (int i = visibleWindowCount - 1; i >= 0; i--) {
1139 WindowState windowState = visibleWindows.valueAt(i);
1140 if (windowState.isFocused()) {
1141 // Compute the bounds in the screen.
1142 Rect boundsInScreen = mTempRect;
1143 computeWindowBoundsInScreen(windowState, boundsInScreen);
1144
1145 // Add the window to the ones to be reported.
1146 WindowInfo window = obtainPopulatedWindowInfo(windowState,
1147 boundsInScreen);
1148 addedWindows.add(window.token);
1149 windows.add(window);
1150 break;
1151 }
1152 }
1153 }
1154
Svetoslav8e3feb12014-02-24 13:46:47 -08001155 // Remove child/parent references to windows that were not added.
1156 final int windowCount = windows.size();
1157 for (int i = 0; i < windowCount; i++) {
1158 WindowInfo window = windows.get(i);
1159 if (!addedWindows.contains(window.parentToken)) {
1160 window.parentToken = null;
1161 }
1162 if (window.childTokens != null) {
1163 final int childTokenCount = window.childTokens.size();
1164 for (int j = childTokenCount - 1; j >= 0; j--) {
1165 if (!addedWindows.contains(window.childTokens.get(j))) {
1166 window.childTokens.remove(j);
1167 }
1168 }
1169 // Leave the child token list if empty.
1170 }
1171 }
1172
1173 visibleWindows.clear();
1174 addedWindows.clear();
1175
1176 // We computed the windows and if they changed notify the client.
Svetoslav8e3feb12014-02-24 13:46:47 -08001177 if (mOldWindows.size() != windows.size()) {
1178 // Different size means something changed.
1179 windowsChanged = true;
1180 } else if (!mOldWindows.isEmpty() || !windows.isEmpty()) {
1181 // Since we always traverse windows from high to low layer
1182 // the old and new windows at the same index should be the
1183 // same, otherwise something changed.
1184 for (int i = 0; i < windowCount; i++) {
1185 WindowInfo oldWindow = mOldWindows.get(i);
1186 WindowInfo newWindow = windows.get(i);
1187 // We do not care for layer changes given the window
1188 // order does not change. This brings no new information
1189 // to the clients.
1190 if (windowChangedNoLayer(oldWindow, newWindow)) {
1191 windowsChanged = true;
1192 break;
1193 }
1194 }
1195 }
1196
1197 if (windowsChanged) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001198 cacheWindows(windows);
Svetoslav8e3feb12014-02-24 13:46:47 -08001199 }
1200 }
Svetoslav3a0d8782014-12-04 12:50:11 -08001201
1202 // Now we do not hold the lock, so send the windows over.
1203 if (windowsChanged) {
1204 if (DEBUG) {
1205 Log.i(LOG_TAG, "Windows changed:" + windows);
1206 }
1207 mCallback.onWindowsForAccessibilityChanged(windows);
1208 } else {
1209 if (DEBUG) {
1210 Log.i(LOG_TAG, "No windows changed.");
1211 }
1212 }
1213
1214 // Recycle the windows as we do not need them.
1215 clearAndRecycleWindows(windows);
Svetoslav8e3feb12014-02-24 13:46:47 -08001216 }
1217
Svetoslavf7174e82014-06-12 11:29:35 -07001218 private void computeWindowBoundsInScreen(WindowState windowState, Rect outBounds) {
1219 // Get the touchable frame.
1220 Region touchableRegion = mTempRegion1;
1221 windowState.getTouchableRegion(touchableRegion);
1222 Rect touchableFrame = mTempRect;
1223 touchableRegion.getBounds(touchableFrame);
1224
1225 // Move to origin as all transforms are captured by the matrix.
1226 RectF windowFrame = mTempRectF;
1227 windowFrame.set(touchableFrame);
1228 windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
1229
1230 // Map the frame to get what appears on the screen.
1231 Matrix matrix = mTempMatrix;
1232 populateTransformationMatrixLocked(windowState, matrix);
1233 matrix.mapRect(windowFrame);
1234
1235 // Got the bounds.
1236 outBounds.set((int) windowFrame.left, (int) windowFrame.top,
1237 (int) windowFrame.right, (int) windowFrame.bottom);
1238 }
1239
Wale Ogunwaleadde52e2016-07-16 13:11:55 -07001240 private static WindowInfo obtainPopulatedWindowInfo(
1241 WindowState windowState, Rect boundsInScreen) {
1242 final WindowInfo window = windowState.getWindowInfo();
Svetoslavf7174e82014-06-12 11:29:35 -07001243 window.boundsInScreen.set(boundsInScreen);
Svetoslavf7174e82014-06-12 11:29:35 -07001244 return window;
1245 }
1246
Svetoslav8e3feb12014-02-24 13:46:47 -08001247 private void cacheWindows(List<WindowInfo> windows) {
1248 final int oldWindowCount = mOldWindows.size();
1249 for (int i = oldWindowCount - 1; i >= 0; i--) {
1250 mOldWindows.remove(i).recycle();
1251 }
1252 final int newWindowCount = windows.size();
1253 for (int i = 0; i < newWindowCount; i++) {
1254 WindowInfo newWindow = windows.get(i);
1255 mOldWindows.add(WindowInfo.obtain(newWindow));
1256 }
1257 }
1258
1259 private boolean windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow) {
1260 if (oldWindow == newWindow) {
1261 return false;
1262 }
Svetoslavf7174e82014-06-12 11:29:35 -07001263 if (oldWindow == null) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001264 return true;
1265 }
Svetoslavf7174e82014-06-12 11:29:35 -07001266 if (newWindow == null) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001267 return true;
1268 }
1269 if (oldWindow.type != newWindow.type) {
1270 return true;
1271 }
1272 if (oldWindow.focused != newWindow.focused) {
1273 return true;
1274 }
1275 if (oldWindow.token == null) {
1276 if (newWindow.token != null) {
1277 return true;
1278 }
1279 } else if (!oldWindow.token.equals(newWindow.token)) {
1280 return true;
1281 }
1282 if (oldWindow.parentToken == null) {
1283 if (newWindow.parentToken != null) {
1284 return true;
1285 }
1286 } else if (!oldWindow.parentToken.equals(newWindow.parentToken)) {
1287 return true;
1288 }
1289 if (!oldWindow.boundsInScreen.equals(newWindow.boundsInScreen)) {
1290 return true;
1291 }
1292 if (oldWindow.childTokens != null && newWindow.childTokens != null
1293 && !oldWindow.childTokens.equals(newWindow.childTokens)) {
1294 return true;
1295 }
Phil Weaver396d5492016-03-22 17:53:50 -07001296 if (!TextUtils.equals(oldWindow.title, newWindow.title)) {
1297 return true;
1298 }
1299 if (oldWindow.accessibilityIdOfAnchor != newWindow.accessibilityIdOfAnchor) {
1300 return true;
1301 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001302 return false;
1303 }
1304
Svetoslav3a0d8782014-12-04 12:50:11 -08001305 private static void clearAndRecycleWindows(List<WindowInfo> windows) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001306 final int windowCount = windows.size();
1307 for (int i = windowCount - 1; i >= 0; i--) {
1308 windows.remove(i).recycle();
1309 }
1310 }
1311
1312 private static boolean isReportedWindowType(int windowType) {
Jorim Jaggi73294b62016-10-26 18:02:36 -07001313 return (windowType != WindowManager.LayoutParams.TYPE_WALLPAPER
Svetoslav8e3feb12014-02-24 13:46:47 -08001314 && windowType != WindowManager.LayoutParams.TYPE_BOOT_PROGRESS
1315 && windowType != WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY
1316 && windowType != WindowManager.LayoutParams.TYPE_DRAG
Selim Cinekf83e8242015-05-19 18:08:14 -07001317 && windowType != WindowManager.LayoutParams.TYPE_INPUT_CONSUMER
Svetoslav8e3feb12014-02-24 13:46:47 -08001318 && windowType != WindowManager.LayoutParams.TYPE_POINTER
Phil Weaverd321075e2017-06-13 09:13:35 -07001319 && windowType != TYPE_MAGNIFICATION_OVERLAY
Svetoslav8e3feb12014-02-24 13:46:47 -08001320 && windowType != WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
1321 && windowType != WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY
1322 && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
1323 }
1324
1325 private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
Robert Carre625fcf2017-09-01 12:36:28 -07001326 final DisplayContent dc = mService.getDefaultDisplayContentLocked();
Robert Carrb1579c82017-09-05 14:54:47 -07001327 mTempLayer = 0;
Wale Ogunwaled1880962016-11-08 10:31:59 -08001328 dc.forAllWindows((w) -> {
1329 if (w.isVisibleLw()) {
Robert Carrb1579c82017-09-05 14:54:47 -07001330 outWindows.put(mTempLayer++, w);
Svetoslav8e3feb12014-02-24 13:46:47 -08001331 }
Wale Ogunwaled1880962016-11-08 10:31:59 -08001332 }, false /* traverseTopToBottom */ );
Svetoslav8e3feb12014-02-24 13:46:47 -08001333 }
1334
1335 private class MyHandler extends Handler {
Svetoslavf7174e82014-06-12 11:29:35 -07001336 public static final int MESSAGE_COMPUTE_CHANGED_WINDOWS = 1;
Svetoslav8e3feb12014-02-24 13:46:47 -08001337
1338 public MyHandler(Looper looper) {
1339 super(looper, null, false);
1340 }
1341
1342 @Override
1343 @SuppressWarnings("unchecked")
1344 public void handleMessage(Message message) {
1345 switch (message.what) {
Svetoslavf7174e82014-06-12 11:29:35 -07001346 case MESSAGE_COMPUTE_CHANGED_WINDOWS: {
1347 computeChangedWindows();
1348 } break;
Svetoslav8e3feb12014-02-24 13:46:47 -08001349 }
1350 }
1351 }
1352 }
1353}