1 /* 2 * Copyright (C) 2012 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 17 package com.android.server.wm; 18 19 import android.animation.ObjectAnimator; 20 import android.animation.ValueAnimator; 21 import android.app.Service; 22 import android.content.Context; 23 import android.graphics.Canvas; 24 import android.graphics.Color; 25 import android.graphics.Matrix; 26 import android.graphics.Paint; 27 import android.graphics.Path; 28 import android.graphics.PixelFormat; 29 import android.graphics.Point; 30 import android.graphics.PorterDuff.Mode; 31 import android.graphics.Rect; 32 import android.graphics.RectF; 33 import android.graphics.Region; 34 import android.os.Handler; 35 import android.os.Looper; 36 import android.os.Message; 37 import android.os.RemoteException; 38 import android.util.Pools.SimplePool; 39 import android.util.Slog; 40 import android.util.SparseArray; 41 import android.util.TypedValue; 42 import android.view.IMagnificationCallbacks; 43 import android.view.MagnificationSpec; 44 import android.view.Surface; 45 import android.view.Surface.OutOfResourcesException; 46 import android.view.SurfaceControl; 47 import android.view.WindowManager; 48 import android.view.WindowManagerPolicy; 49 import android.view.animation.DecelerateInterpolator; 50 import android.view.animation.Interpolator; 51 52 import com.android.internal.R; 53 import com.android.internal.os.SomeArgs; 54 55 /** 56 * This class is a part of the window manager and encapsulates the 57 * functionality related to display magnification. 58 */ 59 final class DisplayMagnifier { 60 private static final String LOG_TAG = DisplayMagnifier.class.getSimpleName(); 61 62 private static final boolean DEBUG_WINDOW_TRANSITIONS = false; 63 private static final boolean DEBUG_ROTATION = false; 64 private static final boolean DEBUG_LAYERS = false; 65 private static final boolean DEBUG_RECTANGLE_REQUESTED = false; 66 private static final boolean DEBUG_VIEWPORT_WINDOW = false; 67 68 private final Rect mTempRect1 = new Rect(); 69 private final Rect mTempRect2 = new Rect(); 70 71 private final Region mTempRegion1 = new Region(); 72 private final Region mTempRegion2 = new Region(); 73 private final Region mTempRegion3 = new Region(); 74 private final Region mTempRegion4 = new Region(); 75 76 private final Context mContext; 77 private final WindowManagerService mWindowManagerService; 78 private final MagnifiedViewport mMagnifedViewport; 79 private final Handler mHandler; 80 81 private final IMagnificationCallbacks mCallbacks; 82 83 private final long mLongAnimationDuration; 84 DisplayMagnifier(WindowManagerService windowManagerService, IMagnificationCallbacks callbacks)85 public DisplayMagnifier(WindowManagerService windowManagerService, 86 IMagnificationCallbacks callbacks) { 87 mContext = windowManagerService.mContext; 88 mWindowManagerService = windowManagerService; 89 mCallbacks = callbacks; 90 mHandler = new MyHandler(mWindowManagerService.mH.getLooper()); 91 mMagnifedViewport = new MagnifiedViewport(); 92 mLongAnimationDuration = mContext.getResources().getInteger( 93 com.android.internal.R.integer.config_longAnimTime); 94 } 95 setMagnificationSpecLocked(MagnificationSpec spec)96 public void setMagnificationSpecLocked(MagnificationSpec spec) { 97 mMagnifedViewport.updateMagnificationSpecLocked(spec); 98 mMagnifedViewport.recomputeBoundsLocked(); 99 mWindowManagerService.scheduleAnimationLocked(); 100 } 101 onRectangleOnScreenRequestedLocked(Rect rectangle, boolean immediate)102 public void onRectangleOnScreenRequestedLocked(Rect rectangle, boolean immediate) { 103 if (DEBUG_RECTANGLE_REQUESTED) { 104 Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle); 105 } 106 if (!mMagnifedViewport.isMagnifyingLocked()) { 107 return; 108 } 109 Rect magnifiedRegionBounds = mTempRect2; 110 mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds); 111 if (magnifiedRegionBounds.contains(rectangle)) { 112 return; 113 } 114 SomeArgs args = SomeArgs.obtain(); 115 args.argi1 = rectangle.left; 116 args.argi2 = rectangle.top; 117 args.argi3 = rectangle.right; 118 args.argi4 = rectangle.bottom; 119 mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED, 120 args).sendToTarget(); 121 } 122 onWindowLayersChangedLocked()123 public void onWindowLayersChangedLocked() { 124 if (DEBUG_LAYERS) { 125 Slog.i(LOG_TAG, "Layers changed."); 126 } 127 mMagnifedViewport.recomputeBoundsLocked(); 128 mWindowManagerService.scheduleAnimationLocked(); 129 } 130 onRotationChangedLocked(DisplayContent displayContent, int rotation)131 public void onRotationChangedLocked(DisplayContent displayContent, int rotation) { 132 if (DEBUG_ROTATION) { 133 Slog.i(LOG_TAG, "Rotaton: " + Surface.rotationToString(rotation) 134 + " displayId: " + displayContent.getDisplayId()); 135 } 136 mMagnifedViewport.onRotationChangedLocked(); 137 mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED); 138 } 139 onAppWindowTransitionLocked(WindowState windowState, int transition)140 public void onAppWindowTransitionLocked(WindowState windowState, int transition) { 141 if (DEBUG_WINDOW_TRANSITIONS) { 142 Slog.i(LOG_TAG, "Window transition: " 143 + AppTransition.appTransitionToString(transition) 144 + " displayId: " + windowState.getDisplayId()); 145 } 146 final boolean magnifying = mMagnifedViewport.isMagnifyingLocked(); 147 if (magnifying) { 148 switch (transition) { 149 case AppTransition.TRANSIT_ACTIVITY_OPEN: 150 case AppTransition.TRANSIT_TASK_OPEN: 151 case AppTransition.TRANSIT_TASK_TO_FRONT: 152 case AppTransition.TRANSIT_WALLPAPER_OPEN: 153 case AppTransition.TRANSIT_WALLPAPER_CLOSE: 154 case AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN: { 155 mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED); 156 } 157 } 158 } 159 } 160 onWindowTransitionLocked(WindowState windowState, int transition)161 public void onWindowTransitionLocked(WindowState windowState, int transition) { 162 if (DEBUG_WINDOW_TRANSITIONS) { 163 Slog.i(LOG_TAG, "Window transition: " 164 + AppTransition.appTransitionToString(transition) 165 + " displayId: " + windowState.getDisplayId()); 166 } 167 final boolean magnifying = mMagnifedViewport.isMagnifyingLocked(); 168 final int type = windowState.mAttrs.type; 169 switch (transition) { 170 case WindowManagerPolicy.TRANSIT_ENTER: 171 case WindowManagerPolicy.TRANSIT_SHOW: { 172 if (!magnifying) { 173 break; 174 } 175 switch (type) { 176 case WindowManager.LayoutParams.TYPE_APPLICATION: 177 case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL: 178 case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA: 179 case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL: 180 case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG: 181 case WindowManager.LayoutParams.TYPE_SEARCH_BAR: 182 case WindowManager.LayoutParams.TYPE_PHONE: 183 case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT: 184 case WindowManager.LayoutParams.TYPE_TOAST: 185 case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY: 186 case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE: 187 case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG: 188 case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG: 189 case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR: 190 case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY: 191 case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: 192 case WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY: { 193 Rect magnifiedRegionBounds = mTempRect2; 194 mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked( 195 magnifiedRegionBounds); 196 Rect touchableRegionBounds = mTempRect1; 197 windowState.getTouchableRegion(mTempRegion1); 198 mTempRegion1.getBounds(touchableRegionBounds); 199 if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) { 200 try { 201 mCallbacks.onRectangleOnScreenRequested( 202 touchableRegionBounds.left, 203 touchableRegionBounds.top, 204 touchableRegionBounds.right, 205 touchableRegionBounds.bottom); 206 } catch (RemoteException re) { 207 /* ignore */ 208 } 209 } 210 } break; 211 } break; 212 } 213 } 214 } 215 getMagnificationSpecForWindowLocked(WindowState windowState)216 public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) { 217 MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked(); 218 if (spec != null && !spec.isNop()) { 219 WindowManagerPolicy policy = mWindowManagerService.mPolicy; 220 final int windowType = windowState.mAttrs.type; 221 if (!policy.isTopLevelWindow(windowType) && windowState.mAttachedWindow != null 222 && !policy.canMagnifyWindow(windowType)) { 223 return null; 224 } 225 if (!policy.canMagnifyWindow(windowState.mAttrs.type)) { 226 return null; 227 } 228 } 229 return spec; 230 } 231 destroyLocked()232 public void destroyLocked() { 233 mMagnifedViewport.destroyWindow(); 234 } 235 236 /** NOTE: This has to be called within a surface transaction. */ drawMagnifiedRegionBorderIfNeededLocked()237 public void drawMagnifiedRegionBorderIfNeededLocked() { 238 mMagnifedViewport.drawWindowIfNeededLocked(); 239 } 240 241 private final class MagnifiedViewport { 242 243 private static final int DEFAUTLT_BORDER_WIDTH_DIP = 5; 244 245 private final SparseArray<WindowStateInfo> mTempWindowStateInfos = 246 new SparseArray<WindowStateInfo>(); 247 248 private final float[] mTempFloats = new float[9]; 249 250 private final RectF mTempRectF = new RectF(); 251 252 private final Point mTempPoint = new Point(); 253 254 private final Matrix mTempMatrix = new Matrix(); 255 256 private final Region mMagnifiedBounds = new Region(); 257 private final Region mOldMagnifiedBounds = new Region(); 258 259 private final MagnificationSpec mMagnificationSpec = MagnificationSpec.obtain(); 260 261 private final WindowManager mWindowManager; 262 263 private final int mBorderWidth; 264 private final int mHalfBorderWidth; 265 266 private final ViewportWindow mWindow; 267 268 private boolean mFullRedrawNeeded; 269 MagnifiedViewport()270 public MagnifiedViewport() { 271 mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE); 272 mBorderWidth = (int) TypedValue.applyDimension( 273 TypedValue.COMPLEX_UNIT_DIP, DEFAUTLT_BORDER_WIDTH_DIP, 274 mContext.getResources().getDisplayMetrics()); 275 mHalfBorderWidth = (int) (mBorderWidth + 0.5) / 2; 276 mWindow = new ViewportWindow(mContext); 277 recomputeBoundsLocked(); 278 } 279 updateMagnificationSpecLocked(MagnificationSpec spec)280 public void updateMagnificationSpecLocked(MagnificationSpec spec) { 281 if (spec != null) { 282 mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY); 283 } else { 284 mMagnificationSpec.clear(); 285 } 286 // If this message is pending we are in a rotation animation and do not want 287 // to show the border. We will do so when the pending message is handled. 288 if (!mHandler.hasMessages(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) { 289 setMagnifiedRegionBorderShownLocked(isMagnifyingLocked(), true); 290 } 291 } 292 recomputeBoundsLocked()293 public void recomputeBoundsLocked() { 294 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint); 295 final int screenWidth = mTempPoint.x; 296 final int screenHeight = mTempPoint.y; 297 298 Region magnifiedBounds = mMagnifiedBounds; 299 magnifiedBounds.set(0, 0, 0, 0); 300 301 Region availableBounds = mTempRegion1; 302 availableBounds.set(0, 0, screenWidth, screenHeight); 303 304 Region nonMagnifiedBounds = mTempRegion4; 305 nonMagnifiedBounds.set(0, 0, 0, 0); 306 307 SparseArray<WindowStateInfo> visibleWindows = mTempWindowStateInfos; 308 visibleWindows.clear(); 309 getWindowsOnScreenLocked(visibleWindows); 310 311 final int visibleWindowCount = visibleWindows.size(); 312 for (int i = visibleWindowCount - 1; i >= 0; i--) { 313 WindowStateInfo info = visibleWindows.valueAt(i); 314 if (info.mWindowState.mAttrs.type == WindowManager 315 .LayoutParams.TYPE_MAGNIFICATION_OVERLAY) { 316 continue; 317 } 318 319 Region windowBounds = mTempRegion2; 320 Matrix matrix = mTempMatrix; 321 populateTransformationMatrix(info.mWindowState, matrix); 322 RectF windowFrame = mTempRectF; 323 324 if (mWindowManagerService.mPolicy.canMagnifyWindow(info.mWindowState.mAttrs.type)) { 325 windowFrame.set(info.mWindowState.mFrame); 326 windowFrame.offset(-windowFrame.left, -windowFrame.top); 327 matrix.mapRect(windowFrame); 328 windowBounds.set((int) windowFrame.left, (int) windowFrame.top, 329 (int) windowFrame.right, (int) windowFrame.bottom); 330 magnifiedBounds.op(windowBounds, Region.Op.UNION); 331 magnifiedBounds.op(availableBounds, Region.Op.INTERSECT); 332 } else { 333 windowFrame.set(info.mTouchableRegion); 334 windowFrame.offset(-info.mWindowState.mFrame.left, 335 -info.mWindowState.mFrame.top); 336 matrix.mapRect(windowFrame); 337 windowBounds.set((int) windowFrame.left, (int) windowFrame.top, 338 (int) windowFrame.right, (int) windowFrame.bottom); 339 nonMagnifiedBounds.op(windowBounds, Region.Op.UNION); 340 windowBounds.op(magnifiedBounds, Region.Op.DIFFERENCE); 341 availableBounds.op(windowBounds, Region.Op.DIFFERENCE); 342 } 343 344 Region accountedBounds = mTempRegion2; 345 accountedBounds.set(magnifiedBounds); 346 accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION); 347 accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT); 348 349 if (accountedBounds.isRect()) { 350 Rect accountedFrame = mTempRect1; 351 accountedBounds.getBounds(accountedFrame); 352 if (accountedFrame.width() == screenWidth 353 && accountedFrame.height() == screenHeight) { 354 break; 355 } 356 } 357 } 358 359 for (int i = visibleWindowCount - 1; i >= 0; i--) { 360 WindowStateInfo info = visibleWindows.valueAt(i); 361 info.recycle(); 362 visibleWindows.removeAt(i); 363 } 364 365 magnifiedBounds.op(mHalfBorderWidth, mHalfBorderWidth, 366 screenWidth - mHalfBorderWidth, screenHeight - mHalfBorderWidth, 367 Region.Op.INTERSECT); 368 369 if (!mOldMagnifiedBounds.equals(magnifiedBounds)) { 370 Region bounds = Region.obtain(); 371 bounds.set(magnifiedBounds); 372 mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED, 373 bounds).sendToTarget(); 374 375 mWindow.setBounds(magnifiedBounds); 376 Rect dirtyRect = mTempRect1; 377 if (mFullRedrawNeeded) { 378 mFullRedrawNeeded = false; 379 dirtyRect.set(mHalfBorderWidth, mHalfBorderWidth, 380 screenWidth - mHalfBorderWidth, screenHeight - mHalfBorderWidth); 381 mWindow.invalidate(dirtyRect); 382 } else { 383 Region dirtyRegion = mTempRegion3; 384 dirtyRegion.set(magnifiedBounds); 385 dirtyRegion.op(mOldMagnifiedBounds, Region.Op.UNION); 386 dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT); 387 dirtyRegion.getBounds(dirtyRect); 388 mWindow.invalidate(dirtyRect); 389 } 390 391 mOldMagnifiedBounds.set(magnifiedBounds); 392 } 393 } 394 populateTransformationMatrix(WindowState windowState, Matrix outMatrix)395 private void populateTransformationMatrix(WindowState windowState, Matrix outMatrix) { 396 mTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx; 397 mTempFloats[Matrix.MSKEW_Y] = windowState.mWinAnimator.mDtDx; 398 mTempFloats[Matrix.MSKEW_X] = windowState.mWinAnimator.mDsDy; 399 mTempFloats[Matrix.MSCALE_Y] = windowState.mWinAnimator.mDtDy; 400 mTempFloats[Matrix.MTRANS_X] = windowState.mShownFrame.left; 401 mTempFloats[Matrix.MTRANS_Y] = windowState.mShownFrame.top; 402 mTempFloats[Matrix.MPERSP_0] = 0; 403 mTempFloats[Matrix.MPERSP_1] = 0; 404 mTempFloats[Matrix.MPERSP_2] = 1; 405 outMatrix.setValues(mTempFloats); 406 } 407 getWindowsOnScreenLocked(SparseArray<WindowStateInfo> outWindowStates)408 private void getWindowsOnScreenLocked(SparseArray<WindowStateInfo> outWindowStates) { 409 DisplayContent displayContent = mWindowManagerService.getDefaultDisplayContentLocked(); 410 WindowList windowList = displayContent.getWindowList(); 411 final int windowCount = windowList.size(); 412 for (int i = 0; i < windowCount; i++) { 413 WindowState windowState = windowList.get(i); 414 if ((windowState.isOnScreen() || windowState.mAttrs.type == WindowManager 415 .LayoutParams.TYPE_UNIVERSE_BACKGROUND) 416 && !windowState.mWinAnimator.mEnterAnimationPending) { 417 outWindowStates.put(windowState.mLayer, WindowStateInfo.obtain(windowState)); 418 } 419 } 420 } 421 onRotationChangedLocked()422 public void onRotationChangedLocked() { 423 // If we are magnifying, hide the magnified border window immediately so 424 // the user does not see strange artifacts during rotation. The screenshot 425 // used for rotation has already the border. After the rotation is complete 426 // we will show the border. 427 if (isMagnifyingLocked()) { 428 setMagnifiedRegionBorderShownLocked(false, false); 429 final long delay = (long) (mLongAnimationDuration 430 * mWindowManagerService.mWindowAnimationScale); 431 Message message = mHandler.obtainMessage( 432 MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED); 433 mHandler.sendMessageDelayed(message, delay); 434 } 435 recomputeBoundsLocked(); 436 mWindow.updateSize(); 437 } 438 setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate)439 public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) { 440 if (shown) { 441 mFullRedrawNeeded = true; 442 mOldMagnifiedBounds.set(0, 0, 0, 0); 443 } 444 mWindow.setShown(shown, animate); 445 } 446 getMagnifiedFrameInContentCoordsLocked(Rect rect)447 public void getMagnifiedFrameInContentCoordsLocked(Rect rect) { 448 MagnificationSpec spec = mMagnificationSpec; 449 mMagnifiedBounds.getBounds(rect); 450 rect.offset((int) -spec.offsetX, (int) -spec.offsetY); 451 rect.scale(1.0f / spec.scale); 452 } 453 isMagnifyingLocked()454 public boolean isMagnifyingLocked() { 455 return mMagnificationSpec.scale > 1.0f; 456 } 457 getMagnificationSpecLocked()458 public MagnificationSpec getMagnificationSpecLocked() { 459 return mMagnificationSpec; 460 } 461 462 /** NOTE: This has to be called within a surface transaction. */ drawWindowIfNeededLocked()463 public void drawWindowIfNeededLocked() { 464 recomputeBoundsLocked(); 465 mWindow.drawIfNeeded(); 466 } 467 destroyWindow()468 public void destroyWindow() { 469 mWindow.releaseSurface(); 470 } 471 472 private final class ViewportWindow { 473 private static final String SURFACE_TITLE = "Magnification Overlay"; 474 475 private static final String PROPERTY_NAME_ALPHA = "alpha"; 476 477 private static final int MIN_ALPHA = 0; 478 private static final int MAX_ALPHA = 255; 479 480 private final Region mBounds = new Region(); 481 private final Rect mDirtyRect = new Rect(); 482 private final Paint mPaint = new Paint(); 483 484 private final ValueAnimator mShowHideFrameAnimator; 485 private final SurfaceControl mSurfaceControl; 486 private final Surface mSurface = new Surface(); 487 488 private boolean mShown; 489 private int mAlpha; 490 491 private boolean mInvalidated; 492 ViewportWindow(Context context)493 public ViewportWindow(Context context) { 494 SurfaceControl surfaceControl = null; 495 try { 496 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint); 497 surfaceControl = new SurfaceControl(mWindowManagerService.mFxSession, SURFACE_TITLE, 498 mTempPoint.x, mTempPoint.y, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN); 499 } catch (OutOfResourcesException oore) { 500 /* ignore */ 501 } 502 mSurfaceControl = surfaceControl; 503 mSurfaceControl.setLayerStack(mWindowManager.getDefaultDisplay().getLayerStack()); 504 mSurfaceControl.setLayer(mWindowManagerService.mPolicy.windowTypeToLayerLw( 505 WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY) 506 * WindowManagerService.TYPE_LAYER_MULTIPLIER); 507 mSurfaceControl.setPosition(0, 0); 508 mSurface.copyFrom(mSurfaceControl); 509 510 TypedValue typedValue = new TypedValue(); 511 context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight, 512 typedValue, true); 513 final int borderColor = context.getResources().getColor(typedValue.resourceId); 514 515 mPaint.setStyle(Paint.Style.STROKE); 516 mPaint.setStrokeWidth(mBorderWidth); 517 mPaint.setColor(borderColor); 518 519 Interpolator interpolator = new DecelerateInterpolator(2.5f); 520 final long longAnimationDuration = context.getResources().getInteger( 521 com.android.internal.R.integer.config_longAnimTime); 522 523 mShowHideFrameAnimator = ObjectAnimator.ofInt(this, PROPERTY_NAME_ALPHA, 524 MIN_ALPHA, MAX_ALPHA); 525 mShowHideFrameAnimator.setInterpolator(interpolator); 526 mShowHideFrameAnimator.setDuration(longAnimationDuration); 527 mInvalidated = true; 528 } 529 setShown(boolean shown, boolean animate)530 public void setShown(boolean shown, boolean animate) { 531 synchronized (mWindowManagerService.mWindowMap) { 532 if (mShown == shown) { 533 return; 534 } 535 mShown = shown; 536 if (animate) { 537 if (mShowHideFrameAnimator.isRunning()) { 538 mShowHideFrameAnimator.reverse(); 539 } else { 540 if (shown) { 541 mShowHideFrameAnimator.start(); 542 } else { 543 mShowHideFrameAnimator.reverse(); 544 } 545 } 546 } else { 547 mShowHideFrameAnimator.cancel(); 548 if (shown) { 549 setAlpha(MAX_ALPHA); 550 } else { 551 setAlpha(MIN_ALPHA); 552 } 553 } 554 if (DEBUG_VIEWPORT_WINDOW) { 555 Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown); 556 } 557 } 558 } 559 560 @SuppressWarnings("unused") 561 // Called reflectively from an animator. getAlpha()562 public int getAlpha() { 563 synchronized (mWindowManagerService.mWindowMap) { 564 return mAlpha; 565 } 566 } 567 setAlpha(int alpha)568 public void setAlpha(int alpha) { 569 synchronized (mWindowManagerService.mWindowMap) { 570 if (mAlpha == alpha) { 571 return; 572 } 573 mAlpha = alpha; 574 invalidate(null); 575 if (DEBUG_VIEWPORT_WINDOW) { 576 Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha); 577 } 578 } 579 } 580 setBounds(Region bounds)581 public void setBounds(Region bounds) { 582 synchronized (mWindowManagerService.mWindowMap) { 583 if (mBounds.equals(bounds)) { 584 return; 585 } 586 mBounds.set(bounds); 587 invalidate(mDirtyRect); 588 if (DEBUG_VIEWPORT_WINDOW) { 589 Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds); 590 } 591 } 592 } 593 updateSize()594 public void updateSize() { 595 synchronized (mWindowManagerService.mWindowMap) { 596 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint); 597 mSurfaceControl.setSize(mTempPoint.x, mTempPoint.y); 598 invalidate(mDirtyRect); 599 } 600 } 601 invalidate(Rect dirtyRect)602 public void invalidate(Rect dirtyRect) { 603 if (dirtyRect != null) { 604 mDirtyRect.set(dirtyRect); 605 } else { 606 mDirtyRect.setEmpty(); 607 } 608 mInvalidated = true; 609 mWindowManagerService.scheduleAnimationLocked(); 610 } 611 612 /** NOTE: This has to be called within a surface transaction. */ drawIfNeeded()613 public void drawIfNeeded() { 614 synchronized (mWindowManagerService.mWindowMap) { 615 if (!mInvalidated) { 616 return; 617 } 618 mInvalidated = false; 619 Canvas canvas = null; 620 try { 621 // Empty dirty rectangle means unspecified. 622 if (mDirtyRect.isEmpty()) { 623 mBounds.getBounds(mDirtyRect); 624 } 625 mDirtyRect.inset(- mHalfBorderWidth, - mHalfBorderWidth); 626 canvas = mSurface.lockCanvas(mDirtyRect); 627 if (DEBUG_VIEWPORT_WINDOW) { 628 Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect); 629 } 630 } catch (IllegalArgumentException iae) { 631 /* ignore */ 632 } catch (Surface.OutOfResourcesException oore) { 633 /* ignore */ 634 } 635 if (canvas == null) { 636 return; 637 } 638 if (DEBUG_VIEWPORT_WINDOW) { 639 Slog.i(LOG_TAG, "Bounds: " + mBounds); 640 } 641 canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR); 642 mPaint.setAlpha(mAlpha); 643 Path path = mBounds.getBoundaryPath(); 644 canvas.drawPath(path, mPaint); 645 646 mSurface.unlockCanvasAndPost(canvas); 647 648 if (mAlpha > 0) { 649 mSurfaceControl.show(); 650 } else { 651 mSurfaceControl.hide(); 652 } 653 } 654 } 655 releaseSurface()656 public void releaseSurface() { 657 mSurfaceControl.release(); 658 mSurface.release(); 659 } 660 } 661 } 662 663 private static final class WindowStateInfo { 664 private static final int MAX_POOL_SIZE = 30; 665 666 private static final SimplePool<WindowStateInfo> sPool = 667 new SimplePool<WindowStateInfo>(MAX_POOL_SIZE); 668 669 private static final Region mTempRegion = new Region(); 670 671 public WindowState mWindowState; 672 public final Rect mTouchableRegion = new Rect(); 673 obtain(WindowState windowState)674 public static WindowStateInfo obtain(WindowState windowState) { 675 WindowStateInfo info = sPool.acquire(); 676 if (info == null) { 677 info = new WindowStateInfo(); 678 } 679 info.mWindowState = windowState; 680 windowState.getTouchableRegion(mTempRegion); 681 mTempRegion.getBounds(info.mTouchableRegion); 682 return info; 683 } 684 recycle()685 public void recycle() { 686 mWindowState = null; 687 mTouchableRegion.setEmpty(); 688 sPool.release(this); 689 } 690 } 691 692 private class MyHandler extends Handler { 693 public static final int MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED = 1; 694 public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2; 695 public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3; 696 public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4; 697 public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5; 698 MyHandler(Looper looper)699 public MyHandler(Looper looper) { 700 super(looper); 701 } 702 703 @Override handleMessage(Message message)704 public void handleMessage(Message message) { 705 switch (message.what) { 706 case MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED: { 707 Region bounds = (Region) message.obj; 708 try { 709 mCallbacks.onMagnifedBoundsChanged(bounds); 710 } catch (RemoteException re) { 711 /* ignore */ 712 } finally { 713 bounds.recycle(); 714 } 715 } break; 716 case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: { 717 SomeArgs args = (SomeArgs) message.obj; 718 final int left = args.argi1; 719 final int top = args.argi2; 720 final int right = args.argi3; 721 final int bottom = args.argi4; 722 try { 723 mCallbacks.onRectangleOnScreenRequested(left, top, right, bottom); 724 } catch (RemoteException re) { 725 /* ignore */ 726 } finally { 727 args.recycle(); 728 } 729 } break; 730 case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: { 731 try { 732 mCallbacks.onUserContextChanged(); 733 } catch (RemoteException re) { 734 /* ignore */ 735 } 736 } break; 737 case MESSAGE_NOTIFY_ROTATION_CHANGED: { 738 final int rotation = message.arg1; 739 try { 740 mCallbacks.onRotationChanged(rotation); 741 } catch (RemoteException re) { 742 /* ignore */ 743 } 744 } break; 745 case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : { 746 synchronized (mWindowManagerService.mWindowMap) { 747 if (mMagnifedViewport.isMagnifyingLocked()) { 748 mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true); 749 mWindowManagerService.scheduleAnimationLocked(); 750 } 751 } 752 } break; 753 } 754 } 755 } 756 } 757