blob: 95cfa2431c54736dc52725a8724d4127a800ee9b [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
19import android.animation.ObjectAnimator;
20import android.animation.ValueAnimator;
21import android.app.Service;
22import android.content.Context;
23import android.graphics.Canvas;
24import android.graphics.Color;
25import android.graphics.Matrix;
26import android.graphics.Paint;
27import android.graphics.Path;
28import android.graphics.PixelFormat;
29import android.graphics.Point;
30import android.graphics.PorterDuff.Mode;
31import android.graphics.Rect;
32import android.graphics.RectF;
33import android.graphics.Region;
34import android.os.Handler;
35import android.os.IBinder;
36import android.os.Looper;
37import android.os.Message;
38import android.util.ArraySet;
39import android.util.Log;
40import android.util.Slog;
41import android.util.SparseArray;
42import android.util.TypedValue;
43import android.view.MagnificationSpec;
44import android.view.Surface;
45import android.view.Surface.OutOfResourcesException;
46import android.view.SurfaceControl;
47import android.view.WindowInfo;
48import android.view.WindowManager;
49import android.view.WindowManagerInternal.MagnificationCallbacks;
50import android.view.WindowManagerInternal.WindowsForAccessibilityCallback;
51import android.view.WindowManagerPolicy;
52import android.view.animation.DecelerateInterpolator;
53import android.view.animation.Interpolator;
54
55import com.android.internal.R;
56import com.android.internal.os.SomeArgs;
57
58import java.util.ArrayList;
59import java.util.List;
60import java.util.Set;
61
62/**
63 * This class contains the accessibility related logic of the window manger.
64 */
65final class AccessibilityController {
66
67 private final WindowManagerService mWindowManagerService;
68
69 private static final float[] sTempFloats = new float[9];
70
71 public AccessibilityController(WindowManagerService service) {
72 mWindowManagerService = service;
73 }
74
75 private DisplayMagnifier mDisplayMagnifier;
76
77 private WindowsForAccessibilityObserver mWindowsForAccessibilityObserver;
78
79 public void setMagnificationCallbacksLocked(MagnificationCallbacks callbacks) {
80 if (callbacks != null) {
81 if (mDisplayMagnifier != null) {
82 throw new IllegalStateException("Magnification callbacks already set!");
83 }
84 mDisplayMagnifier = new DisplayMagnifier(mWindowManagerService, callbacks);
85 } else {
86 if (mDisplayMagnifier == null) {
87 throw new IllegalStateException("Magnification callbacks already cleared!");
88 }
89 mDisplayMagnifier.destroyLocked();
90 mDisplayMagnifier = null;
91 }
92 }
93
94 public void setWindowsForAccessibilityCallback(WindowsForAccessibilityCallback callback) {
95 if (callback != null) {
96 if (mWindowsForAccessibilityObserver != null) {
97 throw new IllegalStateException(
98 "Windows for accessibility callback already set!");
99 }
100 mWindowsForAccessibilityObserver = new WindowsForAccessibilityObserver(
101 mWindowManagerService, callback);
102 } else {
103 if (mWindowsForAccessibilityObserver == null) {
104 throw new IllegalStateException(
105 "Windows for accessibility callback already cleared!");
106 }
107 mWindowsForAccessibilityObserver = null;
108 }
109 }
110
111 public void setMagnificationSpecLocked(MagnificationSpec spec) {
112 if (mDisplayMagnifier != null) {
113 mDisplayMagnifier.setMagnificationSpecLocked(spec);
114 }
115 if (mWindowsForAccessibilityObserver != null) {
116 mWindowsForAccessibilityObserver.computeChangedWindows();
117 }
118 }
119
120 public void onRectangleOnScreenRequestedLocked(Rect rectangle, boolean immediate) {
121 if (mDisplayMagnifier != null) {
122 mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle, immediate);
123 }
124 // Not relevant for the window observer.
125 }
126
127 public void onWindowLayersChangedLocked() {
128 if (mDisplayMagnifier != null) {
129 mDisplayMagnifier.onWindowLayersChangedLocked();
130 }
131 if (mWindowsForAccessibilityObserver != null) {
132 mWindowsForAccessibilityObserver.computeChangedWindows();
133 }
134 }
135
136 public void onRotationChangedLocked(DisplayContent displayContent, int rotation) {
137 if (mDisplayMagnifier != null) {
138 mDisplayMagnifier.onRotationChangedLocked(displayContent, rotation);
139 }
140 if (mWindowsForAccessibilityObserver != null) {
141 mWindowsForAccessibilityObserver.computeChangedWindows();
142 }
143 }
144
145 public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
146 if (mDisplayMagnifier != null) {
147 mDisplayMagnifier.onAppWindowTransitionLocked(windowState, transition);
148 }
149 // Not relevant for the window observer.
150 }
151
152 public void onWindowTransitionLocked(WindowState windowState, int transition) {
153 if (mDisplayMagnifier != null) {
154 mDisplayMagnifier.onWindowTransitionLocked(windowState, transition);
155 }
156 if (mWindowsForAccessibilityObserver != null) {
157 mWindowsForAccessibilityObserver.computeChangedWindows();
158 }
159 }
160
161 public void onWindowFocusChangedLocked() {
162 // Not relevant for the display magnifier.
163
164 if (mWindowsForAccessibilityObserver != null) {
165 mWindowsForAccessibilityObserver.computeChangedWindows();
166 }
167 }
168
Svetoslav4604abc2014-06-10 18:59:30 -0700169
170 public void onSomeWindowResizedOrMoved() {
171 // Not relevant for the display magnifier.
172
173 if (mWindowsForAccessibilityObserver != null) {
174 mWindowsForAccessibilityObserver.computeChangedWindows();
175 }
176 }
177
Svetoslav8e3feb12014-02-24 13:46:47 -0800178 /** NOTE: This has to be called within a surface transaction. */
179 public void drawMagnifiedRegionBorderIfNeededLocked() {
180 if (mDisplayMagnifier != null) {
181 mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked();
182 }
183 // Not relevant for the window observer.
184 }
185
186 public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
187 if (mDisplayMagnifier != null) {
188 return mDisplayMagnifier.getMagnificationSpecForWindowLocked(windowState);
189 }
190 return null;
191 }
192
193 public boolean hasCallbacksLocked() {
194 return (mDisplayMagnifier != null
195 || mWindowsForAccessibilityObserver != null);
196 }
197
198 private static void populateTransformationMatrixLocked(WindowState windowState,
199 Matrix outMatrix) {
200 sTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx;
201 sTempFloats[Matrix.MSKEW_Y] = windowState.mWinAnimator.mDtDx;
202 sTempFloats[Matrix.MSKEW_X] = windowState.mWinAnimator.mDsDy;
203 sTempFloats[Matrix.MSCALE_Y] = windowState.mWinAnimator.mDtDy;
204 sTempFloats[Matrix.MTRANS_X] = windowState.mShownFrame.left;
205 sTempFloats[Matrix.MTRANS_Y] = windowState.mShownFrame.top;
206 sTempFloats[Matrix.MPERSP_0] = 0;
207 sTempFloats[Matrix.MPERSP_1] = 0;
208 sTempFloats[Matrix.MPERSP_2] = 1;
209 outMatrix.setValues(sTempFloats);
210 }
211
212 /**
213 * This class encapsulates the functionality related to display magnification.
214 */
215 private static final class DisplayMagnifier {
216
217 private static final String LOG_TAG = "DisplayMagnifier";
218
219 private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
220 private static final boolean DEBUG_ROTATION = false;
221 private static final boolean DEBUG_LAYERS = false;
222 private static final boolean DEBUG_RECTANGLE_REQUESTED = false;
223 private static final boolean DEBUG_VIEWPORT_WINDOW = false;
224
225 private final Rect mTempRect1 = new Rect();
226 private final Rect mTempRect2 = new Rect();
227
228 private final Region mTempRegion1 = new Region();
229 private final Region mTempRegion2 = new Region();
230 private final Region mTempRegion3 = new Region();
231 private final Region mTempRegion4 = new Region();
232
233 private final Context mContext;
234 private final WindowManagerService mWindowManagerService;
235 private final MagnifiedViewport mMagnifedViewport;
236 private final Handler mHandler;
237
238 private final MagnificationCallbacks mCallbacks;
239
240 private final long mLongAnimationDuration;
241
242 public DisplayMagnifier(WindowManagerService windowManagerService,
243 MagnificationCallbacks callbacks) {
244 mContext = windowManagerService.mContext;
245 mWindowManagerService = windowManagerService;
246 mCallbacks = callbacks;
247 mHandler = new MyHandler(mWindowManagerService.mH.getLooper());
248 mMagnifedViewport = new MagnifiedViewport();
249 mLongAnimationDuration = mContext.getResources().getInteger(
250 com.android.internal.R.integer.config_longAnimTime);
251 }
252
253 public void setMagnificationSpecLocked(MagnificationSpec spec) {
254 mMagnifedViewport.updateMagnificationSpecLocked(spec);
255 mMagnifedViewport.recomputeBoundsLocked();
256 mWindowManagerService.scheduleAnimationLocked();
257 }
258
259 public void onRectangleOnScreenRequestedLocked(Rect rectangle, boolean immediate) {
260 if (DEBUG_RECTANGLE_REQUESTED) {
261 Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
262 }
263 if (!mMagnifedViewport.isMagnifyingLocked()) {
264 return;
265 }
266 Rect magnifiedRegionBounds = mTempRect2;
267 mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds);
268 if (magnifiedRegionBounds.contains(rectangle)) {
269 return;
270 }
271 SomeArgs args = SomeArgs.obtain();
272 args.argi1 = rectangle.left;
273 args.argi2 = rectangle.top;
274 args.argi3 = rectangle.right;
275 args.argi4 = rectangle.bottom;
276 mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED,
277 args).sendToTarget();
278 }
279
280 public void onWindowLayersChangedLocked() {
281 if (DEBUG_LAYERS) {
282 Slog.i(LOG_TAG, "Layers changed.");
283 }
284 mMagnifedViewport.recomputeBoundsLocked();
285 mWindowManagerService.scheduleAnimationLocked();
286 }
287
288 public void onRotationChangedLocked(DisplayContent displayContent, int rotation) {
289 if (DEBUG_ROTATION) {
290 Slog.i(LOG_TAG, "Rotaton: " + Surface.rotationToString(rotation)
291 + " displayId: " + displayContent.getDisplayId());
292 }
293 mMagnifedViewport.onRotationChangedLocked();
294 mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
295 }
296
297 public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
298 if (DEBUG_WINDOW_TRANSITIONS) {
299 Slog.i(LOG_TAG, "Window transition: "
300 + AppTransition.appTransitionToString(transition)
301 + " displayId: " + windowState.getDisplayId());
302 }
303 final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
304 if (magnifying) {
305 switch (transition) {
306 case AppTransition.TRANSIT_ACTIVITY_OPEN:
307 case AppTransition.TRANSIT_TASK_OPEN:
308 case AppTransition.TRANSIT_TASK_TO_FRONT:
309 case AppTransition.TRANSIT_WALLPAPER_OPEN:
310 case AppTransition.TRANSIT_WALLPAPER_CLOSE:
311 case AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN: {
312 mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED);
313 }
314 }
315 }
316 }
317
318 public void onWindowTransitionLocked(WindowState windowState, int transition) {
319 if (DEBUG_WINDOW_TRANSITIONS) {
320 Slog.i(LOG_TAG, "Window transition: "
321 + AppTransition.appTransitionToString(transition)
322 + " displayId: " + windowState.getDisplayId());
323 }
324 final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
325 final int type = windowState.mAttrs.type;
326 switch (transition) {
327 case WindowManagerPolicy.TRANSIT_ENTER:
328 case WindowManagerPolicy.TRANSIT_SHOW: {
329 if (!magnifying) {
330 break;
331 }
332 switch (type) {
333 case WindowManager.LayoutParams.TYPE_APPLICATION:
334 case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
335 case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
336 case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
337 case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
338 case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
339 case WindowManager.LayoutParams.TYPE_PHONE:
340 case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
341 case WindowManager.LayoutParams.TYPE_TOAST:
342 case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
343 case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
344 case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
345 case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
346 case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
347 case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
348 case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL:
349 case WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY: {
350 Rect magnifiedRegionBounds = mTempRect2;
351 mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(
352 magnifiedRegionBounds);
353 Rect touchableRegionBounds = mTempRect1;
354 windowState.getTouchableRegion(mTempRegion1);
355 mTempRegion1.getBounds(touchableRegionBounds);
356 if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) {
357 mCallbacks.onRectangleOnScreenRequested(
358 touchableRegionBounds.left,
359 touchableRegionBounds.top,
360 touchableRegionBounds.right,
361 touchableRegionBounds.bottom);
362 }
363 } break;
364 } break;
365 }
366 }
367 }
368
369 public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
370 MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
371 if (spec != null && !spec.isNop()) {
372 WindowManagerPolicy policy = mWindowManagerService.mPolicy;
373 final int windowType = windowState.mAttrs.type;
374 if (!policy.isTopLevelWindow(windowType) && windowState.mAttachedWindow != null
375 && !policy.canMagnifyWindow(windowType)) {
376 return null;
377 }
378 if (!policy.canMagnifyWindow(windowState.mAttrs.type)) {
379 return null;
380 }
381 }
382 return spec;
383 }
384
385 public void destroyLocked() {
386 mMagnifedViewport.destroyWindow();
387 }
388
389 /** NOTE: This has to be called within a surface transaction. */
390 public void drawMagnifiedRegionBorderIfNeededLocked() {
391 mMagnifedViewport.drawWindowIfNeededLocked();
392 }
393
394 private final class MagnifiedViewport {
395
396 private static final int DEFAUTLT_BORDER_WIDTH_DIP = 5;
397
398 private final SparseArray<WindowState> mTempWindowStates =
399 new SparseArray<WindowState>();
400
401 private final RectF mTempRectF = new RectF();
402
403 private final Point mTempPoint = new Point();
404
405 private final Matrix mTempMatrix = new Matrix();
406
407 private final Region mMagnifiedBounds = new Region();
408 private final Region mOldMagnifiedBounds = new Region();
409
410 private final MagnificationSpec mMagnificationSpec = MagnificationSpec.obtain();
411
412 private final WindowManager mWindowManager;
413
414 private final int mBorderWidth;
415 private final int mHalfBorderWidth;
416
417 private final ViewportWindow mWindow;
418
419 private boolean mFullRedrawNeeded;
420
421 public MagnifiedViewport() {
422 mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE);
423 mBorderWidth = (int) TypedValue.applyDimension(
424 TypedValue.COMPLEX_UNIT_DIP, DEFAUTLT_BORDER_WIDTH_DIP,
425 mContext.getResources().getDisplayMetrics());
426 mHalfBorderWidth = (int) (mBorderWidth + 0.5) / 2;
427 mWindow = new ViewportWindow(mContext);
428 recomputeBoundsLocked();
429 }
430
431 public void updateMagnificationSpecLocked(MagnificationSpec spec) {
432 if (spec != null) {
433 mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY);
434 } else {
435 mMagnificationSpec.clear();
436 }
437 // If this message is pending we are in a rotation animation and do not want
438 // to show the border. We will do so when the pending message is handled.
439 if (!mHandler.hasMessages(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
440 setMagnifiedRegionBorderShownLocked(isMagnifyingLocked(), true);
441 }
442 }
443
444 public void recomputeBoundsLocked() {
445 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
446 final int screenWidth = mTempPoint.x;
447 final int screenHeight = mTempPoint.y;
448
449 Region magnifiedBounds = mMagnifiedBounds;
450 magnifiedBounds.set(0, 0, 0, 0);
451
452 Region availableBounds = mTempRegion1;
453 availableBounds.set(0, 0, screenWidth, screenHeight);
454
455 Region nonMagnifiedBounds = mTempRegion4;
456 nonMagnifiedBounds.set(0, 0, 0, 0);
457
458 SparseArray<WindowState> visibleWindows = mTempWindowStates;
459 visibleWindows.clear();
460 populateWindowsOnScreenLocked(visibleWindows);
461
462 final int visibleWindowCount = visibleWindows.size();
463 for (int i = visibleWindowCount - 1; i >= 0; i--) {
464 WindowState windowState = visibleWindows.valueAt(i);
465 if (windowState.mAttrs.type == WindowManager
466 .LayoutParams.TYPE_MAGNIFICATION_OVERLAY) {
467 continue;
468 }
469
470 Region windowBounds = mTempRegion2;
471 Matrix matrix = mTempMatrix;
472 populateTransformationMatrixLocked(windowState, matrix);
473 RectF windowFrame = mTempRectF;
474
475 if (mWindowManagerService.mPolicy.canMagnifyWindow(windowState.mAttrs.type)) {
476 windowFrame.set(windowState.mFrame);
477 windowFrame.offset(-windowFrame.left, -windowFrame.top);
478 matrix.mapRect(windowFrame);
479 windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
480 (int) windowFrame.right, (int) windowFrame.bottom);
481 magnifiedBounds.op(windowBounds, Region.Op.UNION);
482 magnifiedBounds.op(availableBounds, Region.Op.INTERSECT);
483 } else {
484 Region touchableRegion = mTempRegion3;
485 windowState.getTouchableRegion(touchableRegion);
486 Rect touchableFrame = mTempRect1;
487 touchableRegion.getBounds(touchableFrame);
488 windowFrame.set(touchableFrame);
489 windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
490 matrix.mapRect(windowFrame);
491 windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
492 (int) windowFrame.right, (int) windowFrame.bottom);
493 nonMagnifiedBounds.op(windowBounds, Region.Op.UNION);
494 windowBounds.op(magnifiedBounds, Region.Op.DIFFERENCE);
495 availableBounds.op(windowBounds, Region.Op.DIFFERENCE);
496 }
497
498 Region accountedBounds = mTempRegion2;
499 accountedBounds.set(magnifiedBounds);
500 accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION);
501 accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT);
502
503 if (accountedBounds.isRect()) {
504 Rect accountedFrame = mTempRect1;
505 accountedBounds.getBounds(accountedFrame);
506 if (accountedFrame.width() == screenWidth
507 && accountedFrame.height() == screenHeight) {
508 break;
509 }
510 }
511 }
512
513 visibleWindows.clear();
514
515 magnifiedBounds.op(mHalfBorderWidth, mHalfBorderWidth,
516 screenWidth - mHalfBorderWidth, screenHeight - mHalfBorderWidth,
517 Region.Op.INTERSECT);
518
519 if (!mOldMagnifiedBounds.equals(magnifiedBounds)) {
520 Region bounds = Region.obtain();
521 bounds.set(magnifiedBounds);
522 mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED,
523 bounds).sendToTarget();
524
525 mWindow.setBounds(magnifiedBounds);
526 Rect dirtyRect = mTempRect1;
527 if (mFullRedrawNeeded) {
528 mFullRedrawNeeded = false;
529 dirtyRect.set(mHalfBorderWidth, mHalfBorderWidth,
530 screenWidth - mHalfBorderWidth, screenHeight - mHalfBorderWidth);
531 mWindow.invalidate(dirtyRect);
532 } else {
533 Region dirtyRegion = mTempRegion3;
534 dirtyRegion.set(magnifiedBounds);
535 dirtyRegion.op(mOldMagnifiedBounds, Region.Op.UNION);
536 dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT);
537 dirtyRegion.getBounds(dirtyRect);
538 mWindow.invalidate(dirtyRect);
539 }
540
541 mOldMagnifiedBounds.set(magnifiedBounds);
542 }
543 }
544
545 public void onRotationChangedLocked() {
546 // If we are magnifying, hide the magnified border window immediately so
547 // the user does not see strange artifacts during rotation. The screenshot
548 // used for rotation has already the border. After the rotation is complete
549 // we will show the border.
550 if (isMagnifyingLocked()) {
551 setMagnifiedRegionBorderShownLocked(false, false);
552 final long delay = (long) (mLongAnimationDuration
Dianne Hackborneb94fa72014-06-03 17:48:12 -0700553 * mWindowManagerService.getWindowAnimationScaleLocked());
Svetoslav8e3feb12014-02-24 13:46:47 -0800554 Message message = mHandler.obtainMessage(
555 MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED);
556 mHandler.sendMessageDelayed(message, delay);
557 }
558 recomputeBoundsLocked();
559 mWindow.updateSize();
560 }
561
562 public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) {
563 if (shown) {
564 mFullRedrawNeeded = true;
565 mOldMagnifiedBounds.set(0, 0, 0, 0);
566 }
567 mWindow.setShown(shown, animate);
568 }
569
570 public void getMagnifiedFrameInContentCoordsLocked(Rect rect) {
571 MagnificationSpec spec = mMagnificationSpec;
572 mMagnifiedBounds.getBounds(rect);
573 rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
574 rect.scale(1.0f / spec.scale);
575 }
576
577 public boolean isMagnifyingLocked() {
578 return mMagnificationSpec.scale > 1.0f;
579 }
580
581 public MagnificationSpec getMagnificationSpecLocked() {
582 return mMagnificationSpec;
583 }
584
585 /** NOTE: This has to be called within a surface transaction. */
586 public void drawWindowIfNeededLocked() {
587 recomputeBoundsLocked();
588 mWindow.drawIfNeeded();
589 }
590
591 public void destroyWindow() {
592 mWindow.releaseSurface();
593 }
594
595 private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
596 DisplayContent displayContent = mWindowManagerService
597 .getDefaultDisplayContentLocked();
598 WindowList windowList = displayContent.getWindowList();
599 final int windowCount = windowList.size();
600 for (int i = 0; i < windowCount; i++) {
601 WindowState windowState = windowList.get(i);
602 if ((windowState.isOnScreen() || windowState.mAttrs.type == WindowManager
603 .LayoutParams.TYPE_UNIVERSE_BACKGROUND)
604 && !windowState.mWinAnimator.mEnterAnimationPending) {
605 outWindows.put(windowState.mLayer, windowState);
606 }
607 }
608 }
609
610 private final class ViewportWindow {
611 private static final String SURFACE_TITLE = "Magnification Overlay";
612
613 private static final String PROPERTY_NAME_ALPHA = "alpha";
614
615 private static final int MIN_ALPHA = 0;
616 private static final int MAX_ALPHA = 255;
617
618 private final Region mBounds = new Region();
619 private final Rect mDirtyRect = new Rect();
620 private final Paint mPaint = new Paint();
621
622 private final ValueAnimator mShowHideFrameAnimator;
623 private final SurfaceControl mSurfaceControl;
624 private final Surface mSurface = new Surface();
625
626 private boolean mShown;
627 private int mAlpha;
628
629 private boolean mInvalidated;
630
631 public ViewportWindow(Context context) {
632 SurfaceControl surfaceControl = null;
633 try {
634 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
635 surfaceControl = new SurfaceControl(mWindowManagerService.mFxSession,
636 SURFACE_TITLE, mTempPoint.x, mTempPoint.y, PixelFormat.TRANSLUCENT,
637 SurfaceControl.HIDDEN);
638 } catch (OutOfResourcesException oore) {
639 /* ignore */
640 }
641 mSurfaceControl = surfaceControl;
642 mSurfaceControl.setLayerStack(mWindowManager.getDefaultDisplay()
643 .getLayerStack());
644 mSurfaceControl.setLayer(mWindowManagerService.mPolicy.windowTypeToLayerLw(
645 WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY)
646 * WindowManagerService.TYPE_LAYER_MULTIPLIER);
647 mSurfaceControl.setPosition(0, 0);
648 mSurface.copyFrom(mSurfaceControl);
649
650 TypedValue typedValue = new TypedValue();
651 context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight,
652 typedValue, true);
653 final int borderColor = context.getResources().getColor(typedValue.resourceId);
654
655 mPaint.setStyle(Paint.Style.STROKE);
656 mPaint.setStrokeWidth(mBorderWidth);
657 mPaint.setColor(borderColor);
658
659 Interpolator interpolator = new DecelerateInterpolator(2.5f);
660 final long longAnimationDuration = context.getResources().getInteger(
661 com.android.internal.R.integer.config_longAnimTime);
662
663 mShowHideFrameAnimator = ObjectAnimator.ofInt(this, PROPERTY_NAME_ALPHA,
664 MIN_ALPHA, MAX_ALPHA);
665 mShowHideFrameAnimator.setInterpolator(interpolator);
666 mShowHideFrameAnimator.setDuration(longAnimationDuration);
667 mInvalidated = true;
668 }
669
670 public void setShown(boolean shown, boolean animate) {
671 synchronized (mWindowManagerService.mWindowMap) {
672 if (mShown == shown) {
673 return;
674 }
675 mShown = shown;
676 if (animate) {
677 if (mShowHideFrameAnimator.isRunning()) {
678 mShowHideFrameAnimator.reverse();
679 } else {
680 if (shown) {
681 mShowHideFrameAnimator.start();
682 } else {
683 mShowHideFrameAnimator.reverse();
684 }
685 }
686 } else {
687 mShowHideFrameAnimator.cancel();
688 if (shown) {
689 setAlpha(MAX_ALPHA);
690 } else {
691 setAlpha(MIN_ALPHA);
692 }
693 }
694 if (DEBUG_VIEWPORT_WINDOW) {
695 Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown);
696 }
697 }
698 }
699
700 @SuppressWarnings("unused")
701 // Called reflectively from an animator.
702 public int getAlpha() {
703 synchronized (mWindowManagerService.mWindowMap) {
704 return mAlpha;
705 }
706 }
707
708 public void setAlpha(int alpha) {
709 synchronized (mWindowManagerService.mWindowMap) {
710 if (mAlpha == alpha) {
711 return;
712 }
713 mAlpha = alpha;
714 invalidate(null);
715 if (DEBUG_VIEWPORT_WINDOW) {
716 Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha);
717 }
718 }
719 }
720
721 public void setBounds(Region bounds) {
722 synchronized (mWindowManagerService.mWindowMap) {
723 if (mBounds.equals(bounds)) {
724 return;
725 }
726 mBounds.set(bounds);
727 invalidate(mDirtyRect);
728 if (DEBUG_VIEWPORT_WINDOW) {
729 Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds);
730 }
731 }
732 }
733
734 public void updateSize() {
735 synchronized (mWindowManagerService.mWindowMap) {
736 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
737 mSurfaceControl.setSize(mTempPoint.x, mTempPoint.y);
738 invalidate(mDirtyRect);
739 }
740 }
741
742 public void invalidate(Rect dirtyRect) {
743 if (dirtyRect != null) {
744 mDirtyRect.set(dirtyRect);
745 } else {
746 mDirtyRect.setEmpty();
747 }
748 mInvalidated = true;
749 mWindowManagerService.scheduleAnimationLocked();
750 }
751
752 /** NOTE: This has to be called within a surface transaction. */
753 public void drawIfNeeded() {
754 synchronized (mWindowManagerService.mWindowMap) {
755 if (!mInvalidated) {
756 return;
757 }
758 mInvalidated = false;
759 Canvas canvas = null;
760 try {
761 // Empty dirty rectangle means unspecified.
762 if (mDirtyRect.isEmpty()) {
763 mBounds.getBounds(mDirtyRect);
764 }
765 mDirtyRect.inset(- mHalfBorderWidth, - mHalfBorderWidth);
766 canvas = mSurface.lockCanvas(mDirtyRect);
767 if (DEBUG_VIEWPORT_WINDOW) {
768 Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect);
769 }
770 } catch (IllegalArgumentException iae) {
771 /* ignore */
772 } catch (Surface.OutOfResourcesException oore) {
773 /* ignore */
774 }
775 if (canvas == null) {
776 return;
777 }
778 if (DEBUG_VIEWPORT_WINDOW) {
779 Slog.i(LOG_TAG, "Bounds: " + mBounds);
780 }
781 canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
782 mPaint.setAlpha(mAlpha);
783 Path path = mBounds.getBoundaryPath();
784 canvas.drawPath(path, mPaint);
785
786 mSurface.unlockCanvasAndPost(canvas);
787
788 if (mAlpha > 0) {
789 mSurfaceControl.show();
790 } else {
791 mSurfaceControl.hide();
792 }
793 }
794 }
795
796 public void releaseSurface() {
797 mSurfaceControl.release();
798 mSurface.release();
799 }
800 }
801 }
802
803 private class MyHandler extends Handler {
804 public static final int MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED = 1;
805 public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2;
806 public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
807 public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
808 public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
809
810 public MyHandler(Looper looper) {
811 super(looper);
812 }
813
814 @Override
815 public void handleMessage(Message message) {
816 switch (message.what) {
817 case MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED: {
818 Region bounds = (Region) message.obj;
819 mCallbacks.onMagnifedBoundsChanged(bounds);
820 bounds.recycle();
821 } break;
822
823 case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: {
824 SomeArgs args = (SomeArgs) message.obj;
825 final int left = args.argi1;
826 final int top = args.argi2;
827 final int right = args.argi3;
828 final int bottom = args.argi4;
829 mCallbacks.onRectangleOnScreenRequested(left, top, right, bottom);
830 args.recycle();
831 } break;
832
833 case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: {
834 mCallbacks.onUserContextChanged();
835 } break;
836
837 case MESSAGE_NOTIFY_ROTATION_CHANGED: {
838 final int rotation = message.arg1;
839 mCallbacks.onRotationChanged(rotation);
840 } break;
841
842 case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
843 synchronized (mWindowManagerService.mWindowMap) {
844 if (mMagnifedViewport.isMagnifyingLocked()) {
845 mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true);
846 mWindowManagerService.scheduleAnimationLocked();
847 }
848 }
849 } break;
850 }
851 }
852 }
853 }
854
855 /**
856 * This class encapsulates the functionality related to computing the windows
857 * reported for accessibility purposes. These windows are all windows a sighted
858 * user can see on the screen.
859 */
860 private static final class WindowsForAccessibilityObserver {
861 private static final String LOG_TAG = "WindowsForAccessibilityObserver";
862
863 private static final boolean DEBUG = false;
864
865 private final SparseArray<WindowState> mTempWindowStates =
866 new SparseArray<WindowState>();
867
868 private final List<WindowInfo> mOldWindows = new ArrayList<WindowInfo>();
869
870 private final Set<IBinder> mTempBinderSet = new ArraySet<IBinder>();
871
872 private final RectF mTempRectF = new RectF();
873
874 private final Matrix mTempMatrix = new Matrix();
875
876 private final Point mTempPoint = new Point();
877
878 private final Rect mTempRect = new Rect();
879
880 private final Region mTempRegion = new Region();
881
882 private final Region mTempRegion1 = new Region();
883
884 private final Context mContext;
885
886 private final WindowManagerService mWindowManagerService;
887
888 private final Handler mHandler;
889
890 private final WindowsForAccessibilityCallback mCallback;
891
892 public WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
893 WindowsForAccessibilityCallback callback) {
894 mContext = windowManagerService.mContext;
895 mWindowManagerService = windowManagerService;
896 mCallback = callback;
897 mHandler = new MyHandler(mWindowManagerService.mH.getLooper());
898 computeChangedWindows();
899 }
900
901 public void computeChangedWindows() {
902 if (DEBUG) {
903 Slog.i(LOG_TAG, "computeChangedWindows()");
904 }
905
906 synchronized (mWindowManagerService.mWindowMap) {
907 WindowManager windowManager = (WindowManager)
908 mContext.getSystemService(Context.WINDOW_SERVICE);
909 windowManager.getDefaultDisplay().getRealSize(mTempPoint);
910 final int screenWidth = mTempPoint.x;
911 final int screenHeight = mTempPoint.y;
912
913 Region unaccountedSpace = mTempRegion;
914 unaccountedSpace.set(0, 0, screenWidth, screenHeight);
915
916 SparseArray<WindowState> visibleWindows = mTempWindowStates;
917 populateVisibleWindowsOnScreenLocked(visibleWindows);
918
919 List<WindowInfo> windows = new ArrayList<WindowInfo>();
920
921 Set<IBinder> addedWindows = mTempBinderSet;
922 addedWindows.clear();
923
924 final int visibleWindowCount = visibleWindows.size();
925 for (int i = visibleWindowCount - 1; i >= 0; i--) {
926 WindowState windowState = visibleWindows.valueAt(i);
927 // Compute the window touchable frame as shown on the screen.
928
929 // Get the touchable frame.
930 Region touchableRegion = mTempRegion1;
931 windowState.getTouchableRegion(touchableRegion);
932 Rect touchableFrame = mTempRect;
933 touchableRegion.getBounds(touchableFrame);
934
935 // Move to origin as all transforms are captured by the matrix.
936 RectF windowFrame = mTempRectF;
937 windowFrame.set(touchableFrame);
938 windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
939
940 // Map the frame to get what appears on the screen.
941 Matrix matrix = mTempMatrix;
942 populateTransformationMatrixLocked(windowState, matrix);
943 matrix.mapRect(windowFrame);
944
945 // Got the bounds.
946 Rect boundsInScreen = mTempRect;
947 boundsInScreen.set((int) windowFrame.left, (int) windowFrame.top,
948 (int) windowFrame.right, (int) windowFrame.bottom);
949
950 final int flags = windowState.mAttrs.flags;
951
952 // If the window is not touchable, do not report it but take into account
953 // the space it takes since the content behind it cannot be touched.
954 if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) == 1) {
955 unaccountedSpace.op(boundsInScreen, unaccountedSpace,
956 Region.Op.DIFFERENCE);
957 continue;
958 }
959
960 // If the window is completely covered by other windows - ignore.
961 if (unaccountedSpace.quickReject(boundsInScreen)) {
962 continue;
963 }
964
965 // Add windows of certain types not covered by modal windows.
966 if (isReportedWindowType(windowState.mAttrs.type)) {
967 // Add the window to the ones to be reported.
968 WindowInfo window = WindowInfo.obtain();
969 window.type = windowState.mAttrs.type;
970 window.layer = windowState.mLayer;
971 window.token = windowState.mClient.asBinder();
972
973 addedWindows.add(window.token);
974
975 WindowState attachedWindow = windowState.mAttachedWindow;
976 if (attachedWindow != null) {
977 window.parentToken = attachedWindow.mClient.asBinder();
978 }
979
980 window.focused = windowState.isFocused();
981 window.boundsInScreen.set(boundsInScreen);
982
983 final int childCount = windowState.mChildWindows.size();
984 if (childCount > 0) {
985 if (window.childTokens == null) {
986 window.childTokens = new ArrayList<IBinder>();
987 }
988 for (int j = 0; j < childCount; j++) {
989 WindowState child = windowState.mChildWindows.get(j);
990 window.childTokens.add(child.mClient.asBinder());
991 }
992 }
993
994 windows.add(window);
995 }
996
997 // Account for the space this window takes.
998 unaccountedSpace.op(boundsInScreen, unaccountedSpace,
999 Region.Op.REVERSE_DIFFERENCE);
1000
1001 // We figured out what is touchable for the entire screen - done.
1002 if (unaccountedSpace.isEmpty()) {
1003 break;
1004 }
1005
1006 // If a window is modal, no other below can be touched - done.
1007 if ((flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1008 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
1009 break;
1010 }
1011 }
1012
1013 // Remove child/parent references to windows that were not added.
1014 final int windowCount = windows.size();
1015 for (int i = 0; i < windowCount; i++) {
1016 WindowInfo window = windows.get(i);
1017 if (!addedWindows.contains(window.parentToken)) {
1018 window.parentToken = null;
1019 }
1020 if (window.childTokens != null) {
1021 final int childTokenCount = window.childTokens.size();
1022 for (int j = childTokenCount - 1; j >= 0; j--) {
1023 if (!addedWindows.contains(window.childTokens.get(j))) {
1024 window.childTokens.remove(j);
1025 }
1026 }
1027 // Leave the child token list if empty.
1028 }
1029 }
1030
1031 visibleWindows.clear();
1032 addedWindows.clear();
1033
1034 // We computed the windows and if they changed notify the client.
1035 boolean windowsChanged = false;
1036 if (mOldWindows.size() != windows.size()) {
1037 // Different size means something changed.
1038 windowsChanged = true;
1039 } else if (!mOldWindows.isEmpty() || !windows.isEmpty()) {
1040 // Since we always traverse windows from high to low layer
1041 // the old and new windows at the same index should be the
1042 // same, otherwise something changed.
1043 for (int i = 0; i < windowCount; i++) {
1044 WindowInfo oldWindow = mOldWindows.get(i);
1045 WindowInfo newWindow = windows.get(i);
1046 // We do not care for layer changes given the window
1047 // order does not change. This brings no new information
1048 // to the clients.
1049 if (windowChangedNoLayer(oldWindow, newWindow)) {
1050 windowsChanged = true;
1051 break;
1052 }
1053 }
1054 }
1055
1056 if (windowsChanged) {
1057 if (DEBUG) {
1058 Log.i(LOG_TAG, "Windows changed:" + windows);
1059 }
1060 // Remember the old windows to detect changes.
1061 cacheWindows(windows);
1062 // Announce the change.
1063 mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED,
1064 windows).sendToTarget();
1065 } else {
1066 if (DEBUG) {
1067 Log.i(LOG_TAG, "No windows changed.");
1068 }
1069 // Recycle the nodes as we do not need them.
1070 clearAndRecycleWindows(windows);
1071 }
1072 }
1073 }
1074
1075 private void cacheWindows(List<WindowInfo> windows) {
1076 final int oldWindowCount = mOldWindows.size();
1077 for (int i = oldWindowCount - 1; i >= 0; i--) {
1078 mOldWindows.remove(i).recycle();
1079 }
1080 final int newWindowCount = windows.size();
1081 for (int i = 0; i < newWindowCount; i++) {
1082 WindowInfo newWindow = windows.get(i);
1083 mOldWindows.add(WindowInfo.obtain(newWindow));
1084 }
1085 }
1086
1087 private boolean windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow) {
1088 if (oldWindow == newWindow) {
1089 return false;
1090 }
1091 if (oldWindow == null && newWindow != null) {
1092 return true;
1093 }
1094 if (oldWindow != null && newWindow == null) {
1095 return true;
1096 }
1097 if (oldWindow.type != newWindow.type) {
1098 return true;
1099 }
1100 if (oldWindow.focused != newWindow.focused) {
1101 return true;
1102 }
1103 if (oldWindow.token == null) {
1104 if (newWindow.token != null) {
1105 return true;
1106 }
1107 } else if (!oldWindow.token.equals(newWindow.token)) {
1108 return true;
1109 }
1110 if (oldWindow.parentToken == null) {
1111 if (newWindow.parentToken != null) {
1112 return true;
1113 }
1114 } else if (!oldWindow.parentToken.equals(newWindow.parentToken)) {
1115 return true;
1116 }
1117 if (!oldWindow.boundsInScreen.equals(newWindow.boundsInScreen)) {
1118 return true;
1119 }
1120 if (oldWindow.childTokens != null && newWindow.childTokens != null
1121 && !oldWindow.childTokens.equals(newWindow.childTokens)) {
1122 return true;
1123 }
1124 return false;
1125 }
1126
1127 private void clearAndRecycleWindows(List<WindowInfo> windows) {
1128 final int windowCount = windows.size();
1129 for (int i = windowCount - 1; i >= 0; i--) {
1130 windows.remove(i).recycle();
1131 }
1132 }
1133
1134 private static boolean isReportedWindowType(int windowType) {
1135 return (windowType != WindowManager.LayoutParams.TYPE_KEYGUARD_SCRIM
1136 && windowType != WindowManager.LayoutParams.TYPE_WALLPAPER
1137 && windowType != WindowManager.LayoutParams.TYPE_BOOT_PROGRESS
1138 && windowType != WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY
1139 && windowType != WindowManager.LayoutParams.TYPE_DRAG
1140 && windowType != WindowManager.LayoutParams.TYPE_HIDDEN_NAV_CONSUMER
1141 && windowType != WindowManager.LayoutParams.TYPE_POINTER
1142 && windowType != WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND
1143 && windowType != WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY
1144 && windowType != WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
1145 && windowType != WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY
1146 && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
1147 }
1148
1149 private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
1150 DisplayContent displayContent = mWindowManagerService
1151 .getDefaultDisplayContentLocked();
1152 WindowList windowList = displayContent.getWindowList();
1153 final int windowCount = windowList.size();
1154 for (int i = 0; i < windowCount; i++) {
1155 WindowState windowState = windowList.get(i);
1156 if (windowState.isVisibleLw()) {
1157 outWindows.put(windowState.mLayer, windowState);
1158 }
1159 }
1160 }
1161
1162 private class MyHandler extends Handler {
1163 public static final int MESSAGE_NOTIFY_WINDOWS_CHANGED = 1;
1164
1165 public MyHandler(Looper looper) {
1166 super(looper, null, false);
1167 }
1168
1169 @Override
1170 @SuppressWarnings("unchecked")
1171 public void handleMessage(Message message) {
1172 switch (message.what) {
1173 case MESSAGE_NOTIFY_WINDOWS_CHANGED: {
1174 List<WindowInfo> windows = (List<WindowInfo>) message.obj;
1175 mCallback.onWindowsForAccessibilityChanged(windows);
1176 clearAndRecycleWindows(windows);
1177 } break;
1178 }
1179 }
1180 }
1181 }
1182}