16a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos/* 26a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * Copyright (C) 2018 The Android Open Source Project 36a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * 46a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * Licensed under the Apache License, Version 2.0 (the "License"); 56a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * you may not use this file except in compliance with the License. 66a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * You may obtain a copy of the License at 76a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * 86a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * http://www.apache.org/licenses/LICENSE-2.0 96a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * 106a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * Unless required by applicable law or agreed to in writing, software 116a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * distributed under the License is distributed on an "AS IS" BASIS, 126a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * See the License for the specific language governing permissions and 146a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * limitations under the License. 156a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos */ 166a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos 176a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roospackage com.android.server.wm.utils; 186a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos 196a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roosimport android.graphics.Rect; 206a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roosimport android.util.Size; 216a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roosimport android.view.DisplayCutout; 226a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roosimport android.view.Gravity; 236a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos 246a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roosimport java.util.List; 256a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roosimport java.util.Objects; 266a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos 276a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos/** 286a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * Wrapper for DisplayCutout that also tracks the display size and using this allows (re)calculating 296a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * safe insets. 306a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos */ 316a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roospublic class WmDisplayCutout { 326a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos 336a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos public static final WmDisplayCutout NO_CUTOUT = new WmDisplayCutout(DisplayCutout.NO_CUTOUT, 346a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos null); 356a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos 366a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos private final DisplayCutout mInner; 376a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos private final Size mFrameSize; 386a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos 396a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos public WmDisplayCutout(DisplayCutout inner, Size frameSize) { 406a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos mInner = inner; 416a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos mFrameSize = frameSize; 426a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos } 436a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos 446a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos public static WmDisplayCutout computeSafeInsets(DisplayCutout inner, 456a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos int displayWidth, int displayHeight) { 466a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos if (inner == DisplayCutout.NO_CUTOUT || inner.isBoundsEmpty()) { 476a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos return NO_CUTOUT; 486a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos } 496a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos 506a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos final Size displaySize = new Size(displayWidth, displayHeight); 516a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos final Rect safeInsets = computeSafeInsets(displaySize, inner); 526a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos return new WmDisplayCutout(inner.replaceSafeInsets(safeInsets), displaySize); 536a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos } 546a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos 556a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos /** 566a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * Insets the reference frame of the cutout in the given directions. 576a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * 586a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * @return a copy of this instance which has been inset 596a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * @hide 606a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos */ 616a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos public WmDisplayCutout inset(int insetLeft, int insetTop, int insetRight, int insetBottom) { 626a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos DisplayCutout newInner = mInner.inset(insetLeft, insetTop, insetRight, insetBottom); 636a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos 646a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos if (mInner == newInner) { 656a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos return this; 666a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos } 676a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos 686a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos Size frame = mFrameSize == null ? null : new Size( 696a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos mFrameSize.getWidth() - insetLeft - insetRight, 706a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos mFrameSize.getHeight() - insetTop - insetBottom); 716a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos 726a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos return new WmDisplayCutout(newInner, frame); 736a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos } 746a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos 756a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos /** 766a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * Recalculates the cutout relative to the given reference frame. 776a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * 786a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * The safe insets must already have been computed, e.g. with {@link #computeSafeInsets}. 796a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * 806a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * @return a copy of this instance with the safe insets recalculated 816a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * @hide 826a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos */ 836a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos public WmDisplayCutout calculateRelativeTo(Rect frame) { 846a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos if (mInner.isEmpty()) { 856a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos return this; 866a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos } 876a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos return inset(frame.left, frame.top, 886a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos mFrameSize.getWidth() - frame.right, mFrameSize.getHeight() - frame.bottom); 896a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos } 906a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos 916a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos /** 926a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * Calculates the safe insets relative to the given display size. 936a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * 946a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * @return a copy of this instance with the safe insets calculated 956a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos * @hide 966a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos */ 976a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos public WmDisplayCutout computeSafeInsets(int width, int height) { 986a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos return computeSafeInsets(mInner, width, height); 996a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos } 1006a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos 1016a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos private static Rect computeSafeInsets(Size displaySize, DisplayCutout cutout) { 1026a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos if (displaySize.getWidth() < displaySize.getHeight()) { 1036a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos final List<Rect> boundingRects = cutout.replaceSafeInsets( 1046a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos new Rect(0, displaySize.getHeight() / 2, 0, displaySize.getHeight() / 2)) 1056a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos .getBoundingRects(); 1066a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos int topInset = findInsetForSide(displaySize, boundingRects, Gravity.TOP); 1076a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos int bottomInset = findInsetForSide(displaySize, boundingRects, Gravity.BOTTOM); 1086a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos return new Rect(0, topInset, 0, bottomInset); 1096a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos } else if (displaySize.getWidth() > displaySize.getHeight()) { 1106a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos final List<Rect> boundingRects = cutout.replaceSafeInsets( 1116a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos new Rect(displaySize.getWidth() / 2, 0, displaySize.getWidth() / 2, 0)) 1126a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos .getBoundingRects(); 1136a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos int leftInset = findInsetForSide(displaySize, boundingRects, Gravity.LEFT); 1146a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos int right = findInsetForSide(displaySize, boundingRects, Gravity.RIGHT); 1156a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos return new Rect(leftInset, 0, right, 0); 1166a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos } else { 1176a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos throw new UnsupportedOperationException("not implemented: display=" + displaySize + 1186a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos " cutout=" + cutout); 1196a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos } 1206a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos } 1216a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos 1226a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos private static int findInsetForSide(Size display, List<Rect> boundingRects, int gravity) { 1236a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos int inset = 0; 1246a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos final int size = boundingRects.size(); 1256a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos for (int i = 0; i < size; i++) { 1266a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos Rect boundingRect = boundingRects.get(i); 1276a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos switch (gravity) { 1286a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos case Gravity.TOP: 1296a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos if (boundingRect.top == 0) { 1306a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos inset = Math.max(inset, boundingRect.bottom); 1316a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos } 1326a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos break; 1336a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos case Gravity.BOTTOM: 1346a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos if (boundingRect.bottom == display.getHeight()) { 1356a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos inset = Math.max(inset, display.getHeight() - boundingRect.top); 1366a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos } 1376a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos break; 1386a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos case Gravity.LEFT: 1396a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos if (boundingRect.left == 0) { 1406a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos inset = Math.max(inset, boundingRect.right); 1416a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos } 1426a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos break; 1436a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos case Gravity.RIGHT: 1446a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos if (boundingRect.right == display.getWidth()) { 1456a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos inset = Math.max(inset, display.getWidth() - boundingRect.left); 1466a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos } 1476a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos break; 1486a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos default: 1496a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos throw new IllegalArgumentException("unknown gravity: " + gravity); 1506a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos } 1516a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos } 1526a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos return inset; 1536a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos } 1546a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos 1556a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos public DisplayCutout getDisplayCutout() { 1566a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos return mInner; 1576a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos } 1586a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos 1596a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos @Override 1606a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos public boolean equals(Object o) { 1616a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos if (!(o instanceof WmDisplayCutout)) { 1626a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos return false; 1636a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos } 1646a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos WmDisplayCutout that = (WmDisplayCutout) o; 1656a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos return Objects.equals(mInner, that.mInner) && 1666a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos Objects.equals(mFrameSize, that.mFrameSize); 1676a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos } 1686a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos 1696a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos @Override 1706a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos public int hashCode() { 1716a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos return Objects.hash(mInner, mFrameSize); 1726a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos } 1736a4fa0ec183e20c32e7816f5475e72fa9126356cAdrian Roos} 174