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.View.SYSTEM_UI_FLAG_LAYOUT_STABLE; 20 import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; 21 import static android.view.ViewRootImpl.NEW_INSETS_MODE_IME; 22 import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE; 23 import static android.view.ViewRootImpl.sNewInsetsMode; 24 import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES; 25 import static android.view.WindowInsets.Type.SYSTEM_GESTURES; 26 import static android.view.WindowInsets.Type.displayCutout; 27 import static android.view.WindowInsets.Type.ime; 28 import static android.view.WindowInsets.Type.indexOf; 29 import static android.view.WindowInsets.Type.isVisibleInsetsType; 30 import static android.view.WindowInsets.Type.statusBars; 31 import static android.view.WindowInsets.Type.systemBars; 32 import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; 33 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; 34 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; 35 36 import android.annotation.IntDef; 37 import android.annotation.Nullable; 38 import android.graphics.Insets; 39 import android.graphics.Rect; 40 import android.os.Parcel; 41 import android.os.Parcelable; 42 import android.util.ArraySet; 43 import android.util.SparseIntArray; 44 import android.view.WindowInsets.Type; 45 import android.view.WindowInsets.Type.InsetsType; 46 import android.view.WindowManager.LayoutParams.SoftInputModeFlags; 47 48 import com.android.internal.annotations.VisibleForTesting; 49 50 import java.io.PrintWriter; 51 import java.lang.annotation.Retention; 52 import java.lang.annotation.RetentionPolicy; 53 import java.util.Arrays; 54 import java.util.Objects; 55 import java.util.StringJoiner; 56 57 /** 58 * Holder for state of system windows that cause window insets for all other windows in the system. 59 * @hide 60 */ 61 public class InsetsState implements Parcelable { 62 63 public static final InsetsState EMPTY = new InsetsState(); 64 65 /** 66 * Internal representation of inset source types. This is different from the public API in 67 * {@link WindowInsets.Type} as one type from the public API might indicate multiple windows 68 * at the same time. 69 */ 70 @Retention(RetentionPolicy.SOURCE) 71 @IntDef(prefix = "ITYPE", value = { 72 ITYPE_STATUS_BAR, 73 ITYPE_NAVIGATION_BAR, 74 ITYPE_CAPTION_BAR, 75 ITYPE_TOP_GESTURES, 76 ITYPE_BOTTOM_GESTURES, 77 ITYPE_LEFT_GESTURES, 78 ITYPE_RIGHT_GESTURES, 79 ITYPE_TOP_MANDATORY_GESTURES, 80 ITYPE_BOTTOM_MANDATORY_GESTURES, 81 ITYPE_LEFT_MANDATORY_GESTURES, 82 ITYPE_RIGHT_MANDATORY_GESTURES, 83 ITYPE_TOP_TAPPABLE_ELEMENT, 84 ITYPE_BOTTOM_TAPPABLE_ELEMENT, 85 ITYPE_LEFT_DISPLAY_CUTOUT, 86 ITYPE_TOP_DISPLAY_CUTOUT, 87 ITYPE_RIGHT_DISPLAY_CUTOUT, 88 ITYPE_BOTTOM_DISPLAY_CUTOUT, 89 ITYPE_IME, 90 ITYPE_CLIMATE_BAR, 91 ITYPE_EXTRA_NAVIGATION_BAR 92 }) 93 public @interface InternalInsetsType {} 94 95 /** 96 * Special value to be used to by methods returning an {@link InternalInsetsType} to indicate 97 * that the objects/parameters aren't associated with an {@link InternalInsetsType} 98 */ 99 public static final int ITYPE_INVALID = -1; 100 101 static final int FIRST_TYPE = 0; 102 103 public static final int ITYPE_STATUS_BAR = FIRST_TYPE; 104 public static final int ITYPE_NAVIGATION_BAR = 1; 105 public static final int ITYPE_CAPTION_BAR = 2; 106 107 public static final int ITYPE_TOP_GESTURES = 3; 108 public static final int ITYPE_BOTTOM_GESTURES = 4; 109 public static final int ITYPE_LEFT_GESTURES = 5; 110 public static final int ITYPE_RIGHT_GESTURES = 6; 111 112 /** Additional gesture inset types that map into {@link Type.MANDATORY_SYSTEM_GESTURES}. */ 113 public static final int ITYPE_TOP_MANDATORY_GESTURES = 7; 114 public static final int ITYPE_BOTTOM_MANDATORY_GESTURES = 8; 115 public static final int ITYPE_LEFT_MANDATORY_GESTURES = 9; 116 public static final int ITYPE_RIGHT_MANDATORY_GESTURES = 10; 117 118 public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 11; 119 public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = 12; 120 121 public static final int ITYPE_LEFT_DISPLAY_CUTOUT = 13; 122 public static final int ITYPE_TOP_DISPLAY_CUTOUT = 14; 123 public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 15; 124 public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 16; 125 126 /** Input method window. */ 127 public static final int ITYPE_IME = 17; 128 129 /** Additional system decorations inset type. */ 130 public static final int ITYPE_CLIMATE_BAR = 18; 131 public static final int ITYPE_EXTRA_NAVIGATION_BAR = 19; 132 133 static final int LAST_TYPE = ITYPE_EXTRA_NAVIGATION_BAR; 134 public static final int SIZE = LAST_TYPE + 1; 135 136 // Derived types 137 138 /** A shelf is the same as the navigation bar. */ 139 public static final int ITYPE_SHELF = ITYPE_NAVIGATION_BAR; 140 141 @Retention(RetentionPolicy.SOURCE) 142 @IntDef(prefix = "IINSETS_SIDE", value = { 143 ISIDE_LEFT, 144 ISIDE_TOP, 145 ISIDE_RIGHT, 146 ISIDE_BOTTOM, 147 ISIDE_FLOATING, 148 ISIDE_UNKNOWN 149 }) 150 public @interface InternalInsetsSide {} 151 static final int ISIDE_LEFT = 0; 152 static final int ISIDE_TOP = 1; 153 static final int ISIDE_RIGHT = 2; 154 static final int ISIDE_BOTTOM = 3; 155 static final int ISIDE_FLOATING = 4; 156 static final int ISIDE_UNKNOWN = 5; 157 158 private InsetsSource[] mSources = new InsetsSource[SIZE]; 159 160 /** 161 * The frame of the display these sources are relative to. 162 */ 163 private final Rect mDisplayFrame = new Rect(); 164 InsetsState()165 public InsetsState() { 166 } 167 InsetsState(InsetsState copy)168 public InsetsState(InsetsState copy) { 169 set(copy); 170 } 171 InsetsState(InsetsState copy, boolean copySources)172 public InsetsState(InsetsState copy, boolean copySources) { 173 set(copy, copySources); 174 } 175 176 /** 177 * Calculates {@link WindowInsets} based on the current source configuration. 178 * 179 * @param frame The frame to calculate the insets relative to. 180 * @param ignoringVisibilityState {@link InsetsState} used to calculate 181 * {@link WindowInsets#getInsetsIgnoringVisibility(int)} information, or pass 182 * {@code null} to use this state to calculate that information. 183 * @return The calculated insets. 184 */ calculateInsets(Rect frame, @Nullable InsetsState ignoringVisibilityState, boolean isScreenRound, boolean alwaysConsumeSystemBars, DisplayCutout cutout, int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags, @Nullable @InternalInsetsSide SparseIntArray typeSideMap)185 public WindowInsets calculateInsets(Rect frame, @Nullable InsetsState ignoringVisibilityState, 186 boolean isScreenRound, boolean alwaysConsumeSystemBars, DisplayCutout cutout, 187 int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags, 188 @Nullable @InternalInsetsSide SparseIntArray typeSideMap) { 189 Insets[] typeInsetsMap = new Insets[Type.SIZE]; 190 Insets[] typeMaxInsetsMap = new Insets[Type.SIZE]; 191 boolean[] typeVisibilityMap = new boolean[SIZE]; 192 final Rect relativeFrame = new Rect(frame); 193 final Rect relativeFrameMax = new Rect(frame); 194 for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) { 195 InsetsSource source = mSources[type]; 196 if (source == null) { 197 int index = indexOf(toPublicType(type)); 198 if (typeInsetsMap[index] == null) { 199 typeInsetsMap[index] = Insets.NONE; 200 } 201 continue; 202 } 203 204 boolean skipNonImeInImeMode = ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_IME 205 && source.getType() != ITYPE_IME; 206 boolean skipSystemBars = ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_FULL 207 && (type == ITYPE_STATUS_BAR || type == ITYPE_NAVIGATION_BAR); 208 boolean skipLegacyTypes = ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE 209 && (type == ITYPE_STATUS_BAR || type == ITYPE_NAVIGATION_BAR 210 || type == ITYPE_IME); 211 if (skipSystemBars || skipLegacyTypes || skipNonImeInImeMode) { 212 typeVisibilityMap[indexOf(toPublicType(type))] = source.isVisible(); 213 continue; 214 } 215 216 processSource(source, relativeFrame, false /* ignoreVisibility */, typeInsetsMap, 217 typeSideMap, typeVisibilityMap); 218 219 // IME won't be reported in max insets as the size depends on the EditorInfo of the IME 220 // target. 221 if (source.getType() != ITYPE_IME) { 222 InsetsSource ignoringVisibilitySource = ignoringVisibilityState != null 223 ? ignoringVisibilityState.getSource(type) 224 : source; 225 if (ignoringVisibilitySource == null) { 226 continue; 227 } 228 processSource(ignoringVisibilitySource, relativeFrameMax, 229 true /* ignoreVisibility */, typeMaxInsetsMap, null /* typeSideMap */, 230 null /* typeVisibilityMap */); 231 } 232 } 233 final int softInputAdjustMode = legacySoftInputMode & SOFT_INPUT_MASK_ADJUST; 234 235 @InsetsType int compatInsetsTypes = systemBars() | displayCutout(); 236 if (softInputAdjustMode == SOFT_INPUT_ADJUST_RESIZE) { 237 compatInsetsTypes |= ime(); 238 } 239 if ((legacyWindowFlags & FLAG_FULLSCREEN) != 0) { 240 compatInsetsTypes &= ~statusBars(); 241 } 242 243 return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound, 244 alwaysConsumeSystemBars, cutout, compatInsetsTypes, 245 sNewInsetsMode == NEW_INSETS_MODE_FULL 246 && (legacySystemUiFlags & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0); 247 } 248 calculateVisibleInsets(Rect frame, @SoftInputModeFlags int softInputMode)249 public Rect calculateVisibleInsets(Rect frame, @SoftInputModeFlags int softInputMode) { 250 Insets insets = Insets.NONE; 251 for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) { 252 InsetsSource source = mSources[type]; 253 if (source == null) { 254 continue; 255 } 256 if (sNewInsetsMode != NEW_INSETS_MODE_FULL && type != ITYPE_IME) { 257 continue; 258 } 259 260 // Ignore everything that's not a system bar or IME. 261 int publicType = InsetsState.toPublicType(type); 262 if (!isVisibleInsetsType(publicType, softInputMode)) { 263 continue; 264 } 265 insets = Insets.max(source.calculateVisibleInsets(frame), insets); 266 } 267 return insets.toRect(); 268 } 269 270 /** 271 * Calculate which insets *cannot* be controlled, because the frame does not cover the 272 * respective side of the inset. 273 * 274 * If the frame of our window doesn't cover the entire inset, the control API makes very 275 * little sense, as we don't deal with negative insets. 276 */ 277 @InsetsType calculateUncontrollableInsetsFromFrame(Rect frame)278 public int calculateUncontrollableInsetsFromFrame(Rect frame) { 279 int blocked = 0; 280 for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) { 281 InsetsSource source = mSources[type]; 282 if (source == null) { 283 continue; 284 } 285 if (!canControlSide(frame, getInsetSide( 286 source.calculateInsets(frame, true /* ignoreVisibility */)))) { 287 blocked |= toPublicType(type); 288 } 289 } 290 return blocked; 291 } 292 canControlSide(Rect frame, int side)293 private boolean canControlSide(Rect frame, int side) { 294 switch (side) { 295 case ISIDE_LEFT: 296 case ISIDE_RIGHT: 297 return frame.left == mDisplayFrame.left && frame.right == mDisplayFrame.right; 298 case ISIDE_TOP: 299 case ISIDE_BOTTOM: 300 return frame.top == mDisplayFrame.top && frame.bottom == mDisplayFrame.bottom; 301 case ISIDE_FLOATING: 302 return true; 303 default: 304 return false; 305 } 306 } 307 processSource(InsetsSource source, Rect relativeFrame, boolean ignoreVisibility, Insets[] typeInsetsMap, @Nullable @InternalInsetsSide SparseIntArray typeSideMap, @Nullable boolean[] typeVisibilityMap)308 private void processSource(InsetsSource source, Rect relativeFrame, boolean ignoreVisibility, 309 Insets[] typeInsetsMap, @Nullable @InternalInsetsSide SparseIntArray typeSideMap, 310 @Nullable boolean[] typeVisibilityMap) { 311 Insets insets = source.calculateInsets(relativeFrame, ignoreVisibility); 312 313 int type = toPublicType(source.getType()); 314 processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap, 315 insets, type); 316 317 if (type == MANDATORY_SYSTEM_GESTURES) { 318 // Mandatory system gestures are also system gestures. 319 // TODO: find a way to express this more generally. One option would be to define 320 // Type.systemGestureInsets() as NORMAL | MANDATORY, but then we lose the 321 // ability to set systemGestureInsets() independently from 322 // mandatorySystemGestureInsets() in the Builder. 323 processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap, 324 insets, SYSTEM_GESTURES); 325 } 326 } 327 processSourceAsPublicType(InsetsSource source, Insets[] typeInsetsMap, @InternalInsetsSide @Nullable SparseIntArray typeSideMap, @Nullable boolean[] typeVisibilityMap, Insets insets, int type)328 private void processSourceAsPublicType(InsetsSource source, Insets[] typeInsetsMap, 329 @InternalInsetsSide @Nullable SparseIntArray typeSideMap, 330 @Nullable boolean[] typeVisibilityMap, Insets insets, int type) { 331 int index = indexOf(type); 332 Insets existing = typeInsetsMap[index]; 333 if (existing == null) { 334 typeInsetsMap[index] = insets; 335 } else { 336 typeInsetsMap[index] = Insets.max(existing, insets); 337 } 338 339 if (typeVisibilityMap != null) { 340 typeVisibilityMap[index] = source.isVisible(); 341 } 342 343 if (typeSideMap != null) { 344 @InternalInsetsSide int insetSide = getInsetSide(insets); 345 if (insetSide != ISIDE_UNKNOWN) { 346 typeSideMap.put(source.getType(), insetSide); 347 } 348 } 349 } 350 351 /** 352 * Retrieves the side for a certain {@code insets}. It is required that only one field l/t/r/b 353 * is set in order that this method returns a meaningful result. 354 */ getInsetSide(Insets insets)355 private @InternalInsetsSide int getInsetSide(Insets insets) { 356 if (Insets.NONE.equals(insets)) { 357 return ISIDE_FLOATING; 358 } 359 if (insets.left != 0) { 360 return ISIDE_LEFT; 361 } 362 if (insets.top != 0) { 363 return ISIDE_TOP; 364 } 365 if (insets.right != 0) { 366 return ISIDE_RIGHT; 367 } 368 if (insets.bottom != 0) { 369 return ISIDE_BOTTOM; 370 } 371 return ISIDE_UNKNOWN; 372 } 373 getSource(@nternalInsetsType int type)374 public InsetsSource getSource(@InternalInsetsType int type) { 375 InsetsSource source = mSources[type]; 376 if (source != null) { 377 return source; 378 } 379 source = new InsetsSource(type); 380 mSources[type] = source; 381 return source; 382 } 383 peekSource(@nternalInsetsType int type)384 public @Nullable InsetsSource peekSource(@InternalInsetsType int type) { 385 return mSources[type]; 386 } 387 hasSources()388 public boolean hasSources() { 389 for (int i = 0; i < SIZE; i++) { 390 if (mSources[i] != null) { 391 return true; 392 } 393 } 394 return false; 395 } 396 397 /** 398 * Returns the source visibility or the default visibility if the source doesn't exist. This is 399 * useful if when treating this object as a request. 400 * 401 * @param type The {@link InternalInsetsType} to query. 402 * @return {@code true} if the source is visible or the type is default visible and the source 403 * doesn't exist. 404 */ getSourceOrDefaultVisibility(@nternalInsetsType int type)405 public boolean getSourceOrDefaultVisibility(@InternalInsetsType int type) { 406 final InsetsSource source = mSources[type]; 407 return source != null ? source.isVisible() : getDefaultVisibility(type); 408 } 409 setDisplayFrame(Rect frame)410 public void setDisplayFrame(Rect frame) { 411 mDisplayFrame.set(frame); 412 } 413 getDisplayFrame()414 public Rect getDisplayFrame() { 415 return mDisplayFrame; 416 } 417 418 /** 419 * Modifies the state of this class to exclude a certain type to make it ready for dispatching 420 * to the client. 421 * 422 * @param type The {@link InternalInsetsType} of the source to remove 423 */ removeSource(@nternalInsetsType int type)424 public void removeSource(@InternalInsetsType int type) { 425 mSources[type] = null; 426 } 427 428 /** 429 * A shortcut for setting the visibility of the source. 430 * 431 * @param type The {@link InternalInsetsType} of the source to set the visibility 432 * @param visible {@code true} for visible 433 */ setSourceVisible(@nternalInsetsType int type, boolean visible)434 public void setSourceVisible(@InternalInsetsType int type, boolean visible) { 435 InsetsSource source = mSources[type]; 436 if (source != null) { 437 source.setVisible(visible); 438 } 439 } 440 set(InsetsState other)441 public void set(InsetsState other) { 442 set(other, false /* copySources */); 443 } 444 set(InsetsState other, boolean copySources)445 public void set(InsetsState other, boolean copySources) { 446 mDisplayFrame.set(other.mDisplayFrame); 447 if (copySources) { 448 for (int i = 0; i < SIZE; i++) { 449 InsetsSource source = other.mSources[i]; 450 mSources[i] = source != null ? new InsetsSource(source) : null; 451 } 452 } else { 453 for (int i = 0; i < SIZE; i++) { 454 mSources[i] = other.mSources[i]; 455 } 456 } 457 } 458 addSource(InsetsSource source)459 public void addSource(InsetsSource source) { 460 mSources[source.getType()] = source; 461 } 462 toInternalType(@nsetsType int types)463 public static @InternalInsetsType ArraySet<Integer> toInternalType(@InsetsType int types) { 464 final ArraySet<Integer> result = new ArraySet<>(); 465 if ((types & Type.STATUS_BARS) != 0) { 466 result.add(ITYPE_STATUS_BAR); 467 result.add(ITYPE_CLIMATE_BAR); 468 } 469 if ((types & Type.NAVIGATION_BARS) != 0) { 470 result.add(ITYPE_NAVIGATION_BAR); 471 result.add(ITYPE_EXTRA_NAVIGATION_BAR); 472 } 473 if ((types & Type.CAPTION_BAR) != 0) { 474 result.add(ITYPE_CAPTION_BAR); 475 } 476 if ((types & Type.DISPLAY_CUTOUT) != 0) { 477 result.add(ITYPE_LEFT_DISPLAY_CUTOUT); 478 result.add(ITYPE_TOP_DISPLAY_CUTOUT); 479 result.add(ITYPE_RIGHT_DISPLAY_CUTOUT); 480 result.add(ITYPE_BOTTOM_DISPLAY_CUTOUT); 481 } 482 if ((types & Type.IME) != 0) { 483 result.add(ITYPE_IME); 484 } 485 return result; 486 } 487 488 /** 489 * Converting a internal type to the public type. 490 * @param type internal insets type, {@code InternalInsetsType}. 491 * @return public insets type, {@code Type.InsetsType}. 492 */ toPublicType(@nternalInsetsType int type)493 public static @Type.InsetsType int toPublicType(@InternalInsetsType int type) { 494 switch (type) { 495 case ITYPE_STATUS_BAR: 496 case ITYPE_CLIMATE_BAR: 497 return Type.STATUS_BARS; 498 case ITYPE_NAVIGATION_BAR: 499 case ITYPE_EXTRA_NAVIGATION_BAR: 500 return Type.NAVIGATION_BARS; 501 case ITYPE_CAPTION_BAR: 502 return Type.CAPTION_BAR; 503 case ITYPE_IME: 504 return Type.IME; 505 case ITYPE_TOP_GESTURES: 506 case ITYPE_BOTTOM_GESTURES: 507 case ITYPE_TOP_MANDATORY_GESTURES: 508 case ITYPE_BOTTOM_MANDATORY_GESTURES: 509 case ITYPE_LEFT_MANDATORY_GESTURES: 510 case ITYPE_RIGHT_MANDATORY_GESTURES: 511 return Type.MANDATORY_SYSTEM_GESTURES; 512 case ITYPE_LEFT_GESTURES: 513 case ITYPE_RIGHT_GESTURES: 514 return Type.SYSTEM_GESTURES; 515 case ITYPE_TOP_TAPPABLE_ELEMENT: 516 case ITYPE_BOTTOM_TAPPABLE_ELEMENT: 517 return Type.TAPPABLE_ELEMENT; 518 case ITYPE_LEFT_DISPLAY_CUTOUT: 519 case ITYPE_TOP_DISPLAY_CUTOUT: 520 case ITYPE_RIGHT_DISPLAY_CUTOUT: 521 case ITYPE_BOTTOM_DISPLAY_CUTOUT: 522 return Type.DISPLAY_CUTOUT; 523 default: 524 throw new IllegalArgumentException("Unknown type: " + type); 525 } 526 } 527 getDefaultVisibility(@nternalInsetsType int type)528 public static boolean getDefaultVisibility(@InternalInsetsType int type) { 529 return type != ITYPE_IME; 530 } 531 containsType(@nternalInsetsType int[] types, @InternalInsetsType int type)532 public static boolean containsType(@InternalInsetsType int[] types, 533 @InternalInsetsType int type) { 534 if (types == null) { 535 return false; 536 } 537 for (int t : types) { 538 if (t == type) { 539 return true; 540 } 541 } 542 return false; 543 } 544 dump(String prefix, PrintWriter pw)545 public void dump(String prefix, PrintWriter pw) { 546 pw.println(prefix + "InsetsState"); 547 for (int i = 0; i < SIZE; i++) { 548 InsetsSource source = mSources[i]; 549 if (source == null) continue; 550 source.dump(prefix + " ", pw); 551 } 552 } 553 typeToString(@nternalInsetsType int type)554 public static String typeToString(@InternalInsetsType int type) { 555 switch (type) { 556 case ITYPE_STATUS_BAR: 557 return "ITYPE_STATUS_BAR"; 558 case ITYPE_NAVIGATION_BAR: 559 return "ITYPE_NAVIGATION_BAR"; 560 case ITYPE_CAPTION_BAR: 561 return "ITYPE_CAPTION_BAR"; 562 case ITYPE_TOP_GESTURES: 563 return "ITYPE_TOP_GESTURES"; 564 case ITYPE_BOTTOM_GESTURES: 565 return "ITYPE_BOTTOM_GESTURES"; 566 case ITYPE_LEFT_GESTURES: 567 return "ITYPE_LEFT_GESTURES"; 568 case ITYPE_RIGHT_GESTURES: 569 return "ITYPE_RIGHT_GESTURES"; 570 case ITYPE_TOP_MANDATORY_GESTURES: 571 return "ITYPE_TOP_MANDATORY_GESTURES"; 572 case ITYPE_BOTTOM_MANDATORY_GESTURES: 573 return "ITYPE_BOTTOM_MANDATORY_GESTURES"; 574 case ITYPE_LEFT_MANDATORY_GESTURES: 575 return "ITYPE_LEFT_MANDATORY_GESTURES"; 576 case ITYPE_RIGHT_MANDATORY_GESTURES: 577 return "ITYPE_RIGHT_MANDATORY_GESTURES"; 578 case ITYPE_TOP_TAPPABLE_ELEMENT: 579 return "ITYPE_TOP_TAPPABLE_ELEMENT"; 580 case ITYPE_BOTTOM_TAPPABLE_ELEMENT: 581 return "ITYPE_BOTTOM_TAPPABLE_ELEMENT"; 582 case ITYPE_LEFT_DISPLAY_CUTOUT: 583 return "ITYPE_LEFT_DISPLAY_CUTOUT"; 584 case ITYPE_TOP_DISPLAY_CUTOUT: 585 return "ITYPE_TOP_DISPLAY_CUTOUT"; 586 case ITYPE_RIGHT_DISPLAY_CUTOUT: 587 return "ITYPE_RIGHT_DISPLAY_CUTOUT"; 588 case ITYPE_BOTTOM_DISPLAY_CUTOUT: 589 return "ITYPE_BOTTOM_DISPLAY_CUTOUT"; 590 case ITYPE_IME: 591 return "ITYPE_IME"; 592 case ITYPE_CLIMATE_BAR: 593 return "ITYPE_CLIMATE_BAR"; 594 case ITYPE_EXTRA_NAVIGATION_BAR: 595 return "ITYPE_EXTRA_NAVIGATION_BAR"; 596 default: 597 return "ITYPE_UNKNOWN_" + type; 598 } 599 } 600 601 @Override equals(Object o)602 public boolean equals(Object o) { 603 return equals(o, false, false); 604 } 605 606 /** 607 * An equals method can exclude the caption insets. This is useful because we assemble the 608 * caption insets information on the client side, and when we communicate with server, it's 609 * excluded. 610 * @param excludingCaptionInsets {@code true} if we want to compare two InsetsState objects but 611 * ignore the caption insets source value. 612 * @param excludeInvisibleImeFrames If {@link #ITYPE_IME} frames should be ignored when IME is 613 * not visible. 614 * @return {@code true} if the two InsetsState objects are equal, {@code false} otherwise. 615 */ 616 @VisibleForTesting equals(Object o, boolean excludingCaptionInsets, boolean excludeInvisibleImeFrames)617 public boolean equals(Object o, boolean excludingCaptionInsets, 618 boolean excludeInvisibleImeFrames) { 619 if (this == o) { return true; } 620 if (o == null || getClass() != o.getClass()) { return false; } 621 622 InsetsState state = (InsetsState) o; 623 624 if (!mDisplayFrame.equals(state.mDisplayFrame)) { 625 return false; 626 } 627 for (int i = 0; i < SIZE; i++) { 628 if (excludingCaptionInsets) { 629 if (i == ITYPE_CAPTION_BAR) continue; 630 } 631 InsetsSource source = mSources[i]; 632 InsetsSource otherSource = state.mSources[i]; 633 if (source == null && otherSource == null) { 634 continue; 635 } 636 if (source != null && otherSource == null || source == null && otherSource != null) { 637 return false; 638 } 639 if (!otherSource.equals(source, excludeInvisibleImeFrames)) { 640 return false; 641 } 642 } 643 return true; 644 } 645 646 @Override hashCode()647 public int hashCode() { 648 return Objects.hash(mDisplayFrame, Arrays.hashCode(mSources)); 649 } 650 InsetsState(Parcel in)651 public InsetsState(Parcel in) { 652 readFromParcel(in); 653 } 654 655 @Override describeContents()656 public int describeContents() { 657 return 0; 658 } 659 660 @Override writeToParcel(Parcel dest, int flags)661 public void writeToParcel(Parcel dest, int flags) { 662 dest.writeParcelable(mDisplayFrame, flags); 663 dest.writeParcelableArray(mSources, 0); 664 } 665 666 public static final @android.annotation.NonNull Creator<InsetsState> CREATOR = new Creator<InsetsState>() { 667 668 public InsetsState createFromParcel(Parcel in) { 669 return new InsetsState(in); 670 } 671 672 public InsetsState[] newArray(int size) { 673 return new InsetsState[size]; 674 } 675 }; 676 readFromParcel(Parcel in)677 public void readFromParcel(Parcel in) { 678 mDisplayFrame.set(in.readParcelable(null /* loader */)); 679 mSources = in.readParcelableArray(null, InsetsSource.class); 680 } 681 682 @Override toString()683 public String toString() { 684 StringJoiner joiner = new StringJoiner(", "); 685 for (int i = 0; i < SIZE; i++) { 686 InsetsSource source = mSources[i]; 687 if (source != null) { 688 joiner.add(source.toString()); 689 } 690 } 691 return "InsetsState: {" 692 + "mDisplayFrame=" + mDisplayFrame 693 + ", mSources= { " + joiner 694 + " }"; 695 } 696 } 697 698