1 /* 2 * Copyright (C) 2018 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 android.view; 18 19 import static android.view.InsetsStateProto.DISPLAY_CUTOUT; 20 import static android.view.InsetsStateProto.DISPLAY_FRAME; 21 import static android.view.InsetsStateProto.SOURCES; 22 import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE; 23 import static android.view.WindowInsets.Type.displayCutout; 24 import static android.view.WindowInsets.Type.ime; 25 import static android.view.WindowInsets.Type.indexOf; 26 import static android.view.WindowInsets.Type.statusBars; 27 import static android.view.WindowInsets.Type.systemBars; 28 import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; 29 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; 30 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; 31 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; 32 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; 33 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; 34 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; 35 36 import android.annotation.IntDef; 37 import android.annotation.NonNull; 38 import android.annotation.Nullable; 39 import android.app.WindowConfiguration; 40 import android.graphics.Insets; 41 import android.graphics.Rect; 42 import android.os.Parcel; 43 import android.os.Parcelable; 44 import android.util.ArraySet; 45 import android.util.SparseIntArray; 46 import android.util.proto.ProtoOutputStream; 47 import android.view.WindowInsets.Type; 48 import android.view.WindowInsets.Type.InsetsType; 49 import android.view.WindowManager.LayoutParams.SoftInputModeFlags; 50 51 import com.android.internal.annotations.VisibleForTesting; 52 53 import java.io.PrintWriter; 54 import java.lang.annotation.Retention; 55 import java.lang.annotation.RetentionPolicy; 56 import java.util.Arrays; 57 import java.util.Objects; 58 import java.util.StringJoiner; 59 60 /** 61 * Holder for state of system windows that cause window insets for all other windows in the system. 62 * @hide 63 */ 64 public class InsetsState implements Parcelable { 65 66 /** 67 * Internal representation of inset source types. This is different from the public API in 68 * {@link WindowInsets.Type} as one type from the public API might indicate multiple windows 69 * at the same time. 70 */ 71 @Retention(RetentionPolicy.SOURCE) 72 @IntDef(prefix = "ITYPE", value = { 73 ITYPE_STATUS_BAR, 74 ITYPE_NAVIGATION_BAR, 75 ITYPE_CAPTION_BAR, 76 ITYPE_TOP_GESTURES, 77 ITYPE_BOTTOM_GESTURES, 78 ITYPE_LEFT_GESTURES, 79 ITYPE_RIGHT_GESTURES, 80 ITYPE_TOP_MANDATORY_GESTURES, 81 ITYPE_BOTTOM_MANDATORY_GESTURES, 82 ITYPE_LEFT_MANDATORY_GESTURES, 83 ITYPE_RIGHT_MANDATORY_GESTURES, 84 ITYPE_TOP_TAPPABLE_ELEMENT, 85 ITYPE_BOTTOM_TAPPABLE_ELEMENT, 86 ITYPE_LEFT_DISPLAY_CUTOUT, 87 ITYPE_TOP_DISPLAY_CUTOUT, 88 ITYPE_RIGHT_DISPLAY_CUTOUT, 89 ITYPE_BOTTOM_DISPLAY_CUTOUT, 90 ITYPE_IME, 91 ITYPE_CLIMATE_BAR, 92 ITYPE_EXTRA_NAVIGATION_BAR, 93 ITYPE_LEFT_GENERIC_OVERLAY, 94 ITYPE_TOP_GENERIC_OVERLAY, 95 ITYPE_RIGHT_GENERIC_OVERLAY, 96 ITYPE_BOTTOM_GENERIC_OVERLAY 97 }) 98 public @interface InternalInsetsType {} 99 100 /** 101 * Special value to be used to by methods returning an {@link InternalInsetsType} to indicate 102 * that the objects/parameters aren't associated with an {@link InternalInsetsType} 103 */ 104 public static final int ITYPE_INVALID = -1; 105 106 static final int FIRST_TYPE = 0; 107 108 public static final int ITYPE_STATUS_BAR = FIRST_TYPE; 109 public static final int ITYPE_NAVIGATION_BAR = 1; 110 public static final int ITYPE_CAPTION_BAR = 2; 111 112 public static final int ITYPE_TOP_GESTURES = 3; 113 public static final int ITYPE_BOTTOM_GESTURES = 4; 114 public static final int ITYPE_LEFT_GESTURES = 5; 115 public static final int ITYPE_RIGHT_GESTURES = 6; 116 117 public static final int ITYPE_TOP_MANDATORY_GESTURES = 7; 118 public static final int ITYPE_BOTTOM_MANDATORY_GESTURES = 8; 119 public static final int ITYPE_LEFT_MANDATORY_GESTURES = 9; 120 public static final int ITYPE_RIGHT_MANDATORY_GESTURES = 10; 121 122 public static final int ITYPE_LEFT_DISPLAY_CUTOUT = 11; 123 public static final int ITYPE_TOP_DISPLAY_CUTOUT = 12; 124 public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 13; 125 public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 14; 126 127 public static final int ITYPE_LEFT_TAPPABLE_ELEMENT = 15; 128 public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 16; 129 public static final int ITYPE_RIGHT_TAPPABLE_ELEMENT = 17; 130 public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = 18; 131 132 /** Input method window. */ 133 public static final int ITYPE_IME = 19; 134 135 /** Additional system decorations inset type. */ 136 public static final int ITYPE_CLIMATE_BAR = 20; 137 public static final int ITYPE_EXTRA_NAVIGATION_BAR = 21; 138 139 /** Additional types for local insets. **/ 140 public static final int ITYPE_LEFT_GENERIC_OVERLAY = 22; 141 public static final int ITYPE_TOP_GENERIC_OVERLAY = 23; 142 public static final int ITYPE_RIGHT_GENERIC_OVERLAY = 24; 143 public static final int ITYPE_BOTTOM_GENERIC_OVERLAY = 25; 144 145 static final int LAST_TYPE = ITYPE_BOTTOM_GENERIC_OVERLAY; 146 public static final int SIZE = LAST_TYPE + 1; 147 148 // Derived types 149 150 /** A shelf is the same as the navigation bar. */ 151 public static final int ITYPE_SHELF = ITYPE_NAVIGATION_BAR; 152 153 @Retention(RetentionPolicy.SOURCE) 154 @IntDef(prefix = "IINSETS_SIDE", value = { 155 ISIDE_LEFT, 156 ISIDE_TOP, 157 ISIDE_RIGHT, 158 ISIDE_BOTTOM, 159 ISIDE_FLOATING, 160 ISIDE_UNKNOWN 161 }) 162 public @interface InternalInsetsSide {} 163 static final int ISIDE_LEFT = 0; 164 static final int ISIDE_TOP = 1; 165 static final int ISIDE_RIGHT = 2; 166 static final int ISIDE_BOTTOM = 3; 167 static final int ISIDE_FLOATING = 4; 168 static final int ISIDE_UNKNOWN = 5; 169 170 private final InsetsSource[] mSources = new InsetsSource[SIZE]; 171 172 /** 173 * The frame of the display these sources are relative to. 174 */ 175 private final Rect mDisplayFrame = new Rect(); 176 177 /** The area cut from the display. */ 178 private final DisplayCutout.ParcelableWrapper mDisplayCutout = 179 new DisplayCutout.ParcelableWrapper(); 180 181 /** 182 * The frame that rounded corners are relative to. 183 * 184 * There are 2 cases that will draw fake rounded corners: 185 * 1. In split-screen mode 186 * 2. Devices with a task bar 187 * We need to report these fake rounded corners to apps by re-calculating based on this frame. 188 */ 189 private final Rect mRoundedCornerFrame = new Rect(); 190 191 /** The rounded corners on the display */ 192 private RoundedCorners mRoundedCorners = RoundedCorners.NO_ROUNDED_CORNERS; 193 194 /** The bounds of the Privacy Indicator */ 195 private PrivacyIndicatorBounds mPrivacyIndicatorBounds = 196 new PrivacyIndicatorBounds(); 197 InsetsState()198 public InsetsState() { 199 } 200 InsetsState(InsetsState copy)201 public InsetsState(InsetsState copy) { 202 set(copy); 203 } 204 InsetsState(InsetsState copy, boolean copySources)205 public InsetsState(InsetsState copy, boolean copySources) { 206 set(copy, copySources); 207 } 208 209 /** 210 * Calculates {@link WindowInsets} based on the current source configuration. 211 * 212 * @param frame The frame to calculate the insets relative to. 213 * @param ignoringVisibilityState {@link InsetsState} used to calculate 214 * {@link WindowInsets#getInsetsIgnoringVisibility(int)} information, or pass 215 * {@code null} to use this state to calculate that information. 216 * @return The calculated insets. 217 */ calculateInsets(Rect frame, @Nullable InsetsState ignoringVisibilityState, boolean isScreenRound, boolean alwaysConsumeSystemBars, int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags, int windowType, @WindowConfiguration.WindowingMode int windowingMode, @Nullable @InternalInsetsSide SparseIntArray typeSideMap)218 public WindowInsets calculateInsets(Rect frame, @Nullable InsetsState ignoringVisibilityState, 219 boolean isScreenRound, boolean alwaysConsumeSystemBars, 220 int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags, 221 int windowType, @WindowConfiguration.WindowingMode int windowingMode, 222 @Nullable @InternalInsetsSide SparseIntArray typeSideMap) { 223 Insets[] typeInsetsMap = new Insets[Type.SIZE]; 224 Insets[] typeMaxInsetsMap = new Insets[Type.SIZE]; 225 boolean[] typeVisibilityMap = new boolean[Type.SIZE]; 226 final Rect relativeFrame = new Rect(frame); 227 final Rect relativeFrameMax = new Rect(frame); 228 for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) { 229 InsetsSource source = mSources[type]; 230 if (source == null) { 231 int index = indexOf(toPublicType(type)); 232 if (typeInsetsMap[index] == null) { 233 typeInsetsMap[index] = Insets.NONE; 234 } 235 continue; 236 } 237 238 processSource(source, relativeFrame, false /* ignoreVisibility */, typeInsetsMap, 239 typeSideMap, typeVisibilityMap); 240 241 // IME won't be reported in max insets as the size depends on the EditorInfo of the IME 242 // target. 243 if (source.getType() != ITYPE_IME) { 244 InsetsSource ignoringVisibilitySource = ignoringVisibilityState != null 245 ? ignoringVisibilityState.getSource(type) 246 : source; 247 if (ignoringVisibilitySource == null) { 248 continue; 249 } 250 processSource(ignoringVisibilitySource, relativeFrameMax, 251 true /* ignoreVisibility */, typeMaxInsetsMap, null /* typeSideMap */, 252 null /* typeVisibilityMap */); 253 } 254 } 255 final int softInputAdjustMode = legacySoftInputMode & SOFT_INPUT_MASK_ADJUST; 256 257 @InsetsType int compatInsetsTypes = systemBars() | displayCutout(); 258 if (softInputAdjustMode == SOFT_INPUT_ADJUST_RESIZE) { 259 compatInsetsTypes |= ime(); 260 } 261 if ((legacyWindowFlags & FLAG_FULLSCREEN) != 0) { 262 compatInsetsTypes &= ~statusBars(); 263 } 264 if (clearsCompatInsets(windowType, legacyWindowFlags, windowingMode)) { 265 compatInsetsTypes = 0; 266 } 267 268 return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound, 269 alwaysConsumeSystemBars, calculateRelativeCutout(frame), 270 calculateRelativeRoundedCorners(frame), 271 calculateRelativePrivacyIndicatorBounds(frame), 272 compatInsetsTypes, (legacySystemUiFlags & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0); 273 } 274 calculateRelativeCutout(Rect frame)275 private DisplayCutout calculateRelativeCutout(Rect frame) { 276 final DisplayCutout raw = mDisplayCutout.get(); 277 if (mDisplayFrame.equals(frame)) { 278 return raw; 279 } 280 if (frame == null) { 281 return DisplayCutout.NO_CUTOUT; 282 } 283 final int insetLeft = frame.left - mDisplayFrame.left; 284 final int insetTop = frame.top - mDisplayFrame.top; 285 final int insetRight = mDisplayFrame.right - frame.right; 286 final int insetBottom = mDisplayFrame.bottom - frame.bottom; 287 if (insetLeft >= raw.getSafeInsetLeft() 288 && insetTop >= raw.getSafeInsetTop() 289 && insetRight >= raw.getSafeInsetRight() 290 && insetBottom >= raw.getSafeInsetBottom()) { 291 return DisplayCutout.NO_CUTOUT; 292 } 293 return raw.inset(insetLeft, insetTop, insetRight, insetBottom); 294 } 295 calculateRelativeRoundedCorners(Rect frame)296 private RoundedCorners calculateRelativeRoundedCorners(Rect frame) { 297 if (frame == null) { 298 return RoundedCorners.NO_ROUNDED_CORNERS; 299 } 300 // If mRoundedCornerFrame is set, we should calculate the new RoundedCorners based on this 301 // frame. 302 final Rect roundedCornerFrame = new Rect(mRoundedCornerFrame); 303 for (InsetsSource source : mSources) { 304 if (source != null && source.getInsetsRoundedCornerFrame()) { 305 final Insets insets = source.calculateInsets(roundedCornerFrame, false); 306 roundedCornerFrame.inset(insets); 307 } 308 } 309 if (!roundedCornerFrame.isEmpty() && !roundedCornerFrame.equals(mDisplayFrame)) { 310 return mRoundedCorners.insetWithFrame(frame, roundedCornerFrame); 311 } 312 if (mDisplayFrame.equals(frame)) { 313 return mRoundedCorners; 314 } 315 final int insetLeft = frame.left - mDisplayFrame.left; 316 final int insetTop = frame.top - mDisplayFrame.top; 317 final int insetRight = mDisplayFrame.right - frame.right; 318 final int insetBottom = mDisplayFrame.bottom - frame.bottom; 319 return mRoundedCorners.inset(insetLeft, insetTop, insetRight, insetBottom); 320 } 321 calculateRelativePrivacyIndicatorBounds(Rect frame)322 private PrivacyIndicatorBounds calculateRelativePrivacyIndicatorBounds(Rect frame) { 323 if (mDisplayFrame.equals(frame)) { 324 return mPrivacyIndicatorBounds; 325 } 326 if (frame == null) { 327 return null; 328 } 329 final int insetLeft = frame.left - mDisplayFrame.left; 330 final int insetTop = frame.top - mDisplayFrame.top; 331 final int insetRight = mDisplayFrame.right - frame.right; 332 final int insetBottom = mDisplayFrame.bottom - frame.bottom; 333 return mPrivacyIndicatorBounds.inset(insetLeft, insetTop, insetRight, insetBottom); 334 } 335 calculateInsets(Rect frame, @InsetsType int types, boolean ignoreVisibility)336 public Insets calculateInsets(Rect frame, @InsetsType int types, boolean ignoreVisibility) { 337 Insets insets = Insets.NONE; 338 for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) { 339 InsetsSource source = mSources[type]; 340 if (source == null) { 341 continue; 342 } 343 int publicType = InsetsState.toPublicType(type); 344 if ((publicType & types) == 0) { 345 continue; 346 } 347 insets = Insets.max(source.calculateInsets(frame, ignoreVisibility), insets); 348 } 349 return insets; 350 } 351 calculateInsets(Rect frame, @InsetsType int types, InsetsVisibilities overrideVisibilities)352 public Insets calculateInsets(Rect frame, @InsetsType int types, 353 InsetsVisibilities overrideVisibilities) { 354 Insets insets = Insets.NONE; 355 for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) { 356 InsetsSource source = mSources[type]; 357 if (source == null) { 358 continue; 359 } 360 int publicType = InsetsState.toPublicType(type); 361 if ((publicType & types) == 0) { 362 continue; 363 } 364 if (!overrideVisibilities.getVisibility(type)) { 365 continue; 366 } 367 insets = Insets.max(source.calculateInsets(frame, true), insets); 368 } 369 return insets; 370 } 371 calculateVisibleInsets(Rect frame, int windowType, int windowingMode, @SoftInputModeFlags int softInputMode, int windowFlags)372 public Insets calculateVisibleInsets(Rect frame, int windowType, int windowingMode, 373 @SoftInputModeFlags int softInputMode, int windowFlags) { 374 if (clearsCompatInsets(windowType, windowFlags, windowingMode)) { 375 return Insets.NONE; 376 } 377 final int softInputAdjustMode = softInputMode & SOFT_INPUT_MASK_ADJUST; 378 final int visibleInsetsTypes = softInputAdjustMode != SOFT_INPUT_ADJUST_NOTHING 379 ? systemBars() | ime() 380 : systemBars(); 381 Insets insets = Insets.NONE; 382 for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) { 383 InsetsSource source = mSources[type]; 384 if (source == null) { 385 continue; 386 } 387 final int publicType = InsetsState.toPublicType(type); 388 if ((publicType & visibleInsetsTypes) == 0) { 389 continue; 390 } 391 insets = Insets.max(source.calculateVisibleInsets(frame), insets); 392 } 393 return insets; 394 } 395 396 /** 397 * Calculate which insets *cannot* be controlled, because the frame does not cover the 398 * respective side of the inset. 399 * 400 * If the frame of our window doesn't cover the entire inset, the control API makes very 401 * little sense, as we don't deal with negative insets. 402 */ 403 @InsetsType calculateUncontrollableInsetsFromFrame(Rect frame)404 public int calculateUncontrollableInsetsFromFrame(Rect frame) { 405 int blocked = 0; 406 for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) { 407 InsetsSource source = mSources[type]; 408 if (source == null) { 409 continue; 410 } 411 if (!canControlSource(frame, source)) { 412 blocked |= toPublicType(type); 413 } 414 } 415 return blocked; 416 } 417 canControlSource(Rect frame, InsetsSource source)418 private static boolean canControlSource(Rect frame, InsetsSource source) { 419 final Insets insets = source.calculateInsets(frame, true /* ignoreVisibility */); 420 final Rect sourceFrame = source.getFrame(); 421 final int sourceWidth = sourceFrame.width(); 422 final int sourceHeight = sourceFrame.height(); 423 return insets.left == sourceWidth || insets.right == sourceWidth 424 || insets.top == sourceHeight || insets.bottom == sourceHeight; 425 } 426 processSource(InsetsSource source, Rect relativeFrame, boolean ignoreVisibility, Insets[] typeInsetsMap, @Nullable @InternalInsetsSide SparseIntArray typeSideMap, @Nullable boolean[] typeVisibilityMap)427 private void processSource(InsetsSource source, Rect relativeFrame, boolean ignoreVisibility, 428 Insets[] typeInsetsMap, @Nullable @InternalInsetsSide SparseIntArray typeSideMap, 429 @Nullable boolean[] typeVisibilityMap) { 430 Insets insets = source.calculateInsets(relativeFrame, ignoreVisibility); 431 432 int type = toPublicType(source.getType()); 433 processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap, 434 insets, type); 435 436 if (type == Type.MANDATORY_SYSTEM_GESTURES) { 437 // Mandatory system gestures are also system gestures. 438 // TODO: find a way to express this more generally. One option would be to define 439 // Type.systemGestureInsets() as NORMAL | MANDATORY, but then we lose the 440 // ability to set systemGestureInsets() independently from 441 // mandatorySystemGestureInsets() in the Builder. 442 processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap, 443 insets, Type.SYSTEM_GESTURES); 444 } 445 if (type == Type.CAPTION_BAR) { 446 // Caption should also be gesture and tappable elements. This should not be needed when 447 // the caption is added from the shell, as the shell can add other types at the same 448 // time. 449 processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap, 450 insets, Type.SYSTEM_GESTURES); 451 processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap, 452 insets, Type.MANDATORY_SYSTEM_GESTURES); 453 processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap, 454 insets, Type.TAPPABLE_ELEMENT); 455 } 456 } 457 processSourceAsPublicType(InsetsSource source, Insets[] typeInsetsMap, @InternalInsetsSide @Nullable SparseIntArray typeSideMap, @Nullable boolean[] typeVisibilityMap, Insets insets, int type)458 private void processSourceAsPublicType(InsetsSource source, Insets[] typeInsetsMap, 459 @InternalInsetsSide @Nullable SparseIntArray typeSideMap, 460 @Nullable boolean[] typeVisibilityMap, Insets insets, int type) { 461 int index = indexOf(type); 462 Insets existing = typeInsetsMap[index]; 463 if (existing == null) { 464 typeInsetsMap[index] = insets; 465 } else { 466 typeInsetsMap[index] = Insets.max(existing, insets); 467 } 468 469 if (typeVisibilityMap != null) { 470 typeVisibilityMap[index] = source.isVisible(); 471 } 472 473 if (typeSideMap != null) { 474 @InternalInsetsSide int insetSide = getInsetSide(insets); 475 if (insetSide != ISIDE_UNKNOWN) { 476 typeSideMap.put(source.getType(), insetSide); 477 } 478 } 479 } 480 481 /** 482 * Retrieves the side for a certain {@code insets}. It is required that only one field l/t/r/b 483 * is set in order that this method returns a meaningful result. 484 */ getInsetSide(Insets insets)485 static @InternalInsetsSide int getInsetSide(Insets insets) { 486 if (Insets.NONE.equals(insets)) { 487 return ISIDE_FLOATING; 488 } 489 if (insets.left != 0) { 490 return ISIDE_LEFT; 491 } 492 if (insets.top != 0) { 493 return ISIDE_TOP; 494 } 495 if (insets.right != 0) { 496 return ISIDE_RIGHT; 497 } 498 if (insets.bottom != 0) { 499 return ISIDE_BOTTOM; 500 } 501 return ISIDE_UNKNOWN; 502 } 503 getSource(@nternalInsetsType int type)504 public InsetsSource getSource(@InternalInsetsType int type) { 505 InsetsSource source = mSources[type]; 506 if (source != null) { 507 return source; 508 } 509 source = new InsetsSource(type); 510 mSources[type] = source; 511 return source; 512 } 513 peekSource(@nternalInsetsType int type)514 public @Nullable InsetsSource peekSource(@InternalInsetsType int type) { 515 return mSources[type]; 516 } 517 518 /** 519 * Returns the source visibility or the default visibility if the source doesn't exist. This is 520 * useful if when treating this object as a request. 521 * 522 * @param type The {@link InternalInsetsType} to query. 523 * @return {@code true} if the source is visible or the type is default visible and the source 524 * doesn't exist. 525 */ getSourceOrDefaultVisibility(@nternalInsetsType int type)526 public boolean getSourceOrDefaultVisibility(@InternalInsetsType int type) { 527 final InsetsSource source = mSources[type]; 528 return source != null ? source.isVisible() : getDefaultVisibility(type); 529 } 530 setDisplayFrame(Rect frame)531 public void setDisplayFrame(Rect frame) { 532 mDisplayFrame.set(frame); 533 } 534 getDisplayFrame()535 public Rect getDisplayFrame() { 536 return mDisplayFrame; 537 } 538 setDisplayCutout(DisplayCutout cutout)539 public void setDisplayCutout(DisplayCutout cutout) { 540 mDisplayCutout.set(cutout); 541 } 542 getDisplayCutout()543 public DisplayCutout getDisplayCutout() { 544 return mDisplayCutout.get(); 545 } 546 getDisplayCutoutSafe(Rect outBounds)547 public void getDisplayCutoutSafe(Rect outBounds) { 548 outBounds.set( 549 WindowLayout.MIN_X, WindowLayout.MIN_Y, WindowLayout.MAX_X, WindowLayout.MAX_Y); 550 final DisplayCutout cutout = mDisplayCutout.get(); 551 final Rect displayFrame = mDisplayFrame; 552 if (!cutout.isEmpty()) { 553 if (cutout.getSafeInsetLeft() > 0) { 554 outBounds.left = displayFrame.left + cutout.getSafeInsetLeft(); 555 } 556 if (cutout.getSafeInsetTop() > 0) { 557 outBounds.top = displayFrame.top + cutout.getSafeInsetTop(); 558 } 559 if (cutout.getSafeInsetRight() > 0) { 560 outBounds.right = displayFrame.right - cutout.getSafeInsetRight(); 561 } 562 if (cutout.getSafeInsetBottom() > 0) { 563 outBounds.bottom = displayFrame.bottom - cutout.getSafeInsetBottom(); 564 } 565 } 566 } 567 setRoundedCorners(RoundedCorners roundedCorners)568 public void setRoundedCorners(RoundedCorners roundedCorners) { 569 mRoundedCorners = roundedCorners; 570 } 571 getRoundedCorners()572 public RoundedCorners getRoundedCorners() { 573 return mRoundedCorners; 574 } 575 576 /** 577 * Set the frame that will be used to calculate the rounded corners. 578 * 579 * @see #mRoundedCornerFrame 580 */ setRoundedCornerFrame(Rect frame)581 public void setRoundedCornerFrame(Rect frame) { 582 mRoundedCornerFrame.set(frame); 583 } 584 setPrivacyIndicatorBounds(PrivacyIndicatorBounds bounds)585 public void setPrivacyIndicatorBounds(PrivacyIndicatorBounds bounds) { 586 mPrivacyIndicatorBounds = bounds; 587 } 588 getPrivacyIndicatorBounds()589 public PrivacyIndicatorBounds getPrivacyIndicatorBounds() { 590 return mPrivacyIndicatorBounds; 591 } 592 593 /** 594 * Modifies the state of this class to exclude a certain type to make it ready for dispatching 595 * to the client. 596 * 597 * @param type The {@link InternalInsetsType} of the source to remove 598 * @return {@code true} if this InsetsState was modified; {@code false} otherwise. 599 */ removeSource(@nternalInsetsType int type)600 public boolean removeSource(@InternalInsetsType int type) { 601 if (mSources[type] == null) { 602 return false; 603 } 604 mSources[type] = null; 605 return true; 606 } 607 608 /** 609 * A shortcut for setting the visibility of the source. 610 * 611 * @param type The {@link InternalInsetsType} of the source to set the visibility 612 * @param visible {@code true} for visible 613 */ setSourceVisible(@nternalInsetsType int type, boolean visible)614 public void setSourceVisible(@InternalInsetsType int type, boolean visible) { 615 InsetsSource source = mSources[type]; 616 if (source != null) { 617 source.setVisible(visible); 618 } 619 } 620 621 /** 622 * Scales the frame and the visible frame (if there is one) of each source. 623 * 624 * @param scale the scale to be applied 625 */ scale(float scale)626 public void scale(float scale) { 627 mDisplayFrame.scale(scale); 628 mDisplayCutout.scale(scale); 629 mRoundedCorners = mRoundedCorners.scale(scale); 630 mRoundedCornerFrame.scale(scale); 631 mPrivacyIndicatorBounds = mPrivacyIndicatorBounds.scale(scale); 632 for (int i = 0; i < SIZE; i++) { 633 final InsetsSource source = mSources[i]; 634 if (source != null) { 635 source.getFrame().scale(scale); 636 final Rect visibleFrame = source.getVisibleFrame(); 637 if (visibleFrame != null) { 638 visibleFrame.scale(scale); 639 } 640 } 641 } 642 } 643 set(InsetsState other)644 public void set(InsetsState other) { 645 set(other, false /* copySources */); 646 } 647 set(InsetsState other, boolean copySources)648 public void set(InsetsState other, boolean copySources) { 649 mDisplayFrame.set(other.mDisplayFrame); 650 mDisplayCutout.set(other.mDisplayCutout); 651 mRoundedCorners = other.getRoundedCorners(); 652 mRoundedCornerFrame.set(other.mRoundedCornerFrame); 653 mPrivacyIndicatorBounds = other.getPrivacyIndicatorBounds(); 654 if (copySources) { 655 for (int i = 0; i < SIZE; i++) { 656 InsetsSource source = other.mSources[i]; 657 mSources[i] = source != null ? new InsetsSource(source) : null; 658 } 659 } else { 660 for (int i = 0; i < SIZE; i++) { 661 mSources[i] = other.mSources[i]; 662 } 663 } 664 } 665 666 /** 667 * Sets the values from the other InsetsState. But for sources, only specific types of source 668 * would be set. 669 * 670 * @param other the other InsetsState. 671 * @param types the only types of sources would be set. 672 */ set(InsetsState other, @InsetsType int types)673 public void set(InsetsState other, @InsetsType int types) { 674 mDisplayFrame.set(other.mDisplayFrame); 675 mDisplayCutout.set(other.mDisplayCutout); 676 mRoundedCorners = other.getRoundedCorners(); 677 mRoundedCornerFrame.set(other.mRoundedCornerFrame); 678 mPrivacyIndicatorBounds = other.getPrivacyIndicatorBounds(); 679 final ArraySet<Integer> t = toInternalType(types); 680 for (int i = t.size() - 1; i >= 0; i--) { 681 final int type = t.valueAt(i); 682 mSources[type] = other.mSources[type]; 683 } 684 } 685 addSource(InsetsSource source)686 public void addSource(InsetsSource source) { 687 mSources[source.getType()] = source; 688 } 689 clearsCompatInsets(int windowType, int windowFlags, int windowingMode)690 public static boolean clearsCompatInsets(int windowType, int windowFlags, int windowingMode) { 691 return (windowFlags & FLAG_LAYOUT_NO_LIMITS) != 0 692 && windowType != TYPE_WALLPAPER && windowType != TYPE_SYSTEM_ERROR 693 && !WindowConfiguration.inMultiWindowMode(windowingMode); 694 } 695 toInternalType(@nsetsType int types)696 public static @InternalInsetsType ArraySet<Integer> toInternalType(@InsetsType int types) { 697 final ArraySet<Integer> result = new ArraySet<>(); 698 if ((types & Type.STATUS_BARS) != 0) { 699 result.add(ITYPE_STATUS_BAR); 700 result.add(ITYPE_CLIMATE_BAR); 701 } 702 if ((types & Type.NAVIGATION_BARS) != 0) { 703 result.add(ITYPE_NAVIGATION_BAR); 704 result.add(ITYPE_EXTRA_NAVIGATION_BAR); 705 } 706 if ((types & Type.GENERIC_OVERLAYS) != 0) { 707 result.add(ITYPE_LEFT_GENERIC_OVERLAY); 708 result.add(ITYPE_TOP_GENERIC_OVERLAY); 709 result.add(ITYPE_RIGHT_GENERIC_OVERLAY); 710 result.add(ITYPE_BOTTOM_GENERIC_OVERLAY); 711 } 712 if ((types & Type.CAPTION_BAR) != 0) { 713 result.add(ITYPE_CAPTION_BAR); 714 } 715 if ((types & Type.SYSTEM_GESTURES) != 0) { 716 result.add(ITYPE_LEFT_GESTURES); 717 result.add(ITYPE_TOP_GESTURES); 718 result.add(ITYPE_RIGHT_GESTURES); 719 result.add(ITYPE_BOTTOM_GESTURES); 720 } 721 if ((types & Type.MANDATORY_SYSTEM_GESTURES) != 0) { 722 result.add(ITYPE_LEFT_MANDATORY_GESTURES); 723 result.add(ITYPE_TOP_MANDATORY_GESTURES); 724 result.add(ITYPE_RIGHT_MANDATORY_GESTURES); 725 result.add(ITYPE_BOTTOM_MANDATORY_GESTURES); 726 } 727 if ((types & Type.DISPLAY_CUTOUT) != 0) { 728 result.add(ITYPE_LEFT_DISPLAY_CUTOUT); 729 result.add(ITYPE_TOP_DISPLAY_CUTOUT); 730 result.add(ITYPE_RIGHT_DISPLAY_CUTOUT); 731 result.add(ITYPE_BOTTOM_DISPLAY_CUTOUT); 732 } 733 if ((types & Type.IME) != 0) { 734 result.add(ITYPE_IME); 735 } 736 return result; 737 } 738 739 /** 740 * Converting a internal type to the public type. 741 * @param type internal insets type, {@code InternalInsetsType}. 742 * @return public insets type, {@code Type.InsetsType}. 743 */ toPublicType(@nternalInsetsType int type)744 public static @Type.InsetsType int toPublicType(@InternalInsetsType int type) { 745 switch (type) { 746 case ITYPE_STATUS_BAR: 747 case ITYPE_CLIMATE_BAR: 748 return Type.STATUS_BARS; 749 case ITYPE_NAVIGATION_BAR: 750 case ITYPE_EXTRA_NAVIGATION_BAR: 751 return Type.NAVIGATION_BARS; 752 case ITYPE_LEFT_GENERIC_OVERLAY: 753 case ITYPE_TOP_GENERIC_OVERLAY: 754 case ITYPE_RIGHT_GENERIC_OVERLAY: 755 case ITYPE_BOTTOM_GENERIC_OVERLAY: 756 return Type.GENERIC_OVERLAYS; 757 case ITYPE_CAPTION_BAR: 758 return Type.CAPTION_BAR; 759 case ITYPE_IME: 760 return Type.IME; 761 case ITYPE_TOP_MANDATORY_GESTURES: 762 case ITYPE_BOTTOM_MANDATORY_GESTURES: 763 case ITYPE_LEFT_MANDATORY_GESTURES: 764 case ITYPE_RIGHT_MANDATORY_GESTURES: 765 return Type.MANDATORY_SYSTEM_GESTURES; 766 case ITYPE_TOP_GESTURES: 767 case ITYPE_BOTTOM_GESTURES: 768 case ITYPE_LEFT_GESTURES: 769 case ITYPE_RIGHT_GESTURES: 770 return Type.SYSTEM_GESTURES; 771 case ITYPE_LEFT_TAPPABLE_ELEMENT: 772 case ITYPE_TOP_TAPPABLE_ELEMENT: 773 case ITYPE_RIGHT_TAPPABLE_ELEMENT: 774 case ITYPE_BOTTOM_TAPPABLE_ELEMENT: 775 return Type.TAPPABLE_ELEMENT; 776 case ITYPE_LEFT_DISPLAY_CUTOUT: 777 case ITYPE_TOP_DISPLAY_CUTOUT: 778 case ITYPE_RIGHT_DISPLAY_CUTOUT: 779 case ITYPE_BOTTOM_DISPLAY_CUTOUT: 780 return Type.DISPLAY_CUTOUT; 781 default: 782 throw new IllegalArgumentException("Unknown type: " + type); 783 } 784 } 785 getDefaultVisibility(@nternalInsetsType int type)786 public static boolean getDefaultVisibility(@InternalInsetsType int type) { 787 return type != ITYPE_IME; 788 } 789 containsType(@nternalInsetsType int[] types, @InternalInsetsType int type)790 public static boolean containsType(@InternalInsetsType int[] types, 791 @InternalInsetsType int type) { 792 if (types == null) { 793 return false; 794 } 795 for (int t : types) { 796 if (t == type) { 797 return true; 798 } 799 } 800 return false; 801 } 802 dump(String prefix, PrintWriter pw)803 public void dump(String prefix, PrintWriter pw) { 804 final String newPrefix = prefix + " "; 805 pw.println(prefix + "InsetsState"); 806 pw.println(newPrefix + "mDisplayFrame=" + mDisplayFrame); 807 pw.println(newPrefix + "mDisplayCutout=" + mDisplayCutout.get()); 808 pw.println(newPrefix + "mRoundedCorners=" + mRoundedCorners); 809 pw.println(newPrefix + "mRoundedCornerFrame=" + mRoundedCornerFrame); 810 pw.println(newPrefix + "mPrivacyIndicatorBounds=" + mPrivacyIndicatorBounds); 811 for (int i = 0; i < SIZE; i++) { 812 InsetsSource source = mSources[i]; 813 if (source == null) continue; 814 source.dump(newPrefix + " ", pw); 815 } 816 } 817 dumpDebug(ProtoOutputStream proto, long fieldId)818 void dumpDebug(ProtoOutputStream proto, long fieldId) { 819 final long token = proto.start(fieldId); 820 InsetsSource source = mSources[ITYPE_IME]; 821 if (source != null) { 822 source.dumpDebug(proto, SOURCES); 823 } 824 mDisplayFrame.dumpDebug(proto, DISPLAY_FRAME); 825 mDisplayCutout.get().dumpDebug(proto, DISPLAY_CUTOUT); 826 proto.end(token); 827 } 828 typeToString(@nternalInsetsType int type)829 public static String typeToString(@InternalInsetsType int type) { 830 switch (type) { 831 case ITYPE_STATUS_BAR: 832 return "ITYPE_STATUS_BAR"; 833 case ITYPE_NAVIGATION_BAR: 834 return "ITYPE_NAVIGATION_BAR"; 835 case ITYPE_CAPTION_BAR: 836 return "ITYPE_CAPTION_BAR"; 837 case ITYPE_TOP_GESTURES: 838 return "ITYPE_TOP_GESTURES"; 839 case ITYPE_BOTTOM_GESTURES: 840 return "ITYPE_BOTTOM_GESTURES"; 841 case ITYPE_LEFT_GESTURES: 842 return "ITYPE_LEFT_GESTURES"; 843 case ITYPE_RIGHT_GESTURES: 844 return "ITYPE_RIGHT_GESTURES"; 845 case ITYPE_TOP_MANDATORY_GESTURES: 846 return "ITYPE_TOP_MANDATORY_GESTURES"; 847 case ITYPE_BOTTOM_MANDATORY_GESTURES: 848 return "ITYPE_BOTTOM_MANDATORY_GESTURES"; 849 case ITYPE_LEFT_MANDATORY_GESTURES: 850 return "ITYPE_LEFT_MANDATORY_GESTURES"; 851 case ITYPE_RIGHT_MANDATORY_GESTURES: 852 return "ITYPE_RIGHT_MANDATORY_GESTURES"; 853 case ITYPE_LEFT_TAPPABLE_ELEMENT: 854 return "ITYPE_LEFT_TAPPABLE_ELEMENT"; 855 case ITYPE_TOP_TAPPABLE_ELEMENT: 856 return "ITYPE_TOP_TAPPABLE_ELEMENT"; 857 case ITYPE_RIGHT_TAPPABLE_ELEMENT: 858 return "ITYPE_RIGHT_TAPPABLE_ELEMENT"; 859 case ITYPE_BOTTOM_TAPPABLE_ELEMENT: 860 return "ITYPE_BOTTOM_TAPPABLE_ELEMENT"; 861 case ITYPE_LEFT_DISPLAY_CUTOUT: 862 return "ITYPE_LEFT_DISPLAY_CUTOUT"; 863 case ITYPE_TOP_DISPLAY_CUTOUT: 864 return "ITYPE_TOP_DISPLAY_CUTOUT"; 865 case ITYPE_RIGHT_DISPLAY_CUTOUT: 866 return "ITYPE_RIGHT_DISPLAY_CUTOUT"; 867 case ITYPE_BOTTOM_DISPLAY_CUTOUT: 868 return "ITYPE_BOTTOM_DISPLAY_CUTOUT"; 869 case ITYPE_IME: 870 return "ITYPE_IME"; 871 case ITYPE_CLIMATE_BAR: 872 return "ITYPE_CLIMATE_BAR"; 873 case ITYPE_EXTRA_NAVIGATION_BAR: 874 return "ITYPE_EXTRA_NAVIGATION_BAR"; 875 case ITYPE_LEFT_GENERIC_OVERLAY: 876 return "ITYPE_LEFT_GENERIC_OVERLAY"; 877 case ITYPE_TOP_GENERIC_OVERLAY: 878 return "ITYPE_TOP_GENERIC_OVERLAY"; 879 case ITYPE_RIGHT_GENERIC_OVERLAY: 880 return "ITYPE_RIGHT_GENERIC_OVERLAY"; 881 case ITYPE_BOTTOM_GENERIC_OVERLAY: 882 return "ITYPE_BOTTOM_GENERIC_OVERLAY"; 883 default: 884 return "ITYPE_UNKNOWN_" + type; 885 } 886 } 887 888 @Override equals(@ullable Object o)889 public boolean equals(@Nullable Object o) { 890 return equals(o, false, false); 891 } 892 893 /** 894 * An equals method can exclude the caption insets. This is useful because we assemble the 895 * caption insets information on the client side, and when we communicate with server, it's 896 * excluded. 897 * @param excludingCaptionInsets {@code true} if we want to compare two InsetsState objects but 898 * ignore the caption insets source value. 899 * @param excludeInvisibleImeFrames If {@link #ITYPE_IME} frames should be ignored when IME is 900 * not visible. 901 * @return {@code true} if the two InsetsState objects are equal, {@code false} otherwise. 902 */ 903 @VisibleForTesting equals(@ullable Object o, boolean excludingCaptionInsets, boolean excludeInvisibleImeFrames)904 public boolean equals(@Nullable Object o, boolean excludingCaptionInsets, 905 boolean excludeInvisibleImeFrames) { 906 if (this == o) { return true; } 907 if (o == null || getClass() != o.getClass()) { return false; } 908 909 InsetsState state = (InsetsState) o; 910 911 if (!mDisplayFrame.equals(state.mDisplayFrame) 912 || !mDisplayCutout.equals(state.mDisplayCutout) 913 || !mRoundedCorners.equals(state.mRoundedCorners) 914 || !mRoundedCornerFrame.equals(state.mRoundedCornerFrame) 915 || !mPrivacyIndicatorBounds.equals(state.mPrivacyIndicatorBounds)) { 916 return false; 917 } 918 for (int i = 0; i < SIZE; i++) { 919 if (excludingCaptionInsets) { 920 if (i == ITYPE_CAPTION_BAR) continue; 921 } 922 InsetsSource source = mSources[i]; 923 InsetsSource otherSource = state.mSources[i]; 924 if (source == null && otherSource == null) { 925 continue; 926 } 927 if (excludeInvisibleImeFrames && i == ITYPE_IME 928 && ((source == null && !otherSource.isVisible()) 929 || (otherSource == null && !source.isVisible()))) { 930 continue; 931 } 932 if (source == null || otherSource == null) { 933 return false; 934 } 935 if (!otherSource.equals(source, excludeInvisibleImeFrames)) { 936 return false; 937 } 938 } 939 return true; 940 } 941 942 @Override hashCode()943 public int hashCode() { 944 return Objects.hash(mDisplayFrame, mDisplayCutout, Arrays.hashCode(mSources), 945 mRoundedCorners, mPrivacyIndicatorBounds, mRoundedCornerFrame); 946 } 947 InsetsState(Parcel in)948 public InsetsState(Parcel in) { 949 readFromParcel(in); 950 } 951 952 @Override describeContents()953 public int describeContents() { 954 return 0; 955 } 956 957 @Override writeToParcel(Parcel dest, int flags)958 public void writeToParcel(Parcel dest, int flags) { 959 mDisplayFrame.writeToParcel(dest, flags); 960 mDisplayCutout.writeToParcel(dest, flags); 961 dest.writeTypedArray(mSources, 0 /* parcelableFlags */); 962 dest.writeTypedObject(mRoundedCorners, flags); 963 mRoundedCornerFrame.writeToParcel(dest, flags); 964 dest.writeTypedObject(mPrivacyIndicatorBounds, flags); 965 } 966 967 public static final @NonNull Creator<InsetsState> CREATOR = new Creator<InsetsState>() { 968 969 public InsetsState createFromParcel(Parcel in) { 970 return new InsetsState(in); 971 } 972 973 public InsetsState[] newArray(int size) { 974 return new InsetsState[size]; 975 } 976 }; 977 readFromParcel(Parcel in)978 public void readFromParcel(Parcel in) { 979 mDisplayFrame.readFromParcel(in); 980 mDisplayCutout.readFromParcel(in); 981 in.readTypedArray(mSources, InsetsSource.CREATOR); 982 mRoundedCorners = in.readTypedObject(RoundedCorners.CREATOR); 983 mRoundedCornerFrame.readFromParcel(in); 984 mPrivacyIndicatorBounds = in.readTypedObject(PrivacyIndicatorBounds.CREATOR); 985 } 986 987 @Override toString()988 public String toString() { 989 StringJoiner joiner = new StringJoiner(", "); 990 for (int i = 0; i < SIZE; i++) { 991 InsetsSource source = mSources[i]; 992 if (source != null) { 993 joiner.add(source.toString()); 994 } 995 } 996 return "InsetsState: {" 997 + "mDisplayFrame=" + mDisplayFrame 998 + ", mDisplayCutout=" + mDisplayCutout 999 + ", mRoundedCorners=" + mRoundedCorners 1000 + " mRoundedCornerFrame=" + mRoundedCornerFrame 1001 + ", mPrivacyIndicatorBounds=" + mPrivacyIndicatorBounds 1002 + ", mSources= { " + joiner 1003 + " }"; 1004 } 1005 } 1006 1007