1 /* 2 * Copyright 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 androidx.core.view; 18 19 import static android.os.Build.VERSION.SDK_INT; 20 21 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP; 22 import static androidx.core.graphics.Insets.toCompatInsets; 23 24 import android.annotation.SuppressLint; 25 import android.graphics.Rect; 26 import android.util.Log; 27 import android.view.View; 28 import android.view.WindowInsets; 29 import android.view.inputmethod.EditorInfo; 30 import android.view.inputmethod.InputMethod; 31 32 import androidx.annotation.IntDef; 33 import androidx.annotation.IntRange; 34 import androidx.annotation.RequiresApi; 35 import androidx.annotation.RestrictTo; 36 import androidx.core.graphics.Insets; 37 import androidx.core.util.ObjectsCompat; 38 import androidx.core.util.Preconditions; 39 import androidx.core.view.WindowInsetsCompat.Type.InsetsType; 40 41 import org.jspecify.annotations.NonNull; 42 import org.jspecify.annotations.Nullable; 43 44 import java.lang.annotation.Retention; 45 import java.lang.annotation.RetentionPolicy; 46 import java.lang.reflect.Constructor; 47 import java.lang.reflect.Field; 48 import java.lang.reflect.Method; 49 import java.util.Objects; 50 51 /** 52 * Describes a set of insets for window content. 53 * 54 * <p>WindowInsetsCompats are immutable and may be expanded to include more inset types in the 55 * future. To adjust insets, use one of the supplied clone methods to obtain a new 56 * WindowInsetsCompat instance with the adjusted properties.</p> 57 */ 58 public class WindowInsetsCompat { 59 private static final String TAG = "WindowInsetsCompat"; 60 61 /** 62 * A {@link WindowInsetsCompat} instance for which {@link #isConsumed()} returns {@code true}. 63 * <p> 64 * This can be used during insets dispatch in the view hierarchy by returning this value from 65 * {@code View.onApplyWindowInsets(WindowInsets)} or 66 * {@link OnApplyWindowInsetsListener#onApplyWindowInsets(View, WindowInsetsCompat)} to stop 67 * dispatch the insets to its children to avoid traversing the entire view hierarchy. 68 * <p> 69 * The application should return this instance once it has taken care of all insets on a certain 70 * level in the view hierarchy, and doesn't need to dispatch to its children anymore for better 71 * performance. 72 * 73 * @see #isConsumed() 74 */ 75 public static final @NonNull WindowInsetsCompat CONSUMED; 76 77 static { 78 if (SDK_INT >= 34) { 79 CONSUMED = Impl34.CONSUMED; 80 } else if (SDK_INT >= 30) { 81 CONSUMED = Impl30.CONSUMED; 82 } else { 83 CONSUMED = Impl.CONSUMED; 84 } 85 } 86 87 private final Impl mImpl; 88 89 @RequiresApi(20) WindowInsetsCompat(@onNull WindowInsets insets)90 private WindowInsetsCompat(@NonNull WindowInsets insets) { 91 if (SDK_INT >= 36) { 92 mImpl = new Impl36(this, insets); 93 } else if (SDK_INT >= 34) { 94 mImpl = new Impl34(this, insets); 95 } else if (SDK_INT >= 30) { 96 mImpl = new Impl30(this, insets); 97 } else if (SDK_INT >= 29) { 98 mImpl = new Impl29(this, insets); 99 } else if (SDK_INT >= 28) { 100 mImpl = new Impl28(this, insets); 101 } else if (SDK_INT >= 21) { 102 mImpl = new Impl21(this, insets); 103 } else if (SDK_INT >= 20) { 104 mImpl = new Impl20(this, insets); 105 } else { 106 mImpl = new Impl(this); 107 } 108 } 109 110 /** 111 * Constructs a new WindowInsetsCompat, copying all values from a source WindowInsetsCompat. 112 * 113 * @param src source from which values are copied 114 */ WindowInsetsCompat(final @Nullable WindowInsetsCompat src)115 public WindowInsetsCompat(final @Nullable WindowInsetsCompat src) { 116 if (src != null) { 117 // We'll copy over from the 'src' instance's impl 118 final Impl srcImpl = src.mImpl; 119 if (SDK_INT >= 34 && srcImpl instanceof Impl34) { 120 mImpl = new Impl34(this, (Impl34) srcImpl); 121 } else if (SDK_INT >= 30 && srcImpl instanceof Impl30) { 122 mImpl = new Impl30(this, (Impl30) srcImpl); 123 } else if (SDK_INT >= 29 && srcImpl instanceof Impl29) { 124 mImpl = new Impl29(this, (Impl29) srcImpl); 125 } else if (SDK_INT >= 28 && srcImpl instanceof Impl28) { 126 mImpl = new Impl28(this, (Impl28) srcImpl); 127 } else if (SDK_INT >= 21 && srcImpl instanceof Impl21) { 128 mImpl = new Impl21(this, (Impl21) srcImpl); 129 } else if (SDK_INT >= 20 && srcImpl instanceof Impl20) { 130 mImpl = new Impl20(this, (Impl20) srcImpl); 131 } else { 132 mImpl = new Impl(this); 133 } 134 srcImpl.copyWindowDataInto(this); 135 } else { 136 // Ideally src would be @NonNull, oh well. 137 mImpl = new Impl(this); 138 } 139 } 140 141 /** 142 * Wrap an instance of {@link WindowInsets} into a {@link WindowInsetsCompat}. 143 * <p> 144 * This version of the function does not allow the resulting {@link WindowInsetsCompat} to be 145 * aware of the root window insets and root view, meaning that the returned values for many of 146 * the different inset {@link Type}s will be incorrect. 147 * <p> 148 * Prefer using {@link #toWindowInsetsCompat(WindowInsets, View)} instead. 149 * 150 * @param insets source insets to wrap 151 * @return the wrapped instance 152 */ 153 @RequiresApi(20) toWindowInsetsCompat(@onNull WindowInsets insets)154 public static @NonNull WindowInsetsCompat toWindowInsetsCompat(@NonNull WindowInsets insets) { 155 return toWindowInsetsCompat(insets, null); 156 } 157 158 /** 159 * Wrap an instance of {@link WindowInsets} into a {@link WindowInsetsCompat}. 160 * <p> 161 * This version of the function allows the resulting {@link WindowInsetsCompat} to be 162 * aware of the root window insets and root view through the {@code view} parameter. This is 163 * required for many of the different inset {@link Type}s to return correct values when used 164 * on devices running Android 10 and before. 165 * 166 * @param insets source insets to wrap 167 * @param view view to use as an entry point for obtaining root window information. This 168 * view needs be attached to the window, otherwise it will be ignored. 169 * @return the wrapped instance 170 */ 171 @RequiresApi(20) toWindowInsetsCompat(@onNull WindowInsets insets, @Nullable View view)172 public static @NonNull WindowInsetsCompat toWindowInsetsCompat(@NonNull WindowInsets insets, 173 @Nullable View view) { 174 WindowInsetsCompat wic = new WindowInsetsCompat(Preconditions.checkNotNull(insets)); 175 if (view != null && view.isAttachedToWindow()) { 176 // Pass the root window insets, which is useful if the Activity is adjustResize 177 wic.setRootWindowInsets(ViewCompat.getRootWindowInsets(view)); 178 // Pass in the root view which allows the WIC to make of a copy of it's visible bounds 179 wic.copyRootViewBounds(view.getRootView()); 180 // Take System UI visibility into account while computing system bar insets 181 wic.setSystemUiVisibility(view.getWindowSystemUiVisibility()); 182 } 183 return wic; 184 } 185 186 /** 187 * Returns the left system window inset in pixels. 188 * 189 * <p>The system window inset represents the area of a full-screen window that is 190 * partially or fully obscured by the status bar, navigation bar, IME or other system windows. 191 * 192 * <p>When running on platforms with API 19 and below, this method always returns {@code 0}. 193 * 194 * @return The left system window inset 195 * @deprecated Use {@link #getInsets(int)} with {@link Type#systemBars()} instead. 196 */ 197 @Deprecated getSystemWindowInsetLeft()198 public int getSystemWindowInsetLeft() { 199 return mImpl.getSystemWindowInsets().left; 200 } 201 202 /** 203 * Returns the top system window inset in pixels. 204 * 205 * <p>The system window inset represents the area of a full-screen window that is 206 * partially or fully obscured by the status bar, navigation bar, IME or other system windows. 207 * 208 * <p>When running on platforms with API 19 and below, this method always returns {@code 0}. 209 * 210 * @return The top system window inset 211 * @deprecated Use {@link #getInsets(int)} with {@link Type#systemBars()} instead. 212 */ 213 @Deprecated getSystemWindowInsetTop()214 public int getSystemWindowInsetTop() { 215 return mImpl.getSystemWindowInsets().top; 216 } 217 218 /** 219 * Returns the right system window inset in pixels. 220 * 221 * <p>The system window inset represents the area of a full-screen window that is 222 * partially or fully obscured by the status bar, navigation bar, IME or other system windows. 223 * 224 * <p>When running on platforms with API 19 and below, this method always returns {@code 0}. 225 * 226 * @return The right system window inset 227 * @deprecated Use {@link #getInsets(int)} with {@link Type#systemBars()} instead. 228 */ 229 @Deprecated getSystemWindowInsetRight()230 public int getSystemWindowInsetRight() { 231 return mImpl.getSystemWindowInsets().right; 232 } 233 234 /** 235 * Returns the bottom system window inset in pixels. 236 * 237 * <p>The system window inset represents the area of a full-screen window that is 238 * partially or fully obscured by the status bar, navigation bar, IME or other system windows. 239 * 240 * <p>When running on platforms with API 19 and below, this method always returns {@code 0}. 241 * 242 * @return The bottom system window inset 243 * @deprecated Use {@link #getInsets(int)} with {@link Type#systemBars()} instead. 244 */ 245 @Deprecated getSystemWindowInsetBottom()246 public int getSystemWindowInsetBottom() { 247 return mImpl.getSystemWindowInsets().bottom; 248 } 249 250 /** 251 * Returns true if this WindowInsets has nonzero system window insets. 252 * 253 * <p>The system window inset represents the area of a full-screen window that is 254 * partially or fully obscured by the status bar, navigation bar, IME or other system windows. 255 * 256 * <p>When running on platforms with API 19 and below, this method always returns {@code false}. 257 * 258 * @return true if any of the system window inset values are nonzero 259 * @deprecated Use {@link #getInsets(int)} with {@link Type#systemBars()} 260 * instead. 261 */ 262 @Deprecated hasSystemWindowInsets()263 public boolean hasSystemWindowInsets() { 264 return !mImpl.getSystemWindowInsets().equals(Insets.NONE); 265 } 266 267 /** 268 * Returns true if this WindowInsets has any non-zero insets. 269 * 270 * <p>When running on platforms with API 19 and below, this method always returns {@code false}. 271 * 272 * @return true if any inset values are nonzero 273 */ hasInsets()274 public boolean hasInsets() { 275 return !getInsets(Type.all()).equals(Insets.NONE) 276 || !getInsetsIgnoringVisibility(Type.all() ^ Type.ime()).equals(Insets.NONE) 277 || getDisplayCutout() != null; 278 } 279 280 /** 281 * Check if these insets have been fully consumed. 282 * 283 * <p>Insets are considered "consumed" if the applicable <code>consume*</code> methods 284 * have been called such that all insets have been set to zero. This affects propagation of 285 * insets through the view hierarchy; insets that have not been fully consumed will continue 286 * to propagate down to child views.</p> 287 * 288 * <p>The result of this method is equivalent to the return value of 289 * {@link android.view.View#fitSystemWindows(android.graphics.Rect)}.</p> 290 * 291 * @return true if the insets have been fully consumed. 292 */ isConsumed()293 public boolean isConsumed() { 294 return mImpl.isConsumed(); 295 } 296 297 /** 298 * Returns true if the associated window has a round shape. 299 * 300 * <p>A round window's left, top, right and bottom edges reach all the way to the 301 * associated edges of the window but the corners may not be visible. Views responding 302 * to round insets should take care to not lay out critical elements within the corners 303 * where they may not be accessible.</p> 304 * 305 * <p>When running on platforms with API 19 and below, this method always returns {@code false}. 306 * 307 * @return true if the window is round 308 */ isRound()309 public boolean isRound() { 310 return mImpl.isRound(); 311 } 312 313 /** 314 * Returns a copy of this WindowInsets with the system window insets fully consumed. 315 * 316 * <p>When running on platforms with API 19 and below, this method always returns {@code null}. 317 * 318 * @return A modified copy of this WindowInsets 319 * @deprecated Consuming of different parts individually of a {@link WindowInsetsCompat} 320 * instance is deprecated, since {@link WindowInsetsCompat} contains many different insets. Use 321 * {@link #CONSUMED} instead to stop dispatching insets. 322 */ 323 @Deprecated consumeSystemWindowInsets()324 public @NonNull WindowInsetsCompat consumeSystemWindowInsets() { 325 return mImpl.consumeSystemWindowInsets(); 326 } 327 328 /** 329 * Returns a copy of this WindowInsets with selected system window insets replaced 330 * with new values. 331 * 332 * <p>When running on platforms with API 19 and below, this method always returns {@code null}. 333 * 334 * @param left New left inset in pixels 335 * @param top New top inset in pixels 336 * @param right New right inset in pixels 337 * @param bottom New bottom inset in pixels 338 * @return A modified copy of this WindowInsets 339 * @deprecated use {@link WindowInsetsCompat.Builder} with 340 * {@link WindowInsetsCompat.Builder#setSystemWindowInsets(Insets)} instead. 341 */ 342 @SuppressWarnings("deprecation") // Builder.setSystemWindowInsets 343 @Deprecated replaceSystemWindowInsets(int left, int top, int right, int bottom)344 public @NonNull WindowInsetsCompat replaceSystemWindowInsets(int left, int top, int right, 345 int bottom) { 346 return new Builder(this) 347 .setSystemWindowInsets(Insets.of(left, top, right, bottom)) 348 .build(); 349 } 350 351 /** 352 * Returns a copy of this WindowInsets with selected system window insets replaced 353 * with new values. 354 * 355 * <p>When running on platforms with API 19 and below, this method always returns {@code null}. 356 * 357 * @param systemWindowInsets New system window insets. Each field is the inset in pixels 358 * for that edge 359 * @return A modified copy of this WindowInsets 360 * @deprecated use {@link WindowInsetsCompat.Builder} with 361 * {@link WindowInsetsCompat.Builder#setSystemWindowInsets(Insets)} instead. 362 */ 363 @SuppressWarnings("deprecation") 364 @Deprecated replaceSystemWindowInsets(@onNull Rect systemWindowInsets)365 public @NonNull WindowInsetsCompat replaceSystemWindowInsets(@NonNull Rect systemWindowInsets) { 366 return new Builder(this) 367 .setSystemWindowInsets(Insets.of(systemWindowInsets)) 368 .build(); 369 } 370 371 /** 372 * Returns the top stable inset in pixels. 373 * 374 * <p>The stable inset represents the area of a full-screen window that <b>may</b> be 375 * partially or fully obscured by the system UI elements. This value does not change 376 * based on the visibility state of those elements; for example, if the status bar is 377 * normally shown, but temporarily hidden, the stable inset will still provide the inset 378 * associated with the status bar being shown.</p> 379 * 380 * <p>When running on platforms with API 20 and below, this method always returns {@code 0}. 381 * 382 * @deprecated Use {@link #getInsetsIgnoringVisibility(int)} with {@link Type#systemBars()} 383 * instead. 384 */ 385 @Deprecated getStableInsetTop()386 public int getStableInsetTop() { 387 return mImpl.getStableInsets().top; 388 } 389 390 /** 391 * Returns the left stable inset in pixels. 392 * 393 * <p>The stable inset represents the area of a full-screen window that <b>may</b> be 394 * partially or fully obscured by the system UI elements. This value does not change 395 * based on the visibility state of those elements; for example, if the status bar is 396 * normally shown, but temporarily hidden, the stable inset will still provide the inset 397 * associated with the status bar being shown.</p> 398 * 399 * <p>When running on platforms with API 20 and below, this method always returns {@code 0}. 400 * 401 * @return The left stable inset 402 * @deprecated Use {@link #getInsetsIgnoringVisibility(int)} with {@link Type#systemBars()} 403 * instead. 404 */ 405 @Deprecated getStableInsetLeft()406 public int getStableInsetLeft() { 407 return mImpl.getStableInsets().left; 408 } 409 410 /** 411 * Returns the right stable inset in pixels. 412 * 413 * <p>The stable inset represents the area of a full-screen window that <b>may</b> be 414 * partially or fully obscured by the system UI elements. This value does not change 415 * based on the visibility state of those elements; for example, if the status bar is 416 * normally shown, but temporarily hidden, the stable inset will still provide the inset 417 * associated with the status bar being shown.</p> 418 * 419 * <p>When running on platforms with API 20 and below, this method always returns {@code 0}. 420 * 421 * @return The right stable inset 422 * @deprecated Use {@link #getInsetsIgnoringVisibility(int)} with {@link Type#systemBars()} 423 * instead. 424 */ 425 @Deprecated getStableInsetRight()426 public int getStableInsetRight() { 427 return mImpl.getStableInsets().right; 428 } 429 430 /** 431 * Returns the bottom stable inset in pixels. 432 * 433 * <p>The stable inset represents the area of a full-screen window that <b>may</b> be 434 * partially or fully obscured by the system UI elements. This value does not change 435 * based on the visibility state of those elements; for example, if the status bar is 436 * normally shown, but temporarily hidden, the stable inset will still provide the inset 437 * associated with the status bar being shown.</p> 438 * 439 * <p>When running on platforms with API 20 and below, this method always returns {@code 0}. 440 * 441 * @return The bottom stable inset 442 * @deprecated Use {@link #getInsetsIgnoringVisibility(int)} with {@link Type#systemBars()} 443 * instead. 444 */ 445 @Deprecated getStableInsetBottom()446 public int getStableInsetBottom() { 447 return mImpl.getStableInsets().bottom; 448 } 449 450 /** 451 * Returns true if this WindowInsets has nonzero stable insets. 452 * 453 * <p>The stable inset represents the area of a full-screen window that <b>may</b> be 454 * partially or fully obscured by the system UI elements. This value does not change 455 * based on the visibility state of those elements; for example, if the status bar is 456 * normally shown, but temporarily hidden, the stable inset will still provide the inset 457 * associated with the status bar being shown.</p> 458 * 459 * <p>When running on platforms with API 20 and below, this method always returns {@code false}. 460 * 461 * @return true if any of the stable inset values are nonzero 462 * @deprecated Use {@link #getInsetsIgnoringVisibility(int)} with {@link Type#systemBars()} 463 * instead. 464 */ 465 @Deprecated hasStableInsets()466 public boolean hasStableInsets() { 467 return !mImpl.getStableInsets().equals(Insets.NONE); 468 } 469 470 /** 471 * Returns a copy of this WindowInsets with the stable insets fully consumed. 472 * 473 * <p>When running on platforms with API 20 and below, this method always returns {@code null}. 474 * 475 * @return A modified copy of this WindowInsetsCompat 476 * @deprecated Consuming of different parts individually of a {@link WindowInsetsCompat} 477 * instance is deprecated, since {@link WindowInsetsCompat} contains many different insets. Use 478 * {@link #CONSUMED} instead to stop dispatching insets. 479 */ 480 @Deprecated consumeStableInsets()481 public @NonNull WindowInsetsCompat consumeStableInsets() { 482 return mImpl.consumeStableInsets(); 483 } 484 485 /** 486 * Returns the display cutout if there is one. 487 * 488 * <p>When running on platforms with API 27 and below, this method always returns {@code null}. 489 * 490 * @return the display cutout or null if there is none 491 * @see DisplayCutoutCompat 492 */ getDisplayCutout()493 public @Nullable DisplayCutoutCompat getDisplayCutout() { 494 return mImpl.getDisplayCutout(); 495 } 496 497 /** 498 * Returns a copy of this WindowInsets with the cutout fully consumed. 499 * 500 * <p>When running on platforms with API 27 and below, this method is a no-op. 501 * 502 * @return A modified copy of this WindowInsets 503 * @deprecated Consuming of different parts individually of a {@link WindowInsetsCompat} 504 * instance is deprecated, since {@link WindowInsetsCompat} contains many different insets. Use 505 * {@link #CONSUMED} instead to stop dispatching insets. 506 */ 507 @Deprecated consumeDisplayCutout()508 public @NonNull WindowInsetsCompat consumeDisplayCutout() { 509 return mImpl.consumeDisplayCutout(); 510 } 511 512 /** 513 * Returns the system window insets in pixels. 514 * 515 * <p>The system window inset represents the area of a full-screen window that is 516 * partially or fully obscured by the status bar, navigation bar, IME or other system windows. 517 * </p> 518 * 519 * @return The system window insets 520 * @see #getSystemWindowInsetLeft() 521 * @see #getSystemWindowInsetTop() 522 * @see #getSystemWindowInsetRight() 523 * @see #getSystemWindowInsetBottom() 524 * @deprecated Use {@link #getInsets(int)} with {@link Type#systemBars()} instead. 525 */ 526 @Deprecated getSystemWindowInsets()527 public @NonNull Insets getSystemWindowInsets() { 528 return mImpl.getSystemWindowInsets(); 529 } 530 531 /** 532 * Returns the stable insets in pixels. 533 * 534 * <p>The stable inset represents the area of a full-screen window that <b>may</b> be 535 * partially or fully obscured by the system UI elements. This value does not change 536 * based on the visibility state of those elements; for example, if the status bar is 537 * normally shown, but temporarily hidden, the stable inset will still provide the inset 538 * associated with the status bar being shown.</p> 539 * 540 * @return The stable insets 541 * @see #getStableInsetLeft() 542 * @see #getStableInsetTop() 543 * @see #getStableInsetRight() 544 * @see #getStableInsetBottom() 545 * @deprecated Use {@link #getInsetsIgnoringVisibility(int)} with {@link Type#systemBars()} 546 * instead. 547 */ 548 @Deprecated getStableInsets()549 public @NonNull Insets getStableInsets() { 550 return mImpl.getStableInsets(); 551 } 552 553 /** 554 * Returns the mandatory system gesture insets. 555 * 556 * <p>The mandatory system gesture insets represent the area of a window where mandatory system 557 * gestures have priority and may consume some or all touch input, e.g. due to the a system bar 558 * occupying it, or it being reserved for touch-only gestures. 559 * 560 * @see WindowInsets#getMandatorySystemGestureInsets 561 * @deprecated Use {@link #getInsets(int)} with {@link Type#mandatorySystemGestures()} 562 * instead. 563 */ 564 @Deprecated getMandatorySystemGestureInsets()565 public @NonNull Insets getMandatorySystemGestureInsets() { 566 return mImpl.getMandatorySystemGestureInsets(); 567 } 568 569 /** 570 * Returns the tappable element insets. 571 * 572 * <p>The tappable element insets represent how much tappable elements <b>must at least</b> be 573 * inset to remain both tappable and visually unobstructed by persistent system windows. 574 * 575 * <p>This may be smaller than {@link #getSystemWindowInsets()} if the system window is 576 * largely transparent and lets through simple taps (but not necessarily more complex gestures). 577 * 578 * @see WindowInsets#getTappableElementInsets 579 * @deprecated Use {@link #getInsets(int)} with {@link Type#tappableElement()} 580 * instead. 581 */ 582 @Deprecated getTappableElementInsets()583 public @NonNull Insets getTappableElementInsets() { 584 return mImpl.getTappableElementInsets(); 585 } 586 587 /** 588 * Returns the system gesture insets. 589 * 590 * <p>The system gesture insets represent the area of a window where system gestures have 591 * priority and may consume some or all touch input, e.g. due to the a system bar 592 * occupying it, or it being reserved for touch-only gestures. 593 * 594 * <p>An app can declare priority over system gestures with 595 * {@link android.view.View#setSystemGestureExclusionRects} outside of the 596 * {@link #getMandatorySystemGestureInsets() mandatory system gesture insets}. 597 * 598 * @see WindowInsets#getSystemGestureInsets 599 * @deprecated Use {@link #getInsets(int)} with {@link Type#systemGestures()} 600 * instead. 601 */ 602 @Deprecated getSystemGestureInsets()603 public @NonNull Insets getSystemGestureInsets() { 604 return mImpl.getSystemGestureInsets(); 605 } 606 607 /** 608 * Returns a copy of this instance inset in the given directions. 609 * 610 * This is intended for dispatching insets to areas of the window that are smaller than the 611 * current area. 612 * 613 * <p>Example: 614 * <pre> 615 * childView.dispatchApplyWindowInsets(insets.inset(childMargins)); 616 * </pre> 617 * 618 * @param insets the amount of insets to remove from all sides. 619 * 620 * @see #inset(int, int, int, int) 621 */ inset(@onNull Insets insets)622 public @NonNull WindowInsetsCompat inset(@NonNull Insets insets) { 623 return inset(insets.left, insets.top, insets.right, insets.bottom); 624 } 625 626 /** 627 * Returns a copy of this instance inset in the given directions. 628 * 629 * This is intended for dispatching insets to areas of the window that are smaller than the 630 * current area. 631 * 632 * <p>Example: 633 * <pre> 634 * childView.dispatchApplyWindowInsets(insets.inset( 635 * childMarginLeft, childMarginTop, childMarginBottom, childMarginRight)); 636 * </pre> 637 * 638 * @param left the amount of insets to remove from the left. Must be non-negative. 639 * @param top the amount of insets to remove from the top. Must be non-negative. 640 * @param right the amount of insets to remove from the right. Must be non-negative. 641 * @param bottom the amount of insets to remove from the bottom. Must be non-negative. 642 * 643 * @return the inset insets 644 */ inset(@ntRangefrom = 0) int left, @IntRange(from = 0) int top, @IntRange(from = 0) int right, @IntRange(from = 0) int bottom)645 public @NonNull WindowInsetsCompat inset(@IntRange(from = 0) int left, 646 @IntRange(from = 0) int top, @IntRange(from = 0) int right, 647 @IntRange(from = 0) int bottom) { 648 return mImpl.inset(left, top, right, bottom); 649 } 650 651 /** 652 * Returns the insets of a specific set of windows causing insets, denoted by the 653 * {@code typeMask} bit mask of {@link Type}s. 654 * 655 * When running on devices with API Level 29 and before, the returned insets are an 656 * approximation based on the information available. This is especially true for the {@link 657 * Type#ime IME} type, which currently only works when running on devices with SDK level 23 658 * and above. 659 * 660 * @param typeMask Bit mask of {@link Type}s to query the insets for. 661 * @return The insets. 662 */ getInsets(@nsetsType int typeMask)663 public @NonNull Insets getInsets(@InsetsType int typeMask) { 664 return mImpl.getInsets(typeMask); 665 } 666 667 /** 668 * Returns the insets a specific set of windows can cause, denoted by the 669 * {@code typeMask} bit mask of {@link Type}s, regardless of whether that type is 670 * currently visible or not. 671 * 672 * <p>The insets represents the area of a a window that that <b>may</b> be partially 673 * or fully obscured by the system window identified by {@code typeMask}. This value does not 674 * change based on the visibility state of those elements. For example, if the status bar is 675 * normally shown, but temporarily hidden, the inset returned here will still provide the inset 676 * associated with the status bar being shown.</p> 677 * 678 * When running on devices with API Level 29 and before, the returned insets are an 679 * approximation based on the information available. This is especially true for the {@link 680 * Type#ime IME} type, which currently only works when running on devices with SDK level 23 681 * and above. 682 * 683 * @param typeMask Bit mask of {@link Type}s to query the insets for. 684 * @return The insets. 685 * @throws IllegalArgumentException If the caller tries to query {@link Type#ime()}. Insets are 686 * not available if the IME isn't visible as the height of the 687 * IME is dynamic depending on the {@link EditorInfo} of the 688 * currently focused view, as well as the UI state of the IME. 689 */ getInsetsIgnoringVisibility(@nsetsType int typeMask)690 public @NonNull Insets getInsetsIgnoringVisibility(@InsetsType int typeMask) { 691 return mImpl.getInsetsIgnoringVisibility(typeMask); 692 } 693 694 /** 695 * Returns whether a set of windows that may cause insets is currently visible on screen, 696 * regardless of whether it actually overlaps with this window. 697 * 698 * When running on devices with API Level 29 and before, the returned value is an 699 * approximation based on the information available. This is especially true for the {@link 700 * Type#ime IME} type, which currently only works when running on devices with SDK level 23 701 * and above. 702 * 703 * @param typeMask Bit mask of {@link Type}s to query visibility status. 704 * @return {@code true} if and only if all windows included in {@code typeMask} are currently 705 * visible on screen. 706 */ isVisible(@nsetsType int typeMask)707 public boolean isVisible(@InsetsType int typeMask) { 708 return mImpl.isVisible(typeMask); 709 } 710 711 @Override equals(Object o)712 public boolean equals(Object o) { 713 if (this == o) { 714 return true; 715 } 716 if (!(o instanceof WindowInsetsCompat)) { 717 return false; 718 } 719 WindowInsetsCompat other = (WindowInsetsCompat) o; 720 return ObjectsCompat.equals(mImpl, other.mImpl); 721 } 722 723 @Override hashCode()724 public int hashCode() { 725 return mImpl == null ? 0 : mImpl.hashCode(); 726 } 727 728 /** 729 * Return the source {@link WindowInsets} instance used in this {@link WindowInsetsCompat}. 730 * 731 * @return the wrapped WindowInsets instance 732 */ 733 @RequiresApi(20) toWindowInsets()734 public @Nullable WindowInsets toWindowInsets() { 735 return mImpl instanceof Impl20 ? ((Impl20) mImpl).mPlatformInsets : null; 736 } 737 738 private static class Impl { 739 @SuppressWarnings("deprecation") 740 static final @NonNull WindowInsetsCompat CONSUMED = new WindowInsetsCompat.Builder() 741 .build() 742 .consumeDisplayCutout() 743 .consumeStableInsets() 744 .consumeSystemWindowInsets(); 745 746 final WindowInsetsCompat mHost; 747 Impl(@onNull WindowInsetsCompat host)748 Impl(@NonNull WindowInsetsCompat host) { 749 mHost = host; 750 } 751 isRound()752 boolean isRound() { 753 return false; 754 } 755 isConsumed()756 boolean isConsumed() { 757 return false; 758 } 759 consumeSystemWindowInsets()760 @NonNull WindowInsetsCompat consumeSystemWindowInsets() { 761 return mHost; 762 } 763 consumeStableInsets()764 @NonNull WindowInsetsCompat consumeStableInsets() { 765 return mHost; 766 } 767 getDisplayCutout()768 @Nullable DisplayCutoutCompat getDisplayCutout() { 769 return null; 770 } 771 consumeDisplayCutout()772 @NonNull WindowInsetsCompat consumeDisplayCutout() { 773 return mHost; 774 } 775 getSystemWindowInsets()776 @NonNull Insets getSystemWindowInsets() { 777 return Insets.NONE; 778 } 779 getStableInsets()780 @NonNull Insets getStableInsets() { 781 return Insets.NONE; 782 } 783 getSystemGestureInsets()784 @NonNull Insets getSystemGestureInsets() { 785 // Pre-Q return the system window insets 786 return getSystemWindowInsets(); 787 } 788 getMandatorySystemGestureInsets()789 @NonNull Insets getMandatorySystemGestureInsets() { 790 // Pre-Q return the system window insets 791 return getSystemWindowInsets(); 792 } 793 getTappableElementInsets()794 @NonNull Insets getTappableElementInsets() { 795 // Pre-Q return the system window insets 796 return getSystemWindowInsets(); 797 } 798 inset(int left, int top, int right, int bottom)799 @NonNull WindowInsetsCompat inset(int left, int top, int right, int bottom) { 800 return CONSUMED; 801 } 802 getInsets(@nsetsType int typeMask)803 @NonNull Insets getInsets(@InsetsType int typeMask) { 804 return Insets.NONE; 805 } 806 getInsetsIgnoringVisibility(@nsetsType int typeMask)807 @NonNull Insets getInsetsIgnoringVisibility(@InsetsType int typeMask) { 808 if ((typeMask & Type.IME) != 0) { 809 throw new IllegalArgumentException("Unable to query the maximum insets for IME"); 810 } 811 return Insets.NONE; 812 } 813 isVisible(@nsetsType int typeMask)814 boolean isVisible(@InsetsType int typeMask) { 815 return true; 816 } 817 818 @Override equals(Object o)819 public boolean equals(Object o) { 820 // On API < 28 we can not rely on WindowInsets.equals(), so we handle it manually 821 if (this == o) return true; 822 if (!(o instanceof Impl)) return false; 823 final Impl impl = (Impl) o; 824 return isRound() == impl.isRound() 825 && isConsumed() == impl.isConsumed() 826 && ObjectsCompat.equals(getSystemWindowInsets(), impl.getSystemWindowInsets()) 827 && ObjectsCompat.equals(getStableInsets(), impl.getStableInsets()) 828 && ObjectsCompat.equals(getDisplayCutout(), impl.getDisplayCutout()); 829 } 830 831 @Override hashCode()832 public int hashCode() { 833 // On API < 28 we can not rely on WindowInsets.hashCode(), so we handle it manually 834 return ObjectsCompat.hash(isRound(), isConsumed(), getSystemWindowInsets(), 835 getStableInsets(), getDisplayCutout()); 836 } 837 setRootWindowInsets(@ullable WindowInsetsCompat rootWindowInsets)838 void setRootWindowInsets(@Nullable WindowInsetsCompat rootWindowInsets) { 839 } 840 setRootViewData(@onNull Insets visibleInsets)841 void setRootViewData(@NonNull Insets visibleInsets) { 842 } 843 copyRootViewBounds(@onNull View rootView)844 void copyRootViewBounds(@NonNull View rootView) { 845 } 846 setSystemUiVisibility(int systemUiVisibility)847 void setSystemUiVisibility(int systemUiVisibility) { 848 } 849 copyWindowDataInto(@onNull WindowInsetsCompat other)850 void copyWindowDataInto(@NonNull WindowInsetsCompat other) { 851 } 852 setOverriddenInsets(Insets[] insetsTypeMask)853 public void setOverriddenInsets(Insets[] insetsTypeMask) { 854 } 855 setStableInsets(Insets stableInsets)856 public void setStableInsets(Insets stableInsets) { 857 } 858 } 859 860 @RequiresApi(20) 861 private static class Impl20 extends Impl { 862 863 private static final int SYSTEM_BAR_VISIBILITY_MASK = 864 View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; 865 private static boolean sVisibleRectReflectionFetched = false; 866 private static Method sGetViewRootImplMethod; 867 private static Class<?> sAttachInfoClass; 868 private static Field sVisibleInsetsField; 869 private static Field sAttachInfoField; 870 871 final @NonNull WindowInsets mPlatformInsets; 872 873 // TODO(175859616) save all insets in the array 874 private Insets[] mOverriddenInsets; 875 876 // Used to cache the wrapped value 877 private Insets mSystemWindowInsets = null; 878 879 private WindowInsetsCompat mRootWindowInsets; 880 Insets mRootViewVisibleInsets; 881 882 int mSystemUiVisibility; 883 Impl20(@onNull WindowInsetsCompat host, @NonNull WindowInsets insets)884 Impl20(@NonNull WindowInsetsCompat host, @NonNull WindowInsets insets) { 885 super(host); 886 mPlatformInsets = insets; 887 } 888 Impl20(@onNull WindowInsetsCompat host, @NonNull Impl20 other)889 Impl20(@NonNull WindowInsetsCompat host, @NonNull Impl20 other) { 890 this(host, new WindowInsets(other.mPlatformInsets)); 891 } 892 893 @Override isRound()894 boolean isRound() { 895 return mPlatformInsets.isRound(); 896 } 897 898 @Override getInsets(int typeMask)899 public @NonNull Insets getInsets(int typeMask) { 900 return getInsets(typeMask, false); 901 } 902 903 @Override getInsetsIgnoringVisibility(int typeMask)904 public @NonNull Insets getInsetsIgnoringVisibility(int typeMask) { 905 return getInsets(typeMask, true); 906 } 907 908 @Override 909 @SuppressLint("WrongConstant") isVisible(final int typeMask)910 boolean isVisible(final int typeMask) { 911 for (int i = Type.FIRST; i <= Type.LAST; i = i << 1) { 912 if ((typeMask & i) == 0) { 913 continue; 914 } 915 if (!isTypeVisible(i)) { 916 return false; 917 } 918 } 919 return true; 920 } 921 922 @SuppressLint("WrongConstant") getInsets(final int typeMask, final boolean ignoreVisibility)923 private @NonNull Insets getInsets(final int typeMask, final boolean ignoreVisibility) { 924 Insets result = Insets.NONE; 925 for (int i = Type.FIRST; i <= Type.LAST; i = i << 1) { 926 if ((typeMask & i) == 0) { 927 continue; 928 } 929 result = Insets.max(result, getInsetsForType(i, ignoreVisibility)); 930 } 931 return result; 932 } 933 934 @SuppressWarnings("deprecation") getInsetsForType(@nsetsType int type, boolean ignoreVisibility)935 protected @NonNull Insets getInsetsForType(@InsetsType int type, boolean ignoreVisibility) { 936 switch (type) { 937 case Type.STATUS_BARS: { 938 if (ignoreVisibility) { 939 final Insets rootStable = getRootStableInsets(); 940 return Insets.of(0, 941 Math.max(rootStable.top, getSystemWindowInsets().top), 0, 0); 942 } else if ((mSystemUiVisibility & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0) { 943 return Insets.NONE; 944 } else { 945 return Insets.of(0, getSystemWindowInsets().top, 0, 0); 946 } 947 } 948 case Type.NAVIGATION_BARS: { 949 if (ignoreVisibility) { 950 final Insets rootStable = getRootStableInsets(); 951 final Insets stable = getStableInsets(); 952 return Insets.of( 953 Math.max(rootStable.left, stable.left), 954 0, /* zero top inset (== status bar) */ 955 Math.max(rootStable.right, stable.right), 956 Math.max(rootStable.bottom, stable.bottom) 957 ); 958 } else if ((mSystemUiVisibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0) { 959 return Insets.NONE; 960 } else { 961 final Insets systemWindow = getSystemWindowInsets(); 962 final Insets rootStable = mRootWindowInsets != null 963 ? mRootWindowInsets.getStableInsets() 964 : null; 965 966 int bottom = systemWindow.bottom; 967 if (rootStable != null) { 968 // If we have root stable insets, and its bottom is less than the 969 // system window bottom, it's likely that the IME is visible. In this 970 // case, we want to only return the stable bottom. 971 bottom = Math.min(bottom, rootStable.bottom); 972 } 973 return Insets.of( 974 systemWindow.left, 975 0, /* zero top inset (== status bar) */ 976 systemWindow.right, 977 bottom 978 ); 979 } 980 } 981 case Type.IME: { 982 Insets overriddenInsets = mOverriddenInsets != null 983 ? mOverriddenInsets[Type.indexOf(Type.IME)] : null; 984 if (overriddenInsets != null) { 985 return overriddenInsets; 986 } 987 final Insets systemWindow = getSystemWindowInsets(); 988 final Insets rootStable = getRootStableInsets(); 989 990 if (systemWindow.bottom > rootStable.bottom) { 991 // This handles the adjustResize case on < API 30, since 992 // systemWindow.bottom is probably going to be the IME 993 return Insets.of(0, 0, 0, systemWindow.bottom); 994 } else if (mRootViewVisibleInsets != null 995 && !mRootViewVisibleInsets.equals(Insets.NONE)) { 996 // This handles the adjustPan case on < API 30. We look at the root view's 997 // visible rect and check it's bottom against the root stable insets 998 if (mRootViewVisibleInsets.bottom > rootStable.bottom) { 999 return Insets.of(0, 0, 0, mRootViewVisibleInsets.bottom); 1000 } 1001 } 1002 return Insets.NONE; 1003 } 1004 case Type.SYSTEM_GESTURES: { 1005 // Visibility does not affect this type of inset 1006 return getSystemGestureInsets(); 1007 } 1008 case Type.MANDATORY_SYSTEM_GESTURES: { 1009 // Visibility does not affect this type of inset 1010 return getMandatorySystemGestureInsets(); 1011 } 1012 case Type.TAPPABLE_ELEMENT: { 1013 // Visibility does not affect this type of inset 1014 return getTappableElementInsets(); 1015 } 1016 case Type.DISPLAY_CUTOUT: { 1017 // Visibility does not affect this type of inset 1018 final DisplayCutoutCompat cutout = mRootWindowInsets != null 1019 ? mRootWindowInsets.getDisplayCutout() 1020 : getDisplayCutout(); 1021 if (cutout != null) { 1022 return Insets.of(cutout.getSafeInsetLeft(), cutout.getSafeInsetTop(), 1023 cutout.getSafeInsetRight(), cutout.getSafeInsetBottom()); 1024 } else { 1025 return Insets.NONE; 1026 } 1027 } 1028 default: 1029 return Insets.NONE; 1030 } 1031 } 1032 isTypeVisible(@nsetsType final int type)1033 protected boolean isTypeVisible(@InsetsType final int type) { 1034 switch (type) { 1035 case Type.STATUS_BARS: 1036 case Type.NAVIGATION_BARS: 1037 case Type.IME: 1038 case Type.DISPLAY_CUTOUT: 1039 return !getInsetsForType(type, false).equals(Insets.NONE); 1040 case Type.CAPTION_BAR: 1041 // Caption bar is always false on API < 30 1042 return false; 1043 default: 1044 return true; 1045 } 1046 } 1047 1048 @Override getSystemWindowInsets()1049 final @NonNull Insets getSystemWindowInsets() { 1050 if (mSystemWindowInsets == null) { 1051 mSystemWindowInsets = Insets.of( 1052 mPlatformInsets.getSystemWindowInsetLeft(), 1053 mPlatformInsets.getSystemWindowInsetTop(), 1054 mPlatformInsets.getSystemWindowInsetRight(), 1055 mPlatformInsets.getSystemWindowInsetBottom()); 1056 } 1057 return mSystemWindowInsets; 1058 } 1059 1060 @Override 1061 @SuppressWarnings("deprecation") inset(int left, int top, int right, int bottom)1062 @NonNull WindowInsetsCompat inset(int left, int top, int right, int bottom) { 1063 Builder b = new Builder(toWindowInsetsCompat(mPlatformInsets)); 1064 b.setSystemWindowInsets(insetInsets(getSystemWindowInsets(), left, top, right, bottom)); 1065 b.setStableInsets(insetInsets(getStableInsets(), left, top, right, bottom)); 1066 return b.build(); 1067 } 1068 1069 @Override copyWindowDataInto(@onNull WindowInsetsCompat other)1070 void copyWindowDataInto(@NonNull WindowInsetsCompat other) { 1071 other.setRootWindowInsets(mRootWindowInsets); 1072 other.setRootViewData(mRootViewVisibleInsets); 1073 other.setSystemUiVisibility(mSystemUiVisibility); 1074 } 1075 1076 @Override setRootWindowInsets(@ullable WindowInsetsCompat rootWindowInsets)1077 void setRootWindowInsets(@Nullable WindowInsetsCompat rootWindowInsets) { 1078 mRootWindowInsets = rootWindowInsets; 1079 } 1080 1081 @Override setRootViewData(@onNull Insets visibleInsets)1082 void setRootViewData(@NonNull Insets visibleInsets) { 1083 mRootViewVisibleInsets = visibleInsets; 1084 } 1085 1086 @SuppressWarnings("deprecation") getRootStableInsets()1087 private Insets getRootStableInsets() { 1088 if (mRootWindowInsets != null) { 1089 return mRootWindowInsets.getStableInsets(); 1090 } else { 1091 return Insets.NONE; 1092 } 1093 } 1094 1095 @Override copyRootViewBounds(@onNull View rootView)1096 void copyRootViewBounds(@NonNull View rootView) { 1097 Insets visibleInsets = getVisibleInsets(rootView); 1098 if (visibleInsets == null) { 1099 visibleInsets = Insets.NONE; 1100 } 1101 setRootViewData(visibleInsets); 1102 } 1103 1104 @Override setSystemUiVisibility(int systemUiVisibility)1105 void setSystemUiVisibility(int systemUiVisibility) { 1106 mSystemUiVisibility = systemUiVisibility; 1107 } 1108 1109 /** 1110 * Attempt to get a copy of the visible rect from this rootView's AttachInfo. 1111 * 1112 * @return a copy of the provided view's AttachInfo.mVisibleRect or null if anything fails 1113 */ getVisibleInsets(@onNull View rootView)1114 private @Nullable Insets getVisibleInsets(@NonNull View rootView) { 1115 if (SDK_INT >= 30) { 1116 throw new UnsupportedOperationException("getVisibleInsets() should not be called " 1117 + "on API >= 30. Use WindowInsets.isVisible() instead."); 1118 } else { 1119 if (!sVisibleRectReflectionFetched) { 1120 loadReflectionField(); 1121 } 1122 1123 if (sGetViewRootImplMethod == null 1124 || sAttachInfoClass == null 1125 || sVisibleInsetsField == null) { 1126 return null; 1127 } 1128 1129 try { 1130 Object viewRootImpl = sGetViewRootImplMethod.invoke(rootView); 1131 if (viewRootImpl == null) { 1132 Log.w(TAG, "Failed to get visible insets. getViewRootImpl() returned null" 1133 + " from the provided view. This means that the view is " 1134 + "either not attached or the method has been overridden", 1135 new NullPointerException()); 1136 return null; 1137 } else { 1138 Object mAttachInfo = sAttachInfoField.get(viewRootImpl); 1139 Rect visibleRect = (Rect) sVisibleInsetsField.get(mAttachInfo); 1140 return visibleRect != null ? Insets.of(visibleRect) : null; 1141 } 1142 } catch (ReflectiveOperationException e) { 1143 Log.e(TAG, 1144 "Failed to get visible insets. (Reflection error). " + e.getMessage(), 1145 e); 1146 } 1147 } 1148 return null; 1149 } 1150 1151 @Override setOverriddenInsets(Insets[] insetsTypeMask)1152 public void setOverriddenInsets(Insets[] insetsTypeMask) { 1153 mOverriddenInsets = insetsTypeMask; 1154 } 1155 1156 @SuppressWarnings("JavaReflectionMemberAccess") // Reflection on private method 1157 @SuppressLint("PrivateApi") loadReflectionField()1158 private static void loadReflectionField() { 1159 try { 1160 sGetViewRootImplMethod = View.class.getDeclaredMethod("getViewRootImpl"); 1161 sAttachInfoClass = Class.forName("android.view.View$AttachInfo"); 1162 sVisibleInsetsField = sAttachInfoClass.getDeclaredField("mVisibleInsets"); 1163 Class<?> viewRootImplClass = Class.forName("android.view.ViewRootImpl"); 1164 sAttachInfoField = viewRootImplClass.getDeclaredField("mAttachInfo"); 1165 sVisibleInsetsField.setAccessible(true); 1166 sAttachInfoField.setAccessible(true); 1167 } catch (ReflectiveOperationException e) { 1168 Log.e(TAG, "Failed to get visible insets. (Reflection error). " + e.getMessage(), 1169 e); 1170 } 1171 sVisibleRectReflectionFetched = true; 1172 } 1173 1174 @Override equals(Object o)1175 public boolean equals(Object o) { 1176 if (!super.equals(o)) return false; 1177 Impl20 impl20 = (Impl20) o; 1178 return Objects.equals(mRootViewVisibleInsets, impl20.mRootViewVisibleInsets) 1179 && systemBarVisibilityEquals(mSystemUiVisibility, impl20.mSystemUiVisibility); 1180 } 1181 systemBarVisibilityEquals(int vis1, int vis2)1182 static boolean systemBarVisibilityEquals(int vis1, int vis2) { 1183 return (SYSTEM_BAR_VISIBILITY_MASK & vis1) == (SYSTEM_BAR_VISIBILITY_MASK & vis2); 1184 } 1185 } 1186 1187 @RequiresApi(21) 1188 private static class Impl21 extends Impl20 { 1189 private Insets mStableInsets = null; 1190 Impl21(@onNull WindowInsetsCompat host, @NonNull WindowInsets insets)1191 Impl21(@NonNull WindowInsetsCompat host, @NonNull WindowInsets insets) { 1192 super(host, insets); 1193 } 1194 Impl21(@onNull WindowInsetsCompat host, @NonNull Impl21 other)1195 Impl21(@NonNull WindowInsetsCompat host, @NonNull Impl21 other) { 1196 super(host, other); 1197 mStableInsets = other.mStableInsets; 1198 } 1199 1200 @Override isConsumed()1201 boolean isConsumed() { 1202 return mPlatformInsets.isConsumed(); 1203 } 1204 1205 @Override consumeStableInsets()1206 @NonNull WindowInsetsCompat consumeStableInsets() { 1207 return toWindowInsetsCompat(mPlatformInsets.consumeStableInsets()); 1208 } 1209 1210 @Override consumeSystemWindowInsets()1211 @NonNull WindowInsetsCompat consumeSystemWindowInsets() { 1212 return toWindowInsetsCompat(mPlatformInsets.consumeSystemWindowInsets()); 1213 } 1214 1215 @Override getStableInsets()1216 final @NonNull Insets getStableInsets() { 1217 if (mStableInsets == null) { 1218 mStableInsets = Insets.of( 1219 mPlatformInsets.getStableInsetLeft(), 1220 mPlatformInsets.getStableInsetTop(), 1221 mPlatformInsets.getStableInsetRight(), 1222 mPlatformInsets.getStableInsetBottom()); 1223 } 1224 return mStableInsets; 1225 } 1226 1227 @Override setStableInsets(@ullable Insets stableInsets)1228 public void setStableInsets(@Nullable Insets stableInsets) { 1229 mStableInsets = stableInsets; 1230 } 1231 1232 } 1233 1234 @RequiresApi(28) 1235 private static class Impl28 extends Impl21 { Impl28(@onNull WindowInsetsCompat host, @NonNull WindowInsets insets)1236 Impl28(@NonNull WindowInsetsCompat host, @NonNull WindowInsets insets) { 1237 super(host, insets); 1238 } 1239 Impl28(@onNull WindowInsetsCompat host, @NonNull Impl28 other)1240 Impl28(@NonNull WindowInsetsCompat host, @NonNull Impl28 other) { 1241 super(host, other); 1242 } 1243 1244 @Override getDisplayCutout()1245 @Nullable DisplayCutoutCompat getDisplayCutout() { 1246 return DisplayCutoutCompat.wrap(mPlatformInsets.getDisplayCutout()); 1247 } 1248 1249 @Override consumeDisplayCutout()1250 @NonNull WindowInsetsCompat consumeDisplayCutout() { 1251 return toWindowInsetsCompat(mPlatformInsets.consumeDisplayCutout()); 1252 } 1253 1254 @Override equals(Object o)1255 public boolean equals(Object o) { 1256 if (this == o) return true; 1257 if (!(o instanceof Impl28)) return false; 1258 Impl28 otherImpl28 = (Impl28) o; 1259 // On API 28+ we can rely on WindowInsets.equals() 1260 return Objects.equals(mPlatformInsets, otherImpl28.mPlatformInsets) 1261 && Objects.equals(mRootViewVisibleInsets, otherImpl28.mRootViewVisibleInsets) 1262 && systemBarVisibilityEquals( 1263 mSystemUiVisibility, otherImpl28.mSystemUiVisibility); 1264 } 1265 1266 @Override hashCode()1267 public int hashCode() { 1268 return mPlatformInsets.hashCode(); 1269 } 1270 } 1271 1272 @RequiresApi(29) 1273 private static class Impl29 extends Impl28 { 1274 // Used to cache the wrapped values 1275 private Insets mSystemGestureInsets = null; 1276 private Insets mMandatorySystemGestureInsets = null; 1277 private Insets mTappableElementInsets = null; 1278 Impl29(@onNull WindowInsetsCompat host, @NonNull WindowInsets insets)1279 Impl29(@NonNull WindowInsetsCompat host, @NonNull WindowInsets insets) { 1280 super(host, insets); 1281 } 1282 Impl29(@onNull WindowInsetsCompat host, @NonNull Impl29 other)1283 Impl29(@NonNull WindowInsetsCompat host, @NonNull Impl29 other) { 1284 super(host, other); 1285 } 1286 1287 @Override getSystemGestureInsets()1288 @NonNull Insets getSystemGestureInsets() { 1289 if (mSystemGestureInsets == null) { 1290 mSystemGestureInsets = toCompatInsets(mPlatformInsets.getSystemGestureInsets()); 1291 } 1292 return mSystemGestureInsets; 1293 } 1294 1295 @Override getMandatorySystemGestureInsets()1296 @NonNull Insets getMandatorySystemGestureInsets() { 1297 if (mMandatorySystemGestureInsets == null) { 1298 mMandatorySystemGestureInsets = 1299 toCompatInsets(mPlatformInsets.getMandatorySystemGestureInsets()); 1300 } 1301 return mMandatorySystemGestureInsets; 1302 } 1303 1304 @Override getTappableElementInsets()1305 @NonNull Insets getTappableElementInsets() { 1306 if (mTappableElementInsets == null) { 1307 mTappableElementInsets = toCompatInsets(mPlatformInsets.getTappableElementInsets()); 1308 } 1309 return mTappableElementInsets; 1310 } 1311 1312 @Override inset(int left, int top, int right, int bottom)1313 @NonNull WindowInsetsCompat inset(int left, int top, int right, int bottom) { 1314 final WindowInsetsCompat newInsets = 1315 toWindowInsetsCompat(mPlatformInsets.inset(left, top, right, bottom)); 1316 1317 // WindowInsets#insetInsets of API 29 has a bug, so here uses our own implementation. 1318 final WindowInsetsCompat.Builder builder = new WindowInsetsCompat.Builder(newInsets); 1319 builder.setSystemWindowInsets( 1320 insetInsets(getSystemWindowInsets(), left, top, right, bottom)); 1321 builder.setStableInsets( 1322 insetInsets(getStableInsets(), left, top, right, bottom)); 1323 builder.setSystemGestureInsets( 1324 insetInsets(getSystemGestureInsets(), left, top, right, bottom)); 1325 builder.setMandatorySystemGestureInsets( 1326 insetInsets(getMandatorySystemGestureInsets(), left, top, right, bottom)); 1327 builder.setTappableElementInsets( 1328 insetInsets(getTappableElementInsets(), left, top, right, bottom)); 1329 return builder.build(); 1330 } 1331 1332 @Override setStableInsets(@ullable Insets stableInsets)1333 public void setStableInsets(@Nullable Insets stableInsets) { 1334 //no-op already in mPlatformInsets 1335 } 1336 } 1337 insetInsets(@onNull Insets insets, int left, int top, int right, int bottom)1338 static Insets insetInsets(@NonNull Insets insets, int left, int top, int right, int bottom) { 1339 int newLeft = Math.max(0, insets.left - left); 1340 int newTop = Math.max(0, insets.top - top); 1341 int newRight = Math.max(0, insets.right - right); 1342 int newBottom = Math.max(0, insets.bottom - bottom); 1343 if (newLeft == insets.left && newTop == insets.top 1344 && newRight == insets.right && newBottom == insets.bottom) { 1345 return insets; 1346 } 1347 return Insets.of(newLeft, newTop, newRight, newBottom); 1348 } 1349 1350 @RequiresApi(30) 1351 private static class Impl30 extends Impl29 { 1352 static final @NonNull WindowInsetsCompat CONSUMED = 1353 toWindowInsetsCompat(WindowInsets.CONSUMED); 1354 Impl30(@onNull WindowInsetsCompat host, @NonNull WindowInsets insets)1355 Impl30(@NonNull WindowInsetsCompat host, @NonNull WindowInsets insets) { 1356 super(host, insets); 1357 } 1358 Impl30(@onNull WindowInsetsCompat host, @NonNull Impl30 other)1359 Impl30(@NonNull WindowInsetsCompat host, @NonNull Impl30 other) { 1360 super(host, other); 1361 } 1362 1363 @Override getInsets(int typeMask)1364 public @NonNull Insets getInsets(int typeMask) { 1365 return toCompatInsets( 1366 mPlatformInsets.getInsets(TypeImpl30.toPlatformType(typeMask)) 1367 ); 1368 } 1369 1370 @Override getInsetsIgnoringVisibility(int typeMask)1371 public @NonNull Insets getInsetsIgnoringVisibility(int typeMask) { 1372 return toCompatInsets( 1373 mPlatformInsets.getInsetsIgnoringVisibility(TypeImpl30.toPlatformType(typeMask)) 1374 ); 1375 } 1376 1377 @Override isVisible(int typeMask)1378 public boolean isVisible(int typeMask) { 1379 return mPlatformInsets.isVisible(TypeImpl30.toPlatformType(typeMask)); 1380 } 1381 1382 @Override copyRootViewBounds(@onNull View rootView)1383 final void copyRootViewBounds(@NonNull View rootView) { 1384 // This is only used to copy the root view visible insets which is 1385 // then only used to get the visibility of the IME on API < 30. 1386 // Overriding this avoid to go through the code path to get the visible insets via 1387 // reflection. 1388 } 1389 1390 @SuppressLint("WrongConstant") 1391 @Override inset(int left, int top, int right, int bottom)1392 @NonNull WindowInsetsCompat inset(int left, int top, int right, int bottom) { 1393 final WindowInsetsCompat newInsets = 1394 toWindowInsetsCompat(mPlatformInsets.inset(left, top, right, bottom)); 1395 1396 // WindowInsets#insetInsets of API 30-35 has a bug, so here uses our own implementation. 1397 final WindowInsetsCompat.Builder builder = new WindowInsetsCompat.Builder(newInsets); 1398 for (int i = Type.FIRST; i <= Type.LAST; i = i << 1) { 1399 builder.setInsets(i, insetInsets(getInsets(i), left, top, right, bottom)); 1400 if ((i & Type.IME) == 0) { 1401 builder.setInsetsIgnoringVisibility( 1402 i, 1403 insetInsets(getInsetsIgnoringVisibility(i), left, top, right, bottom)); 1404 } 1405 } 1406 return builder.build(); 1407 } 1408 } 1409 1410 @RequiresApi(34) 1411 private static class Impl34 extends Impl30 { 1412 static final @NonNull WindowInsetsCompat CONSUMED = 1413 toWindowInsetsCompat(WindowInsets.CONSUMED); 1414 Impl34(@onNull WindowInsetsCompat host, @NonNull WindowInsets insets)1415 Impl34(@NonNull WindowInsetsCompat host, @NonNull WindowInsets insets) { 1416 super(host, insets); 1417 } 1418 Impl34(@onNull WindowInsetsCompat host, @NonNull Impl34 other)1419 Impl34(@NonNull WindowInsetsCompat host, @NonNull Impl34 other) { 1420 super(host, other); 1421 } 1422 1423 @Override getInsets(int typeMask)1424 public @NonNull Insets getInsets(int typeMask) { 1425 return toCompatInsets( 1426 mPlatformInsets.getInsets(TypeImpl34.toPlatformType(typeMask)) 1427 ); 1428 } 1429 1430 @Override getInsetsIgnoringVisibility(int typeMask)1431 public @NonNull Insets getInsetsIgnoringVisibility(int typeMask) { 1432 return toCompatInsets( 1433 mPlatformInsets.getInsetsIgnoringVisibility(TypeImpl34.toPlatformType(typeMask)) 1434 ); 1435 } 1436 1437 @Override isVisible(int typeMask)1438 public boolean isVisible(int typeMask) { 1439 return mPlatformInsets.isVisible(TypeImpl34.toPlatformType(typeMask)); 1440 } 1441 } 1442 1443 @RequiresApi(36) 1444 private static class Impl36 extends Impl34 { 1445 Impl36(@onNull WindowInsetsCompat host, @NonNull WindowInsets insets)1446 Impl36(@NonNull WindowInsetsCompat host, @NonNull WindowInsets insets) { 1447 super(host, insets); 1448 } 1449 Impl36(@onNull WindowInsetsCompat host, @NonNull Impl36 other)1450 Impl36(@NonNull WindowInsetsCompat host, @NonNull Impl36 other) { 1451 super(host, other); 1452 } 1453 1454 @Override inset(int left, int top, int right, int bottom)1455 @NonNull WindowInsetsCompat inset(int left, int top, int right, int bottom) { 1456 return toWindowInsetsCompat(mPlatformInsets.inset(left, top, right, bottom)); 1457 } 1458 1459 } 1460 1461 /** 1462 * Builder for {@link WindowInsetsCompat}. 1463 */ 1464 public static final class Builder { 1465 private final BuilderImpl mImpl; 1466 1467 /** 1468 * Creates a builder where all insets are initially consumed. 1469 */ Builder()1470 public Builder() { 1471 if (SDK_INT >= 34) { 1472 mImpl = new BuilderImpl34(); 1473 } else if (SDK_INT >= 30) { 1474 mImpl = new BuilderImpl30(); 1475 } else if (SDK_INT >= 29) { 1476 mImpl = new BuilderImpl29(); 1477 } else if (SDK_INT >= 20) { 1478 mImpl = new BuilderImpl20(); 1479 } else { 1480 mImpl = new BuilderImpl(); 1481 } 1482 } 1483 1484 /** 1485 * Creates a builder where all insets are initialized from {@link WindowInsetsCompat}. 1486 * 1487 * @param insets the instance to initialize from. 1488 */ Builder(@onNull WindowInsetsCompat insets)1489 public Builder(@NonNull WindowInsetsCompat insets) { 1490 if (SDK_INT >= 34) { 1491 mImpl = new BuilderImpl34(insets); 1492 } else if (SDK_INT >= 30) { 1493 mImpl = new BuilderImpl30(insets); 1494 } else if (SDK_INT >= 29) { 1495 mImpl = new BuilderImpl29(insets); 1496 } else if (SDK_INT >= 20) { 1497 mImpl = new BuilderImpl20(insets); 1498 } else { 1499 mImpl = new BuilderImpl(insets); 1500 } 1501 } 1502 1503 /** 1504 * Sets system window insets in pixels. 1505 * 1506 * <p>The system window inset represents the area of a full-screen window that is 1507 * partially or fully obscured by the status bar, navigation bar, IME or other system 1508 * windows.</p> 1509 * 1510 * @return itself 1511 * @see #getSystemWindowInsets() 1512 * @deprecated Use {@link #setInsets(int, Insets)} with {@link Type#systemBars()}. 1513 */ 1514 @Deprecated setSystemWindowInsets(@onNull Insets insets)1515 public @NonNull Builder setSystemWindowInsets(@NonNull Insets insets) { 1516 mImpl.setSystemWindowInsets(insets); 1517 return this; 1518 } 1519 1520 /** 1521 * Sets system gesture insets in pixels. 1522 * 1523 * <p>The system gesture insets represent the area of a window where system gestures have 1524 * priority and may consume some or all touch input, e.g. due to the a system bar 1525 * occupying it, or it being reserved for touch-only gestures. 1526 * 1527 * <p>The insets passed will only take effect when running on API 29 and above. 1528 * 1529 * @return itself 1530 * @see #getSystemGestureInsets() 1531 * @deprecated Use {@link #setInsets(int, Insets)} with {@link Type#systemGestures()}. 1532 */ 1533 @Deprecated setSystemGestureInsets(@onNull Insets insets)1534 public @NonNull Builder setSystemGestureInsets(@NonNull Insets insets) { 1535 mImpl.setSystemGestureInsets(insets); 1536 return this; 1537 } 1538 1539 /** 1540 * Sets mandatory system gesture insets in pixels. 1541 * 1542 * <p>The mandatory system gesture insets represent the area of a window where mandatory 1543 * system gestures have priority and may consume some or all touch input, e.g. due to the a 1544 * system bar occupying it, or it being reserved for touch-only gestures. 1545 * 1546 * <p>In contrast to {@link #setSystemGestureInsets regular system gestures}, 1547 * <b>mandatory</b> system gestures cannot be overridden by 1548 * {@link ViewCompat#setSystemGestureExclusionRects}. 1549 * 1550 * <p>The insets passed will only take effect when running on API 29 and above. 1551 * 1552 * @return itself 1553 * @see #getMandatorySystemGestureInsets() 1554 * @deprecated Use {@link #setInsets(int, Insets)} with 1555 * {@link Type#mandatorySystemGestures()}. 1556 */ 1557 @Deprecated setMandatorySystemGestureInsets(@onNull Insets insets)1558 public @NonNull Builder setMandatorySystemGestureInsets(@NonNull Insets insets) { 1559 mImpl.setMandatorySystemGestureInsets(insets); 1560 return this; 1561 } 1562 1563 /** 1564 * Sets tappable element insets in pixels. 1565 * 1566 * <p>The tappable element insets represent how much tappable elements <b>must at least</b> 1567 * be inset to remain both tappable and visually unobstructed by persistent system windows. 1568 * 1569 * <p>The insets passed will only take effect when running on API 29 and above. 1570 * 1571 * @return itself 1572 * @see #getTappableElementInsets() 1573 * @deprecated Use {@link #setInsets(int, Insets)} with {@link Type#tappableElement()}. 1574 */ 1575 @Deprecated setTappableElementInsets(@onNull Insets insets)1576 public @NonNull Builder setTappableElementInsets(@NonNull Insets insets) { 1577 mImpl.setTappableElementInsets(insets); 1578 return this; 1579 } 1580 1581 /** 1582 * Sets the insets of a specific window type in pixels. 1583 * 1584 * <p>The insets represents the area of a a window that is partially or fully obscured by 1585 * the system windows identified by {@code typeMask}. 1586 * </p> 1587 * 1588 * @param typeMask The bitmask of {@link Type} to set the insets for. 1589 * @param insets The insets to set. 1590 * @return itself 1591 * @see #getInsets(int) 1592 */ setInsets(@nsetsType int typeMask, @NonNull Insets insets)1593 public @NonNull Builder setInsets(@InsetsType int typeMask, @NonNull Insets insets) { 1594 mImpl.setInsets(typeMask, insets); 1595 return this; 1596 } 1597 1598 /** 1599 * Sets the insets a specific window type in pixels, while ignoring its visibility state. 1600 * 1601 * <p>The insets represents the area of a a window that that <b>may</b> be partially 1602 * or fully obscured by the system window identified by {@code typeMask}. This value does 1603 * not change based on the visibility state of those elements. For example, if the status 1604 * bar is normally shown, but temporarily hidden, the inset returned here will still 1605 * provide the inset associated with the status bar being shown.</p> 1606 * 1607 * @param typeMask The bitmask of {@link Type} to set the insets for. 1608 * @param insets The insets to set. 1609 * @return itself 1610 * @throws IllegalArgumentException If {@code typeMask} contains {@link Type#ime()}. Maximum 1611 * insets are not available for this type as the height of 1612 * the IME is dynamic depending on the {@link EditorInfo} 1613 * of the currently focused view, as well as the UI 1614 * state of the IME. 1615 * @see #getInsetsIgnoringVisibility(int) 1616 */ setInsetsIgnoringVisibility(@nsetsType int typeMask, @NonNull Insets insets)1617 public @NonNull Builder setInsetsIgnoringVisibility(@InsetsType int typeMask, 1618 @NonNull Insets insets) { 1619 mImpl.setInsetsIgnoringVisibility(typeMask, insets); 1620 return this; 1621 } 1622 1623 /** 1624 * Sets whether windows that can cause insets are currently visible on screen. 1625 * 1626 * @param typeMask The bitmask of {@link Type} to set the visibility for. 1627 * @param visible Whether to mark the windows as visible or not. 1628 * @return itself 1629 * @see #isVisible(int) 1630 */ setVisible(@nsetsType int typeMask, boolean visible)1631 public @NonNull Builder setVisible(@InsetsType int typeMask, boolean visible) { 1632 mImpl.setVisible(typeMask, visible); 1633 return this; 1634 } 1635 1636 /** 1637 * Sets the stable insets in pixels. 1638 * 1639 * <p>The stable inset represents the area of a full-screen window that <b>may</b> be 1640 * partially or fully obscured by the system UI elements. This value does not change 1641 * based on the visibility state of those elements; for example, if the status bar is 1642 * normally shown, but temporarily hidden, the stable inset will still provide the inset 1643 * associated with the status bar being shown.</p> 1644 * 1645 * <p>The insets passed will only take effect when running on API 29 and above. 1646 * 1647 * @return itself 1648 * @see #getStableInsets() 1649 * @deprecated Use {@link #setInsetsIgnoringVisibility(int, Insets)} with 1650 * {@link Type#systemBars()}. 1651 */ 1652 @Deprecated setStableInsets(@onNull Insets insets)1653 public @NonNull Builder setStableInsets(@NonNull Insets insets) { 1654 mImpl.setStableInsets(insets); 1655 return this; 1656 } 1657 1658 /** 1659 * Sets the display cutout. 1660 * 1661 * <p>The cutout passed will only take effect when running on API 29 and above. 1662 * 1663 * @param displayCutout the display cutout or null if there is none 1664 * @return itself 1665 * @see #getDisplayCutout() 1666 */ setDisplayCutout(@ullable DisplayCutoutCompat displayCutout)1667 public @NonNull Builder setDisplayCutout(@Nullable DisplayCutoutCompat displayCutout) { 1668 mImpl.setDisplayCutout(displayCutout); 1669 return this; 1670 } 1671 1672 /** 1673 * Builds a {@link WindowInsetsCompat} instance. 1674 * 1675 * @return the {@link WindowInsetsCompat} instance. 1676 */ build()1677 public @NonNull WindowInsetsCompat build() { 1678 return mImpl.build(); 1679 } 1680 } 1681 1682 private static class BuilderImpl { 1683 private final WindowInsetsCompat mInsets; 1684 1685 Insets[] mInsetsTypeMask; 1686 BuilderImpl()1687 BuilderImpl() { 1688 this(new WindowInsetsCompat((WindowInsetsCompat) null)); 1689 } 1690 BuilderImpl(@onNull WindowInsetsCompat insets)1691 BuilderImpl(@NonNull WindowInsetsCompat insets) { 1692 mInsets = insets; 1693 } 1694 setSystemWindowInsets(@onNull Insets insets)1695 void setSystemWindowInsets(@NonNull Insets insets) {} 1696 setSystemGestureInsets(@onNull Insets insets)1697 void setSystemGestureInsets(@NonNull Insets insets) {} 1698 setMandatorySystemGestureInsets(@onNull Insets insets)1699 void setMandatorySystemGestureInsets(@NonNull Insets insets) {} 1700 setTappableElementInsets(@onNull Insets insets)1701 void setTappableElementInsets(@NonNull Insets insets) {} 1702 setStableInsets(@onNull Insets insets)1703 void setStableInsets(@NonNull Insets insets) {} 1704 setDisplayCutout(@ullable DisplayCutoutCompat displayCutout)1705 void setDisplayCutout(@Nullable DisplayCutoutCompat displayCutout) {} 1706 1707 @SuppressWarnings("WrongConstant") setInsets(int typeMask, @NonNull Insets insets)1708 void setInsets(int typeMask, @NonNull Insets insets) { 1709 if (mInsetsTypeMask == null) { 1710 mInsetsTypeMask = new Insets[Type.SIZE]; 1711 } 1712 for (int i = Type.FIRST; i <= Type.LAST; i = i << 1) { 1713 if ((typeMask & i) == 0) { 1714 continue; 1715 } 1716 mInsetsTypeMask[Type.indexOf(i)] = insets; 1717 } 1718 } 1719 setInsetsIgnoringVisibility(int typeMask, @NonNull Insets insets)1720 void setInsetsIgnoringVisibility(int typeMask, @NonNull Insets insets) { 1721 if (typeMask == Type.IME) { 1722 // Not strictly necessary but means we will throw an exception on all platforms, 1723 // rather than just R, which is clearer for all users 1724 throw new IllegalArgumentException("Ignoring visibility inset not available" 1725 + " for IME"); 1726 } 1727 } 1728 setVisible(int typeMask, boolean visible)1729 void setVisible(int typeMask, boolean visible) {} 1730 1731 /** 1732 * This method tries to apply any insets set via {@link #setInsets(int, Insets)} to 1733 * the insets builder. This function will be a no-op on API 30 since 1734 * {@link BuilderImpl30#setInsets(int, Insets)} does not update the array. 1735 */ applyInsetTypes()1736 protected final void applyInsetTypes() { 1737 if (mInsetsTypeMask != null) { 1738 Insets statusBars = mInsetsTypeMask[Type.indexOf(Type.STATUS_BARS)]; 1739 Insets navigationBars = mInsetsTypeMask[Type.indexOf(Type.NAVIGATION_BARS)]; 1740 1741 // If the insets are not set in the builder, default to the insets passed in 1742 // the builder parameter to avoid accidentally setting them to 0 1743 if (navigationBars == null) { 1744 navigationBars = mInsets.getInsets(Type.NAVIGATION_BARS); 1745 } 1746 if (statusBars == null) { 1747 statusBars = mInsets.getInsets(Type.STATUS_BARS); 1748 } 1749 1750 setSystemWindowInsets(Insets.max(statusBars, navigationBars)); 1751 1752 Insets i = mInsetsTypeMask[Type.indexOf(Type.SYSTEM_GESTURES)]; 1753 if (i != null) setSystemGestureInsets(i); 1754 1755 i = mInsetsTypeMask[Type.indexOf(Type.MANDATORY_SYSTEM_GESTURES)]; 1756 if (i != null) setMandatorySystemGestureInsets(i); 1757 1758 i = mInsetsTypeMask[Type.indexOf(Type.TAPPABLE_ELEMENT)]; 1759 if (i != null) setTappableElementInsets(i); 1760 } 1761 } 1762 build()1763 @NonNull WindowInsetsCompat build() { 1764 applyInsetTypes(); 1765 return mInsets; 1766 } 1767 } 1768 setOverriddenInsets(Insets[] insetsTypeMask)1769 void setOverriddenInsets(Insets[] insetsTypeMask) { 1770 mImpl.setOverriddenInsets(insetsTypeMask); 1771 } 1772 1773 @RequiresApi(api = 20) 1774 private static class BuilderImpl20 extends BuilderImpl { 1775 private static Field sConsumedField; 1776 private static boolean sConsumedFieldFetched = false; 1777 1778 private static Constructor<WindowInsets> sConstructor; 1779 private static boolean sConstructorFetched = false; 1780 1781 private WindowInsets mPlatformInsets; 1782 private Insets mStableInsets; 1783 BuilderImpl20()1784 BuilderImpl20() { 1785 mPlatformInsets = createWindowInsetsInstance(); 1786 } 1787 BuilderImpl20(@onNull WindowInsetsCompat insets)1788 BuilderImpl20(@NonNull WindowInsetsCompat insets) { 1789 super(insets); 1790 mPlatformInsets = insets.toWindowInsets(); 1791 } 1792 1793 @Override setSystemWindowInsets(@onNull Insets insets)1794 void setSystemWindowInsets(@NonNull Insets insets) { 1795 if (mPlatformInsets != null) { 1796 mPlatformInsets = mPlatformInsets.replaceSystemWindowInsets( 1797 insets.left, insets.top, insets.right, insets.bottom); 1798 } 1799 } 1800 1801 @Override setStableInsets(@ullable Insets insets)1802 void setStableInsets(@Nullable Insets insets) { 1803 mStableInsets = insets; 1804 } 1805 1806 @Override build()1807 @NonNull WindowInsetsCompat build() { 1808 applyInsetTypes(); 1809 WindowInsetsCompat windowInsetsCompat = WindowInsetsCompat.toWindowInsetsCompat( 1810 mPlatformInsets); 1811 windowInsetsCompat.setOverriddenInsets(this.mInsetsTypeMask); 1812 windowInsetsCompat.setStableInsets(mStableInsets); 1813 return windowInsetsCompat; 1814 } 1815 1816 @SuppressWarnings("JavaReflectionMemberAccess") createWindowInsetsInstance()1817 private static @Nullable WindowInsets createWindowInsetsInstance() { 1818 // On API 20-28, there is no public way to create an WindowInsets instance, so we 1819 // need to use reflection. 1820 1821 // We will first try getting the WindowInsets.CONSUMED static field, and creating a 1822 // copy of it 1823 if (!sConsumedFieldFetched) { 1824 try { 1825 sConsumedField = WindowInsets.class.getDeclaredField("CONSUMED"); 1826 } catch (ReflectiveOperationException e) { 1827 Log.i(TAG, "Could not retrieve WindowInsets.CONSUMED field", e); 1828 } 1829 sConsumedFieldFetched = true; 1830 } 1831 if (sConsumedField != null) { 1832 try { 1833 WindowInsets consumed = (WindowInsets) sConsumedField.get(null); 1834 if (consumed != null) { 1835 return new WindowInsets(consumed); 1836 } 1837 } catch (ReflectiveOperationException e) { 1838 Log.i(TAG, "Could not get value from WindowInsets.CONSUMED field", e); 1839 } 1840 } 1841 1842 // If we reached here, the WindowInsets.CONSUMED field did not exist. We can try 1843 // the hidden WindowInsets(Rect) constructor instead 1844 if (!sConstructorFetched) { 1845 try { 1846 sConstructor = WindowInsets.class.getConstructor(Rect.class); 1847 } catch (ReflectiveOperationException e) { 1848 Log.i(TAG, "Could not retrieve WindowInsets(Rect) constructor", e); 1849 } 1850 sConstructorFetched = true; 1851 } 1852 if (sConstructor != null) { 1853 try { 1854 return sConstructor.newInstance(new Rect()); 1855 } catch (ReflectiveOperationException e) { 1856 Log.i(TAG, "Could not invoke WindowInsets(Rect) constructor", e); 1857 } 1858 } 1859 1860 // If the reflective calls failed, return null 1861 return null; 1862 } 1863 } 1864 setStableInsets(@ullable Insets stableInsets)1865 void setStableInsets(@Nullable Insets stableInsets) { 1866 mImpl.setStableInsets(stableInsets); 1867 } 1868 1869 @RequiresApi(api = 29) 1870 private static class BuilderImpl29 extends BuilderImpl { 1871 final WindowInsets.Builder mPlatBuilder; 1872 BuilderImpl29()1873 BuilderImpl29() { 1874 super(); 1875 mPlatBuilder = new WindowInsets.Builder(); 1876 } 1877 BuilderImpl29(@onNull WindowInsetsCompat insets)1878 BuilderImpl29(@NonNull WindowInsetsCompat insets) { 1879 super(insets); 1880 final WindowInsets platInsets = insets.toWindowInsets(); 1881 mPlatBuilder = platInsets != null 1882 ? new WindowInsets.Builder(platInsets) 1883 : new WindowInsets.Builder(); 1884 } 1885 1886 @Override setSystemWindowInsets(@onNull Insets insets)1887 void setSystemWindowInsets(@NonNull Insets insets) { 1888 mPlatBuilder.setSystemWindowInsets(insets.toPlatformInsets()); 1889 } 1890 1891 @Override setSystemGestureInsets(@onNull Insets insets)1892 void setSystemGestureInsets(@NonNull Insets insets) { 1893 mPlatBuilder.setSystemGestureInsets(insets.toPlatformInsets()); 1894 } 1895 1896 @Override setMandatorySystemGestureInsets(@onNull Insets insets)1897 void setMandatorySystemGestureInsets(@NonNull Insets insets) { 1898 mPlatBuilder.setMandatorySystemGestureInsets(insets.toPlatformInsets()); 1899 } 1900 1901 @Override setTappableElementInsets(@onNull Insets insets)1902 void setTappableElementInsets(@NonNull Insets insets) { 1903 mPlatBuilder.setTappableElementInsets(insets.toPlatformInsets()); 1904 } 1905 1906 @Override setStableInsets(@onNull Insets insets)1907 void setStableInsets(@NonNull Insets insets) { 1908 mPlatBuilder.setStableInsets(insets.toPlatformInsets()); 1909 } 1910 1911 @Override setDisplayCutout(@ullable DisplayCutoutCompat displayCutout)1912 void setDisplayCutout(@Nullable DisplayCutoutCompat displayCutout) { 1913 mPlatBuilder.setDisplayCutout(displayCutout != null ? displayCutout.unwrap() : null); 1914 } 1915 1916 @Override build()1917 @NonNull WindowInsetsCompat build() { 1918 applyInsetTypes(); 1919 WindowInsetsCompat windowInsetsCompat = WindowInsetsCompat.toWindowInsetsCompat( 1920 mPlatBuilder.build()); 1921 windowInsetsCompat.setOverriddenInsets(mInsetsTypeMask); 1922 return windowInsetsCompat; 1923 } 1924 } 1925 1926 @RequiresApi(30) 1927 private static class BuilderImpl30 extends BuilderImpl29 { BuilderImpl30()1928 BuilderImpl30() { 1929 super(); 1930 } 1931 BuilderImpl30(@onNull WindowInsetsCompat insets)1932 BuilderImpl30(@NonNull WindowInsetsCompat insets) { 1933 super(insets); 1934 } 1935 1936 @Override setInsets(int typeMask, @NonNull Insets insets)1937 void setInsets(int typeMask, @NonNull Insets insets) { 1938 mPlatBuilder.setInsets( 1939 TypeImpl30.toPlatformType(typeMask), 1940 insets.toPlatformInsets() 1941 ); 1942 } 1943 1944 @Override setInsetsIgnoringVisibility(int typeMask, @NonNull Insets insets)1945 void setInsetsIgnoringVisibility(int typeMask, @NonNull Insets insets) { 1946 mPlatBuilder.setInsetsIgnoringVisibility( 1947 TypeImpl30.toPlatformType(typeMask), 1948 insets.toPlatformInsets() 1949 ); 1950 } 1951 1952 @Override setVisible(int typeMask, boolean visible)1953 void setVisible(int typeMask, boolean visible) { 1954 mPlatBuilder.setVisible(TypeImpl30.toPlatformType(typeMask), visible); 1955 } 1956 } 1957 1958 @RequiresApi(34) 1959 private static class BuilderImpl34 extends BuilderImpl30 { BuilderImpl34()1960 BuilderImpl34() { 1961 super(); 1962 } 1963 BuilderImpl34(@onNull WindowInsetsCompat insets)1964 BuilderImpl34(@NonNull WindowInsetsCompat insets) { 1965 super(insets); 1966 } 1967 1968 @Override setInsets(int typeMask, @NonNull Insets insets)1969 void setInsets(int typeMask, @NonNull Insets insets) { 1970 mPlatBuilder.setInsets( 1971 TypeImpl34.toPlatformType(typeMask), 1972 insets.toPlatformInsets() 1973 ); 1974 } 1975 1976 @Override setInsetsIgnoringVisibility(int typeMask, @NonNull Insets insets)1977 void setInsetsIgnoringVisibility(int typeMask, @NonNull Insets insets) { 1978 mPlatBuilder.setInsetsIgnoringVisibility( 1979 TypeImpl34.toPlatformType(typeMask), 1980 insets.toPlatformInsets() 1981 ); 1982 } 1983 1984 @Override setVisible(int typeMask, boolean visible)1985 void setVisible(int typeMask, boolean visible) { 1986 mPlatBuilder.setVisible(TypeImpl34.toPlatformType(typeMask), visible); 1987 } 1988 } 1989 1990 /** 1991 * Class that defines different types of sources causing window insets. 1992 */ 1993 public static final class Type { 1994 static final int FIRST = 1; 1995 static final int STATUS_BARS = FIRST; 1996 static final int NAVIGATION_BARS = 1 << 1; 1997 static final int CAPTION_BAR = 1 << 2; 1998 1999 static final int IME = 1 << 3; 2000 2001 static final int SYSTEM_GESTURES = 1 << 4; 2002 static final int MANDATORY_SYSTEM_GESTURES = 1 << 5; 2003 static final int TAPPABLE_ELEMENT = 1 << 6; 2004 2005 static final int DISPLAY_CUTOUT = 1 << 7; 2006 2007 static final int WINDOW_DECOR = 1 << 8; 2008 static final int SYSTEM_OVERLAYS = 1 << 9; 2009 static final int LAST = SYSTEM_OVERLAYS; 2010 static final int SIZE = 10; 2011 Type()2012 private Type() {} 2013 2014 /** 2015 * @return An insets type representing any system bars for displaying status. 2016 */ 2017 @InsetsType statusBars()2018 public static int statusBars() { 2019 return STATUS_BARS; 2020 } 2021 2022 /** 2023 * @return An insets type representing any system bars for navigation. 2024 */ 2025 @InsetsType navigationBars()2026 public static int navigationBars() { 2027 return NAVIGATION_BARS; 2028 } 2029 2030 /** 2031 * @return An insets type representing the window of a caption bar. 2032 */ 2033 @InsetsType captionBar()2034 public static int captionBar() { 2035 return CAPTION_BAR; 2036 } 2037 2038 /** 2039 * @return An insets type representing the window of an {@link InputMethod}. 2040 */ 2041 @InsetsType ime()2042 public static int ime() { 2043 return IME; 2044 } 2045 2046 /** 2047 * Returns an insets type representing the system gesture insets. 2048 * 2049 * <p>The system gesture insets represent the area of a window where system gestures have 2050 * priority and may consume some or all touch input, e.g. due to the a system bar 2051 * occupying it, or it being reserved for touch-only gestures. 2052 * 2053 * <p>Simple taps are guaranteed to reach the window even within the system gesture insets, 2054 * as long as they are outside the {@link #getSystemWindowInsets() system window insets}. 2055 * 2056 * <p>When {@link View#SYSTEM_UI_FLAG_LAYOUT_STABLE} is requested, an inset will be returned 2057 * even when the system gestures are inactive due to 2058 * {@link View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN} or 2059 * {@link View#SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION}. 2060 * 2061 * @see #getSystemGestureInsets() 2062 */ 2063 @InsetsType systemGestures()2064 public static int systemGestures() { 2065 return SYSTEM_GESTURES; 2066 } 2067 2068 /** 2069 * @see #getMandatorySystemGestureInsets 2070 */ 2071 @InsetsType mandatorySystemGestures()2072 public static int mandatorySystemGestures() { 2073 return MANDATORY_SYSTEM_GESTURES; 2074 } 2075 2076 /** 2077 * @see #getTappableElementInsets 2078 */ 2079 @InsetsType tappableElement()2080 public static int tappableElement() { 2081 return TAPPABLE_ELEMENT; 2082 } 2083 2084 /** 2085 * Returns an insets type representing the area that used by {@link DisplayCutoutCompat}. 2086 * 2087 * <p>This is equivalent to the safe insets on {@link #getDisplayCutout()}.</p> 2088 * 2089 * @see DisplayCutoutCompat#getSafeInsetLeft() 2090 * @see DisplayCutoutCompat#getSafeInsetTop() 2091 * @see DisplayCutoutCompat#getSafeInsetRight() 2092 * @see DisplayCutoutCompat#getSafeInsetBottom() 2093 */ 2094 @InsetsType displayCutout()2095 public static int displayCutout() { 2096 return DISPLAY_CUTOUT; 2097 } 2098 2099 /** 2100 * System overlays represent the insets caused by the system visible elements. Unlike 2101 * {@link #navigationBars()} or {@link #statusBars()}, system overlays might not be 2102 * hidden by the client. 2103 * 2104 * For compatibility reasons, this type is included in {@link #systemBars()}. In this 2105 * way, views which fit {@link #systemBars()} fit {@link #systemOverlays()}. 2106 * 2107 * Examples include climate controls, multi-tasking affordances, etc. 2108 * 2109 * @return An insets type representing the system overlays. 2110 */ 2111 @InsetsType systemOverlays()2112 public static int systemOverlays() { 2113 return SYSTEM_OVERLAYS; 2114 } 2115 2116 /** 2117 * @return All system bars. Includes {@link #statusBars()}, {@link #captionBar()} as well as 2118 * {@link #navigationBars()}, {@link #systemOverlays()} but not {@link #ime()}. 2119 */ 2120 @InsetsType systemBars()2121 public static int systemBars() { 2122 return STATUS_BARS | NAVIGATION_BARS | CAPTION_BAR | SYSTEM_OVERLAYS; 2123 } 2124 2125 /** 2126 * @return All inset types combined. 2127 */ 2128 @InsetsType 2129 @RestrictTo(LIBRARY_GROUP) 2130 @SuppressLint("WrongConstant") all()2131 static int all() { 2132 return 0xFFFFFFFF; 2133 } 2134 indexOf(@nsetsType int type)2135 static int indexOf(@InsetsType int type) { 2136 switch (type) { 2137 case STATUS_BARS: 2138 return 0; 2139 case NAVIGATION_BARS: 2140 return 1; 2141 case CAPTION_BAR: 2142 return 2; 2143 case IME: 2144 return 3; 2145 case SYSTEM_GESTURES: 2146 return 4; 2147 case MANDATORY_SYSTEM_GESTURES: 2148 return 5; 2149 case TAPPABLE_ELEMENT: 2150 return 6; 2151 case DISPLAY_CUTOUT: 2152 return 7; 2153 case WINDOW_DECOR: 2154 return 8; 2155 case SYSTEM_OVERLAYS: 2156 return 9; 2157 default: 2158 throw new IllegalArgumentException("type needs to be >= FIRST and <= LAST," 2159 + " type=" + type); 2160 } 2161 } 2162 2163 @RestrictTo(LIBRARY_GROUP) 2164 @Retention(RetentionPolicy.SOURCE) 2165 @IntDef(flag = true, value = {STATUS_BARS, NAVIGATION_BARS, CAPTION_BAR, IME, WINDOW_DECOR, 2166 SYSTEM_GESTURES, MANDATORY_SYSTEM_GESTURES, TAPPABLE_ELEMENT, DISPLAY_CUTOUT, 2167 SYSTEM_OVERLAYS}) 2168 public @interface InsetsType { 2169 } 2170 } 2171 2172 /** 2173 * Class that defines different sides for insets. 2174 */ 2175 public static final class Side { 2176 public static final int LEFT = 1 << 0; 2177 public static final int TOP = 1 << 1; 2178 public static final int RIGHT = 1 << 2; 2179 public static final int BOTTOM = 1 << 3; 2180 Side()2181 private Side() { 2182 } 2183 2184 @RestrictTo(LIBRARY_GROUP) 2185 @Retention(RetentionPolicy.SOURCE) 2186 @IntDef(flag = true, value = {LEFT, TOP, RIGHT, BOTTOM}) 2187 public @interface InsetsSide { 2188 } 2189 2190 /** 2191 * @return all four sides. 2192 */ all()2193 public static @InsetsSide int all() { 2194 return LEFT | TOP | RIGHT | BOTTOM; 2195 } 2196 } 2197 2198 @RequiresApi(30) 2199 private static final class TypeImpl30 { TypeImpl30()2200 private TypeImpl30() {} 2201 2202 /** 2203 * Maps from our internal type mask constants to the platform's. Ideally we will keep the 2204 * constant values in sync, but this allows the platform to return different constants in 2205 * the future without breaking the logic in this class. 2206 */ toPlatformType(@nsetsType final int typeMask)2207 static int toPlatformType(@InsetsType final int typeMask) { 2208 int result = 0; 2209 for (int i = Type.FIRST; i <= Type.LAST; i = i << 1) { 2210 if ((typeMask & i) != 0) { 2211 switch (i) { 2212 case Type.STATUS_BARS: 2213 result |= WindowInsets.Type.statusBars(); 2214 break; 2215 case Type.NAVIGATION_BARS: 2216 result |= WindowInsets.Type.navigationBars(); 2217 break; 2218 case Type.CAPTION_BAR: 2219 result |= WindowInsets.Type.captionBar(); 2220 break; 2221 case Type.IME: 2222 result |= WindowInsets.Type.ime(); 2223 break; 2224 case Type.SYSTEM_GESTURES: 2225 result |= WindowInsets.Type.systemGestures(); 2226 break; 2227 case Type.MANDATORY_SYSTEM_GESTURES: 2228 result |= WindowInsets.Type.mandatorySystemGestures(); 2229 break; 2230 case Type.TAPPABLE_ELEMENT: 2231 result |= WindowInsets.Type.tappableElement(); 2232 break; 2233 case Type.DISPLAY_CUTOUT: 2234 result |= WindowInsets.Type.displayCutout(); 2235 break; 2236 } 2237 } 2238 } 2239 return result; 2240 } 2241 } 2242 2243 @RequiresApi(34) 2244 private static final class TypeImpl34 { TypeImpl34()2245 private TypeImpl34() {} 2246 2247 /** 2248 * Maps from our internal type mask constants to the platform's. Ideally we will keep the 2249 * constant values in sync, but this allows the platform to return different constants in 2250 * the future without breaking the logic in this class. 2251 */ toPlatformType(@nsetsType final int typeMask)2252 static int toPlatformType(@InsetsType final int typeMask) { 2253 int result = 0; 2254 for (int i = Type.FIRST; i <= Type.LAST; i = i << 1) { 2255 if ((typeMask & i) != 0) { 2256 switch (i) { 2257 case Type.STATUS_BARS: 2258 result |= WindowInsets.Type.statusBars(); 2259 break; 2260 case Type.NAVIGATION_BARS: 2261 result |= WindowInsets.Type.navigationBars(); 2262 break; 2263 case Type.CAPTION_BAR: 2264 result |= WindowInsets.Type.captionBar(); 2265 break; 2266 case Type.IME: 2267 result |= WindowInsets.Type.ime(); 2268 break; 2269 case Type.SYSTEM_GESTURES: 2270 result |= WindowInsets.Type.systemGestures(); 2271 break; 2272 case Type.MANDATORY_SYSTEM_GESTURES: 2273 result |= WindowInsets.Type.mandatorySystemGestures(); 2274 break; 2275 case Type.TAPPABLE_ELEMENT: 2276 result |= WindowInsets.Type.tappableElement(); 2277 break; 2278 case Type.DISPLAY_CUTOUT: 2279 result |= WindowInsets.Type.displayCutout(); 2280 break; 2281 case Type.SYSTEM_OVERLAYS: 2282 result |= WindowInsets.Type.systemOverlays(); 2283 break; 2284 } 2285 } 2286 } 2287 return result; 2288 } 2289 } 2290 setRootWindowInsets(@ullable WindowInsetsCompat rootWindowInsets)2291 void setRootWindowInsets(@Nullable WindowInsetsCompat rootWindowInsets) { 2292 mImpl.setRootWindowInsets(rootWindowInsets); 2293 } 2294 setRootViewData(@onNull Insets visibleInsets)2295 void setRootViewData(@NonNull Insets visibleInsets) { 2296 mImpl.setRootViewData(visibleInsets); 2297 } 2298 copyRootViewBounds(@onNull View rootView)2299 void copyRootViewBounds(@NonNull View rootView) { 2300 mImpl.copyRootViewBounds(rootView); 2301 } 2302 setSystemUiVisibility(int systemUiVisibility)2303 void setSystemUiVisibility(int systemUiVisibility) { 2304 mImpl.setSystemUiVisibility(systemUiVisibility); 2305 } 2306 2307 @SuppressWarnings("JavaReflectionMemberAccess") // Reflection on private field 2308 @SuppressLint("SoonBlockedPrivateApi") // mAttachInfo is only accessed on SDK 21 and 22 2309 @RequiresApi(21) 2310 static class Api21ReflectionHolder { 2311 Api21ReflectionHolder()2312 private Api21ReflectionHolder() { 2313 // This class is not instantiable. 2314 } 2315 2316 private static Field sViewAttachInfoField; // Only accessed on SDK 21 and 222 2317 private static Field sStableInsets; 2318 private static Field sContentInsets; 2319 private static boolean sReflectionSucceeded; 2320 2321 static { 2322 try { 2323 sViewAttachInfoField = View.class.getDeclaredField("mAttachInfo"); 2324 sViewAttachInfoField.setAccessible(true); 2325 Class<?> sAttachInfoClass = Class.forName("android.view.View$AttachInfo"); 2326 sStableInsets = sAttachInfoClass.getDeclaredField("mStableInsets"); 2327 sStableInsets.setAccessible(true); 2328 sContentInsets = sAttachInfoClass.getDeclaredField("mContentInsets"); 2329 sContentInsets.setAccessible(true); 2330 sReflectionSucceeded = true; 2331 } catch (ReflectiveOperationException e) { 2332 Log.w(TAG, "Failed to get visible insets from AttachInfo " + e.getMessage(), e); 2333 } 2334 } 2335 2336 // Only called on SDK 21 and 22 2337 @SuppressWarnings("deprecation") getRootWindowInsets(@onNull View v)2338 public static @Nullable WindowInsetsCompat getRootWindowInsets(@NonNull View v) { 2339 if (!sReflectionSucceeded || !v.isAttachedToWindow()) { 2340 return null; 2341 } 2342 2343 View rootView = v.getRootView(); 2344 try { 2345 Object attachInfo = sViewAttachInfoField.get(rootView); 2346 if (attachInfo != null) { 2347 Rect stableInsets = (Rect) sStableInsets.get(attachInfo); 2348 Rect visibleInsets = (Rect) sContentInsets.get(attachInfo); 2349 if (stableInsets != null && visibleInsets != null) { 2350 WindowInsetsCompat insets = new Builder() 2351 .setStableInsets(Insets.of(stableInsets)) 2352 .setSystemWindowInsets(Insets.of(visibleInsets)) 2353 .build(); 2354 2355 // The WindowInsetsCompat instance still needs to know about 2356 // what the root window insets, and the root view visible bounds are 2357 insets.setRootWindowInsets(insets); 2358 insets.copyRootViewBounds(v.getRootView()); 2359 return insets; 2360 } 2361 } 2362 } catch (IllegalAccessException e) { 2363 Log.w(TAG, "Failed to get insets from AttachInfo. " + e.getMessage(), e); 2364 } 2365 return null; 2366 } 2367 } 2368 } 2369