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