1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package com.android.systemui; 16 17 import static android.view.Surface.ROTATION_0; 18 import static android.view.Surface.ROTATION_180; 19 import static android.view.Surface.ROTATION_270; 20 import static android.view.Surface.ROTATION_90; 21 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 22 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; 23 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 24 25 import static com.android.systemui.tuner.TunablePadding.FLAG_END; 26 import static com.android.systemui.tuner.TunablePadding.FLAG_START; 27 28 import android.annotation.Dimension; 29 import android.app.Fragment; 30 import android.content.Context; 31 import android.content.res.ColorStateList; 32 import android.content.res.Configuration; 33 import android.graphics.Canvas; 34 import android.graphics.Color; 35 import android.graphics.Matrix; 36 import android.graphics.Paint; 37 import android.graphics.Path; 38 import android.graphics.PixelFormat; 39 import android.graphics.Rect; 40 import android.graphics.Region; 41 import android.hardware.display.DisplayManager; 42 import android.os.SystemProperties; 43 import android.provider.Settings.Secure; 44 import android.support.annotation.VisibleForTesting; 45 import android.util.DisplayMetrics; 46 import android.view.DisplayCutout; 47 import android.view.DisplayInfo; 48 import android.view.Gravity; 49 import android.view.LayoutInflater; 50 import android.view.Surface; 51 import android.view.View; 52 import android.view.View.OnLayoutChangeListener; 53 import android.view.ViewGroup; 54 import android.view.ViewGroup.LayoutParams; 55 import android.view.WindowManager; 56 import android.widget.FrameLayout; 57 import android.widget.ImageView; 58 59 import com.android.systemui.RegionInterceptingFrameLayout.RegionInterceptableView; 60 import com.android.systemui.fragments.FragmentHostManager; 61 import com.android.systemui.fragments.FragmentHostManager.FragmentListener; 62 import com.android.systemui.plugins.qs.QS; 63 import com.android.systemui.qs.SecureSetting; 64 import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment; 65 import com.android.systemui.statusbar.phone.StatusBar; 66 import com.android.systemui.tuner.TunablePadding; 67 import com.android.systemui.tuner.TunerService; 68 import com.android.systemui.tuner.TunerService.Tunable; 69 import com.android.systemui.util.leak.RotationUtils; 70 71 /** 72 * An overlay that draws screen decorations in software (e.g for rounded corners or display cutout) 73 * for antialiasing and emulation purposes. 74 */ 75 public class ScreenDecorations extends SystemUI implements Tunable { 76 public static final String SIZE = "sysui_rounded_size"; 77 public static final String PADDING = "sysui_rounded_content_padding"; 78 private static final boolean DEBUG_SCREENSHOT_ROUNDED_CORNERS = 79 SystemProperties.getBoolean("debug.screenshot_rounded_corners", false); 80 81 private DisplayManager mDisplayManager; 82 private DisplayManager.DisplayListener mDisplayListener; 83 84 private int mRoundedDefault; 85 private int mRoundedDefaultTop; 86 private int mRoundedDefaultBottom; 87 private View mOverlay; 88 private View mBottomOverlay; 89 private float mDensity; 90 private WindowManager mWindowManager; 91 private int mRotation; 92 93 @Override start()94 public void start() { 95 mWindowManager = mContext.getSystemService(WindowManager.class); 96 mRoundedDefault = mContext.getResources().getDimensionPixelSize( 97 R.dimen.rounded_corner_radius); 98 mRoundedDefaultTop = mContext.getResources().getDimensionPixelSize( 99 R.dimen.rounded_corner_radius_top); 100 mRoundedDefaultBottom = mContext.getResources().getDimensionPixelSize( 101 R.dimen.rounded_corner_radius_bottom); 102 if (hasRoundedCorners() || shouldDrawCutout()) { 103 setupDecorations(); 104 } 105 106 int padding = mContext.getResources().getDimensionPixelSize( 107 R.dimen.rounded_corner_content_padding); 108 if (padding != 0) { 109 setupPadding(padding); 110 } 111 112 mDisplayListener = new DisplayManager.DisplayListener() { 113 @Override 114 public void onDisplayAdded(int displayId) { 115 // do nothing 116 } 117 118 @Override 119 public void onDisplayRemoved(int displayId) { 120 // do nothing 121 } 122 123 @Override 124 public void onDisplayChanged(int displayId) { 125 updateOrientation(); 126 } 127 }; 128 129 mRotation = -1; 130 mDisplayManager = (DisplayManager) mContext.getSystemService( 131 Context.DISPLAY_SERVICE); 132 mDisplayManager.registerDisplayListener(mDisplayListener, null); 133 } 134 setupDecorations()135 private void setupDecorations() { 136 mOverlay = LayoutInflater.from(mContext) 137 .inflate(R.layout.rounded_corners, null); 138 DisplayCutoutView cutoutTop = new DisplayCutoutView(mContext, true, 139 this::updateWindowVisibilities); 140 ((ViewGroup)mOverlay).addView(cutoutTop); 141 mBottomOverlay = LayoutInflater.from(mContext) 142 .inflate(R.layout.rounded_corners, null); 143 DisplayCutoutView cutoutBottom = new DisplayCutoutView(mContext, false, 144 this::updateWindowVisibilities); 145 ((ViewGroup)mBottomOverlay).addView(cutoutBottom); 146 147 mOverlay.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE); 148 mOverlay.setAlpha(0); 149 150 mBottomOverlay.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE); 151 mBottomOverlay.setAlpha(0); 152 153 updateViews(); 154 155 mWindowManager.addView(mOverlay, getWindowLayoutParams()); 156 mWindowManager.addView(mBottomOverlay, getBottomLayoutParams()); 157 158 DisplayMetrics metrics = new DisplayMetrics(); 159 mWindowManager.getDefaultDisplay().getMetrics(metrics); 160 mDensity = metrics.density; 161 162 Dependency.get(TunerService.class).addTunable(this, SIZE); 163 164 // Watch color inversion and invert the overlay as needed. 165 SecureSetting setting = new SecureSetting(mContext, Dependency.get(Dependency.MAIN_HANDLER), 166 Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED) { 167 @Override 168 protected void handleValueChanged(int value, boolean observedChange) { 169 int tint = value != 0 ? Color.WHITE : Color.BLACK; 170 ColorStateList tintList = ColorStateList.valueOf(tint); 171 ((ImageView) mOverlay.findViewById(R.id.left)).setImageTintList(tintList); 172 ((ImageView) mOverlay.findViewById(R.id.right)).setImageTintList(tintList); 173 ((ImageView) mBottomOverlay.findViewById(R.id.left)).setImageTintList(tintList); 174 ((ImageView) mBottomOverlay.findViewById(R.id.right)).setImageTintList(tintList); 175 cutoutTop.setColor(tint); 176 cutoutBottom.setColor(tint); 177 } 178 }; 179 setting.setListening(true); 180 setting.onChange(false); 181 182 mOverlay.addOnLayoutChangeListener(new OnLayoutChangeListener() { 183 @Override 184 public void onLayoutChange(View v, int left, int top, int right, int bottom, 185 int oldLeft, 186 int oldTop, int oldRight, int oldBottom) { 187 mOverlay.removeOnLayoutChangeListener(this); 188 mOverlay.animate() 189 .alpha(1) 190 .setDuration(1000) 191 .start(); 192 mBottomOverlay.animate() 193 .alpha(1) 194 .setDuration(1000) 195 .start(); 196 } 197 }); 198 } 199 200 @Override onConfigurationChanged(Configuration newConfig)201 protected void onConfigurationChanged(Configuration newConfig) { 202 updateOrientation(); 203 if (shouldDrawCutout() && mOverlay == null) { 204 setupDecorations(); 205 } 206 } 207 updateOrientation()208 protected void updateOrientation() { 209 int newRotation = RotationUtils.getExactRotation(mContext); 210 if (newRotation != mRotation) { 211 mRotation = newRotation; 212 213 if (mOverlay != null) { 214 updateLayoutParams(); 215 updateViews(); 216 } 217 } 218 } 219 updateViews()220 private void updateViews() { 221 View topLeft = mOverlay.findViewById(R.id.left); 222 View topRight = mOverlay.findViewById(R.id.right); 223 View bottomLeft = mBottomOverlay.findViewById(R.id.left); 224 View bottomRight = mBottomOverlay.findViewById(R.id.right); 225 226 if (mRotation == RotationUtils.ROTATION_NONE) { 227 updateView(topLeft, Gravity.TOP | Gravity.LEFT, 0); 228 updateView(topRight, Gravity.TOP | Gravity.RIGHT, 90); 229 updateView(bottomLeft, Gravity.BOTTOM | Gravity.LEFT, 270); 230 updateView(bottomRight, Gravity.BOTTOM | Gravity.RIGHT, 180); 231 } else if (mRotation == RotationUtils.ROTATION_LANDSCAPE) { 232 updateView(topLeft, Gravity.TOP | Gravity.LEFT, 0); 233 updateView(topRight, Gravity.BOTTOM | Gravity.LEFT, 270); 234 updateView(bottomLeft, Gravity.TOP | Gravity.RIGHT, 90);; 235 updateView(bottomRight, Gravity.BOTTOM | Gravity.RIGHT, 180); 236 } else if (mRotation == RotationUtils.ROTATION_UPSIDE_DOWN) { 237 updateView(topLeft, Gravity.BOTTOM | Gravity.LEFT, 270); 238 updateView(topRight, Gravity.BOTTOM | Gravity.RIGHT, 180); 239 updateView(bottomLeft, Gravity.TOP | Gravity.LEFT, 0); 240 updateView(bottomRight, Gravity.TOP | Gravity.RIGHT, 90); 241 } else if (mRotation == RotationUtils.ROTATION_SEASCAPE) { 242 updateView(topLeft, Gravity.BOTTOM | Gravity.RIGHT, 180); 243 updateView(topRight, Gravity.TOP | Gravity.RIGHT, 90); 244 updateView(bottomLeft, Gravity.BOTTOM | Gravity.LEFT, 270); 245 updateView(bottomRight, Gravity.TOP | Gravity.LEFT, 0); 246 } 247 248 updateWindowVisibilities(); 249 } 250 updateView(View v, int gravity, int rotation)251 private void updateView(View v, int gravity, int rotation) { 252 ((FrameLayout.LayoutParams)v.getLayoutParams()).gravity = gravity; 253 v.setRotation(rotation); 254 } 255 updateWindowVisibilities()256 private void updateWindowVisibilities() { 257 updateWindowVisibility(mOverlay); 258 updateWindowVisibility(mBottomOverlay); 259 } 260 updateWindowVisibility(View overlay)261 private void updateWindowVisibility(View overlay) { 262 boolean visibleForCutout = shouldDrawCutout() 263 && overlay.findViewById(R.id.display_cutout).getVisibility() == View.VISIBLE; 264 boolean visibleForRoundedCorners = hasRoundedCorners(); 265 overlay.setVisibility(visibleForCutout || visibleForRoundedCorners 266 ? View.VISIBLE : View.GONE); 267 } 268 hasRoundedCorners()269 private boolean hasRoundedCorners() { 270 return mRoundedDefault > 0 || mRoundedDefaultBottom > 0 || mRoundedDefaultTop > 0; 271 } 272 shouldDrawCutout()273 private boolean shouldDrawCutout() { 274 return shouldDrawCutout(mContext); 275 } 276 shouldDrawCutout(Context context)277 static boolean shouldDrawCutout(Context context) { 278 return context.getResources().getBoolean( 279 com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout); 280 } 281 setupPadding(int padding)282 private void setupPadding(int padding) { 283 // Add some padding to all the content near the edge of the screen. 284 StatusBar sb = getComponent(StatusBar.class); 285 View statusBar = (sb != null ? sb.getStatusBarWindow() : null); 286 if (statusBar != null) { 287 TunablePadding.addTunablePadding(statusBar.findViewById(R.id.keyguard_header), PADDING, 288 padding, FLAG_END); 289 290 FragmentHostManager fragmentHostManager = FragmentHostManager.get(statusBar); 291 fragmentHostManager.addTagListener(CollapsedStatusBarFragment.TAG, 292 new TunablePaddingTagListener(padding, R.id.status_bar)); 293 fragmentHostManager.addTagListener(QS.TAG, 294 new TunablePaddingTagListener(padding, R.id.header)); 295 } 296 } 297 298 @VisibleForTesting getWindowLayoutParams()299 WindowManager.LayoutParams getWindowLayoutParams() { 300 final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 301 ViewGroup.LayoutParams.MATCH_PARENT, 302 LayoutParams.WRAP_CONTENT, 303 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, 304 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 305 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 306 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH 307 | WindowManager.LayoutParams.FLAG_SLIPPERY 308 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, 309 PixelFormat.TRANSLUCENT); 310 lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS 311 | WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; 312 313 if (!DEBUG_SCREENSHOT_ROUNDED_CORNERS) { 314 lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY; 315 } 316 317 lp.setTitle("ScreenDecorOverlay"); 318 if (mRotation == RotationUtils.ROTATION_SEASCAPE 319 || mRotation == RotationUtils.ROTATION_UPSIDE_DOWN) { 320 lp.gravity = Gravity.BOTTOM | Gravity.RIGHT; 321 } else { 322 lp.gravity = Gravity.TOP | Gravity.LEFT; 323 } 324 lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 325 if (isLandscape(mRotation)) { 326 lp.width = WRAP_CONTENT; 327 lp.height = MATCH_PARENT; 328 } 329 return lp; 330 } 331 getBottomLayoutParams()332 private WindowManager.LayoutParams getBottomLayoutParams() { 333 WindowManager.LayoutParams lp = getWindowLayoutParams(); 334 lp.setTitle("ScreenDecorOverlayBottom"); 335 if (mRotation == RotationUtils.ROTATION_SEASCAPE 336 || mRotation == RotationUtils.ROTATION_UPSIDE_DOWN) { 337 lp.gravity = Gravity.TOP | Gravity.LEFT; 338 } else { 339 lp.gravity = Gravity.BOTTOM | Gravity.RIGHT; 340 } 341 return lp; 342 } 343 updateLayoutParams()344 private void updateLayoutParams() { 345 mWindowManager.updateViewLayout(mOverlay, getWindowLayoutParams()); 346 mWindowManager.updateViewLayout(mBottomOverlay, getBottomLayoutParams()); 347 } 348 349 @Override onTuningChanged(String key, String newValue)350 public void onTuningChanged(String key, String newValue) { 351 if (mOverlay == null) return; 352 if (SIZE.equals(key)) { 353 int size = mRoundedDefault; 354 int sizeTop = mRoundedDefaultTop; 355 int sizeBottom = mRoundedDefaultBottom; 356 if (newValue != null) { 357 try { 358 size = (int) (Integer.parseInt(newValue) * mDensity); 359 } catch (Exception e) { 360 } 361 } 362 363 if (sizeTop == 0) { 364 sizeTop = size; 365 } 366 if (sizeBottom == 0) { 367 sizeBottom = size; 368 } 369 370 setSize(mOverlay.findViewById(R.id.left), sizeTop); 371 setSize(mOverlay.findViewById(R.id.right), sizeTop); 372 setSize(mBottomOverlay.findViewById(R.id.left), sizeBottom); 373 setSize(mBottomOverlay.findViewById(R.id.right), sizeBottom); 374 } 375 } 376 setSize(View view, int pixelSize)377 private void setSize(View view, int pixelSize) { 378 LayoutParams params = view.getLayoutParams(); 379 params.width = pixelSize; 380 params.height = pixelSize; 381 view.setLayoutParams(params); 382 } 383 384 @VisibleForTesting 385 static class TunablePaddingTagListener implements FragmentListener { 386 387 private final int mPadding; 388 private final int mId; 389 private TunablePadding mTunablePadding; 390 TunablePaddingTagListener(int padding, int id)391 public TunablePaddingTagListener(int padding, int id) { 392 mPadding = padding; 393 mId = id; 394 } 395 396 @Override onFragmentViewCreated(String tag, Fragment fragment)397 public void onFragmentViewCreated(String tag, Fragment fragment) { 398 if (mTunablePadding != null) { 399 mTunablePadding.destroy(); 400 } 401 View view = fragment.getView(); 402 if (mId != 0) { 403 view = view.findViewById(mId); 404 } 405 mTunablePadding = TunablePadding.addTunablePadding(view, PADDING, mPadding, 406 FLAG_START | FLAG_END); 407 } 408 } 409 410 public static class DisplayCutoutView extends View implements DisplayManager.DisplayListener, 411 RegionInterceptableView { 412 413 private final DisplayInfo mInfo = new DisplayInfo(); 414 private final Paint mPaint = new Paint(); 415 private final Region mBounds = new Region(); 416 private final Rect mBoundingRect = new Rect(); 417 private final Path mBoundingPath = new Path(); 418 private final int[] mLocation = new int[2]; 419 private final boolean mStart; 420 private final Runnable mVisibilityChangedListener; 421 private int mColor = Color.BLACK; 422 DisplayCutoutView(Context context, boolean start, Runnable visibilityChangedListener)423 public DisplayCutoutView(Context context, boolean start, 424 Runnable visibilityChangedListener) { 425 super(context); 426 mStart = start; 427 mVisibilityChangedListener = visibilityChangedListener; 428 setId(R.id.display_cutout); 429 } 430 setColor(int color)431 public void setColor(int color) { 432 mColor = color; 433 invalidate(); 434 } 435 436 @Override onAttachedToWindow()437 protected void onAttachedToWindow() { 438 super.onAttachedToWindow(); 439 mContext.getSystemService(DisplayManager.class).registerDisplayListener(this, 440 getHandler()); 441 update(); 442 } 443 444 @Override onDetachedFromWindow()445 protected void onDetachedFromWindow() { 446 super.onDetachedFromWindow(); 447 mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this); 448 } 449 450 @Override onDraw(Canvas canvas)451 protected void onDraw(Canvas canvas) { 452 super.onDraw(canvas); 453 getLocationOnScreen(mLocation); 454 canvas.translate(-mLocation[0], -mLocation[1]); 455 if (!mBoundingPath.isEmpty()) { 456 mPaint.setColor(mColor); 457 mPaint.setStyle(Paint.Style.FILL); 458 mPaint.setAntiAlias(true); 459 canvas.drawPath(mBoundingPath, mPaint); 460 } 461 } 462 463 @Override onDisplayAdded(int displayId)464 public void onDisplayAdded(int displayId) { 465 } 466 467 @Override onDisplayRemoved(int displayId)468 public void onDisplayRemoved(int displayId) { 469 } 470 471 @Override onDisplayChanged(int displayId)472 public void onDisplayChanged(int displayId) { 473 if (displayId == getDisplay().getDisplayId()) { 474 update(); 475 } 476 } 477 update()478 private void update() { 479 requestLayout(); 480 getDisplay().getDisplayInfo(mInfo); 481 mBounds.setEmpty(); 482 mBoundingRect.setEmpty(); 483 mBoundingPath.reset(); 484 int newVisible; 485 if (shouldDrawCutout(getContext()) && hasCutout()) { 486 mBounds.set(mInfo.displayCutout.getBounds()); 487 localBounds(mBoundingRect); 488 updateBoundingPath(); 489 invalidate(); 490 newVisible = VISIBLE; 491 } else { 492 newVisible = GONE; 493 } 494 if (newVisible != getVisibility()) { 495 setVisibility(newVisible); 496 mVisibilityChangedListener.run(); 497 } 498 } 499 updateBoundingPath()500 private void updateBoundingPath() { 501 int lw = mInfo.logicalWidth; 502 int lh = mInfo.logicalHeight; 503 504 boolean flipped = mInfo.rotation == ROTATION_90 || mInfo.rotation == ROTATION_270; 505 506 int dw = flipped ? lh : lw; 507 int dh = flipped ? lw : lh; 508 509 mBoundingPath.set(DisplayCutout.pathFromResources(getResources(), dw, dh)); 510 Matrix m = new Matrix(); 511 transformPhysicalToLogicalCoordinates(mInfo.rotation, dw, dh, m); 512 mBoundingPath.transform(m); 513 } 514 transformPhysicalToLogicalCoordinates(@urface.Rotation int rotation, @Dimension int physicalWidth, @Dimension int physicalHeight, Matrix out)515 private static void transformPhysicalToLogicalCoordinates(@Surface.Rotation int rotation, 516 @Dimension int physicalWidth, @Dimension int physicalHeight, Matrix out) { 517 switch (rotation) { 518 case ROTATION_0: 519 out.reset(); 520 break; 521 case ROTATION_90: 522 out.setRotate(270); 523 out.postTranslate(0, physicalWidth); 524 break; 525 case ROTATION_180: 526 out.setRotate(180); 527 out.postTranslate(physicalWidth, physicalHeight); 528 break; 529 case ROTATION_270: 530 out.setRotate(90); 531 out.postTranslate(physicalHeight, 0); 532 break; 533 default: 534 throw new IllegalArgumentException("Unknown rotation: " + rotation); 535 } 536 } 537 hasCutout()538 private boolean hasCutout() { 539 final DisplayCutout displayCutout = mInfo.displayCutout; 540 if (displayCutout == null) { 541 return false; 542 } 543 if (mStart) { 544 return displayCutout.getSafeInsetLeft() > 0 545 || displayCutout.getSafeInsetTop() > 0; 546 } else { 547 return displayCutout.getSafeInsetRight() > 0 548 || displayCutout.getSafeInsetBottom() > 0; 549 } 550 } 551 552 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)553 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 554 if (mBounds.isEmpty()) { 555 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 556 return; 557 } 558 setMeasuredDimension( 559 resolveSizeAndState(mBoundingRect.width(), widthMeasureSpec, 0), 560 resolveSizeAndState(mBoundingRect.height(), heightMeasureSpec, 0)); 561 } 562 boundsFromDirection(DisplayCutout displayCutout, int gravity, Rect out)563 public static void boundsFromDirection(DisplayCutout displayCutout, int gravity, Rect out) { 564 Region bounds = displayCutout.getBounds(); 565 switch (gravity) { 566 case Gravity.TOP: 567 bounds.op(0, 0, Integer.MAX_VALUE, displayCutout.getSafeInsetTop(), 568 Region.Op.INTERSECT); 569 out.set(bounds.getBounds()); 570 break; 571 case Gravity.LEFT: 572 bounds.op(0, 0, displayCutout.getSafeInsetLeft(), Integer.MAX_VALUE, 573 Region.Op.INTERSECT); 574 out.set(bounds.getBounds()); 575 break; 576 case Gravity.BOTTOM: 577 bounds.op(0, displayCutout.getSafeInsetTop() + 1, Integer.MAX_VALUE, 578 Integer.MAX_VALUE, Region.Op.INTERSECT); 579 out.set(bounds.getBounds()); 580 break; 581 case Gravity.RIGHT: 582 bounds.op(displayCutout.getSafeInsetLeft() + 1, 0, Integer.MAX_VALUE, 583 Integer.MAX_VALUE, Region.Op.INTERSECT); 584 out.set(bounds.getBounds()); 585 break; 586 } 587 bounds.recycle(); 588 } 589 localBounds(Rect out)590 private void localBounds(Rect out) { 591 final DisplayCutout displayCutout = mInfo.displayCutout; 592 593 if (mStart) { 594 if (displayCutout.getSafeInsetLeft() > 0) { 595 boundsFromDirection(displayCutout, Gravity.LEFT, out); 596 } else if (displayCutout.getSafeInsetTop() > 0) { 597 boundsFromDirection(displayCutout, Gravity.TOP, out); 598 } 599 } else { 600 if (displayCutout.getSafeInsetRight() > 0) { 601 boundsFromDirection(displayCutout, Gravity.RIGHT, out); 602 } else if (displayCutout.getSafeInsetBottom() > 0) { 603 boundsFromDirection(displayCutout, Gravity.BOTTOM, out); 604 } 605 } 606 } 607 608 @Override shouldInterceptTouch()609 public boolean shouldInterceptTouch() { 610 return mInfo.displayCutout != null && getVisibility() == VISIBLE; 611 } 612 613 @Override getInterceptRegion()614 public Region getInterceptRegion() { 615 if (mInfo.displayCutout == null) { 616 return null; 617 } 618 619 View rootView = getRootView(); 620 Region cutoutBounds = mInfo.displayCutout.getBounds(); 621 622 // Transform to window's coordinate space 623 rootView.getLocationOnScreen(mLocation); 624 cutoutBounds.translate(-mLocation[0], -mLocation[1]); 625 626 // Intersect with window's frame 627 cutoutBounds.op(rootView.getLeft(), rootView.getTop(), rootView.getRight(), 628 rootView.getBottom(), Region.Op.INTERSECT); 629 630 return cutoutBounds; 631 } 632 } 633 isLandscape(int rotation)634 private boolean isLandscape(int rotation) { 635 return rotation == RotationUtils.ROTATION_LANDSCAPE || rotation == 636 RotationUtils.ROTATION_SEASCAPE; 637 } 638 } 639