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.os.Trace.TRACE_TAG_VIEW; 20 import static android.view.InsetsControllerProto.CONTROL; 21 import static android.view.InsetsControllerProto.STATE; 22 import static android.view.InsetsState.ITYPE_CAPTION_BAR; 23 import static android.view.InsetsState.ITYPE_IME; 24 import static android.view.InsetsState.toInternalType; 25 import static android.view.InsetsState.toPublicType; 26 import static android.view.WindowInsets.Type.all; 27 import static android.view.WindowInsets.Type.ime; 28 29 import android.animation.AnimationHandler; 30 import android.animation.Animator; 31 import android.animation.AnimatorListenerAdapter; 32 import android.animation.TypeEvaluator; 33 import android.animation.ValueAnimator; 34 import android.annotation.IntDef; 35 import android.annotation.NonNull; 36 import android.annotation.Nullable; 37 import android.content.res.CompatibilityInfo; 38 import android.graphics.Insets; 39 import android.graphics.Rect; 40 import android.os.CancellationSignal; 41 import android.os.Handler; 42 import android.os.IBinder; 43 import android.os.Trace; 44 import android.util.ArraySet; 45 import android.util.Log; 46 import android.util.Pair; 47 import android.util.SparseArray; 48 import android.util.imetracing.ImeTracing; 49 import android.util.proto.ProtoOutputStream; 50 import android.view.InsetsSourceConsumer.ShowResult; 51 import android.view.InsetsState.InternalInsetsType; 52 import android.view.SurfaceControl.Transaction; 53 import android.view.WindowInsets.Type; 54 import android.view.WindowInsets.Type.InsetsType; 55 import android.view.WindowInsetsAnimation.Bounds; 56 import android.view.WindowManager.LayoutParams.SoftInputModeFlags; 57 import android.view.animation.Interpolator; 58 import android.view.animation.LinearInterpolator; 59 import android.view.animation.PathInterpolator; 60 import android.view.inputmethod.InputMethodManager; 61 62 import com.android.internal.annotations.VisibleForTesting; 63 import com.android.internal.graphics.SfVsyncFrameCallbackProvider; 64 65 import java.io.PrintWriter; 66 import java.lang.annotation.Retention; 67 import java.lang.annotation.RetentionPolicy; 68 import java.util.ArrayList; 69 import java.util.Collections; 70 import java.util.List; 71 import java.util.Objects; 72 import java.util.function.BiFunction; 73 74 /** 75 * Implements {@link WindowInsetsController} on the client. 76 * @hide 77 */ 78 public class InsetsController implements WindowInsetsController, InsetsAnimationControlCallbacks { 79 80 private int mTypesBeingCancelled; 81 82 public interface Host { 83 getHandler()84 Handler getHandler(); 85 86 /** 87 * Notifies host that {@link InsetsController#getState()} has changed. 88 */ notifyInsetsChanged()89 void notifyInsetsChanged(); 90 dispatchWindowInsetsAnimationPrepare(@onNull WindowInsetsAnimation animation)91 void dispatchWindowInsetsAnimationPrepare(@NonNull WindowInsetsAnimation animation); dispatchWindowInsetsAnimationStart( @onNull WindowInsetsAnimation animation, @NonNull Bounds bounds)92 Bounds dispatchWindowInsetsAnimationStart( 93 @NonNull WindowInsetsAnimation animation, @NonNull Bounds bounds); dispatchWindowInsetsAnimationProgress(@onNull WindowInsets insets, @NonNull List<WindowInsetsAnimation> runningAnimations)94 WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull WindowInsets insets, 95 @NonNull List<WindowInsetsAnimation> runningAnimations); dispatchWindowInsetsAnimationEnd(@onNull WindowInsetsAnimation animation)96 void dispatchWindowInsetsAnimationEnd(@NonNull WindowInsetsAnimation animation); 97 98 /** 99 * Requests host to apply surface params in synchronized manner. 100 */ applySurfaceParams(final SyncRtSurfaceTransactionApplier.SurfaceParams... params)101 void applySurfaceParams(final SyncRtSurfaceTransactionApplier.SurfaceParams... params); 102 103 /** 104 * @see ViewRootImpl#updateCompatSysUiVisibility(int, boolean, boolean) 105 */ updateCompatSysUiVisibility(@nternalInsetsType int type, boolean visible, boolean hasControl)106 void updateCompatSysUiVisibility(@InternalInsetsType int type, boolean visible, 107 boolean hasControl); 108 109 /** 110 * Called when insets have been modified by the client and should be reported back to WM. 111 */ onInsetsModified(InsetsState insetsState)112 void onInsetsModified(InsetsState insetsState); 113 114 /** 115 * @return Whether the host has any callbacks it wants to synchronize the animations with. 116 * If there are no callbacks, the animation will be off-loaded to another thread and 117 * slightly different animation curves are picked. 118 */ hasAnimationCallbacks()119 boolean hasAnimationCallbacks(); 120 121 /** 122 * @see WindowInsetsController#setSystemBarsAppearance 123 */ setSystemBarsAppearance(@ppearance int appearance, @Appearance int mask)124 void setSystemBarsAppearance(@Appearance int appearance, @Appearance int mask); 125 126 /** 127 * @see WindowInsetsController#getSystemBarsAppearance() 128 */ getSystemBarsAppearance()129 @Appearance int getSystemBarsAppearance(); 130 isSystemBarsAppearanceControlled()131 default boolean isSystemBarsAppearanceControlled() { 132 return false; 133 } 134 135 /** 136 * @see WindowInsetsController#setSystemBarsBehavior 137 */ setSystemBarsBehavior(@ehavior int behavior)138 void setSystemBarsBehavior(@Behavior int behavior); 139 140 /** 141 * @see WindowInsetsController#getSystemBarsBehavior 142 */ getSystemBarsBehavior()143 @Behavior int getSystemBarsBehavior(); 144 isSystemBarsBehaviorControlled()145 default boolean isSystemBarsBehaviorControlled() { 146 return false; 147 } 148 149 /** 150 * Releases a surface and ensure that this is done after {@link #applySurfaceParams} has 151 * finished applying params. 152 */ releaseSurfaceControlFromRt(SurfaceControl surfaceControl)153 void releaseSurfaceControlFromRt(SurfaceControl surfaceControl); 154 155 /** 156 * If this host is a view hierarchy, adds a pre-draw runnable to ensure proper ordering as 157 * described in {@link WindowInsetsAnimation.Callback#onPrepare}. 158 * 159 * If this host isn't a view hierarchy, the runnable can be executed immediately. 160 */ addOnPreDrawRunnable(Runnable r)161 void addOnPreDrawRunnable(Runnable r); 162 163 /** 164 * Adds a runnbale to be executed during {@link Choreographer#CALLBACK_INSETS_ANIMATION} 165 * phase. 166 */ postInsetsAnimationCallback(Runnable r)167 void postInsetsAnimationCallback(Runnable r); 168 169 /** 170 * Obtains {@link InputMethodManager} instance from host. 171 */ getInputMethodManager()172 InputMethodManager getInputMethodManager(); 173 174 /** 175 * @return title of the rootView, if it has one. 176 * Note: this method is for debugging purposes only. 177 */ 178 @Nullable getRootViewTitle()179 String getRootViewTitle(); 180 181 /** @see ViewRootImpl#dipToPx */ dipToPx(int dips)182 int dipToPx(int dips); 183 184 /** 185 * @return token associated with the host, if it has one. 186 */ 187 @Nullable getWindowToken()188 IBinder getWindowToken(); 189 190 /** 191 * @return Translator associated with the host, if it has one. 192 */ 193 @Nullable getTranslator()194 default CompatibilityInfo.Translator getTranslator() { 195 return null; 196 } 197 } 198 199 private static final String TAG = "InsetsController"; 200 private static final int ANIMATION_DURATION_MOVE_IN_MS = 275; 201 private static final int ANIMATION_DURATION_MOVE_OUT_MS = 340; 202 private static final int ANIMATION_DURATION_FADE_IN_MS = 500; 203 private static final int ANIMATION_DURATION_FADE_OUT_MS = 1500; 204 205 private static final int ANIMATION_DELAY_DIM_MS = 500; 206 207 private static final int ANIMATION_DURATION_SYNC_IME_MS = 285; 208 private static final int ANIMATION_DURATION_UNSYNC_IME_MS = 200; 209 210 private static final int PENDING_CONTROL_TIMEOUT_MS = 2000; 211 212 private static final Interpolator SYSTEM_BARS_INSETS_INTERPOLATOR = 213 new PathInterpolator(0.4f, 0f, 0.2f, 1f); 214 private static final Interpolator SYSTEM_BARS_ALPHA_INTERPOLATOR = 215 new PathInterpolator(0.3f, 0f, 1f, 1f); 216 private static final Interpolator SYSTEM_BARS_DIM_INTERPOLATOR = alphaFraction -> { 217 // While playing dim animation, alphaFraction is changed from 1f to 0f. Here changes it to 218 // time-based fraction for computing delay and interpolation. 219 float fraction = 1 - alphaFraction; 220 final float fractionDelay = (float) ANIMATION_DELAY_DIM_MS / ANIMATION_DURATION_FADE_OUT_MS; 221 if (fraction <= fractionDelay) { 222 return 1f; 223 } else { 224 float innerFraction = (fraction - fractionDelay) / (1f - fractionDelay); 225 return 1f - SYSTEM_BARS_ALPHA_INTERPOLATOR.getInterpolation(innerFraction); 226 } 227 }; 228 private static final Interpolator SYNC_IME_INTERPOLATOR = 229 new PathInterpolator(0.2f, 0f, 0f, 1f); 230 private static final Interpolator LINEAR_OUT_SLOW_IN_INTERPOLATOR = 231 new PathInterpolator(0, 0, 0.2f, 1f); 232 private static final Interpolator FAST_OUT_LINEAR_IN_INTERPOLATOR = 233 new PathInterpolator(0.4f, 0f, 1f, 1f); 234 235 /** The amount IME will move up/down when animating in floating mode. */ 236 private static final int FLOATING_IME_BOTTOM_INSET_DP = -80; 237 238 static final boolean DEBUG = false; 239 static final boolean WARN = false; 240 241 /** 242 * Layout mode during insets animation: The views should be laid out as if the changing inset 243 * types are fully shown. Before starting the animation, {@link View#onApplyWindowInsets} will 244 * be called as if the changing insets types are shown, which will result in the views being 245 * laid out as if the insets are fully shown. 246 */ 247 public static final int LAYOUT_INSETS_DURING_ANIMATION_SHOWN = 0; 248 249 /** 250 * Layout mode during insets animation: The views should be laid out as if the changing inset 251 * types are fully hidden. Before starting the animation, {@link View#onApplyWindowInsets} will 252 * be called as if the changing insets types are hidden, which will result in the views being 253 * laid out as if the insets are fully hidden. 254 */ 255 public static final int LAYOUT_INSETS_DURING_ANIMATION_HIDDEN = 1; 256 257 /** 258 * Determines the behavior of how the views should be laid out during an insets animation that 259 * is controlled by the application by calling {@link #controlWindowInsetsAnimation}. 260 * <p> 261 * When the animation is system-initiated, the layout mode is always chosen such that the 262 * pre-animation layout will represent the opposite of the starting state, i.e. when insets 263 * are appearing, {@link #LAYOUT_INSETS_DURING_ANIMATION_SHOWN} will be used. When insets 264 * are disappearing, {@link #LAYOUT_INSETS_DURING_ANIMATION_HIDDEN} will be used. 265 */ 266 @Retention(RetentionPolicy.SOURCE) 267 @IntDef(value = {LAYOUT_INSETS_DURING_ANIMATION_SHOWN, 268 LAYOUT_INSETS_DURING_ANIMATION_HIDDEN}) 269 @interface LayoutInsetsDuringAnimation { 270 } 271 272 /** Not running an animation. */ 273 @VisibleForTesting 274 public static final int ANIMATION_TYPE_NONE = -1; 275 276 /** Running animation will show insets */ 277 @VisibleForTesting 278 public static final int ANIMATION_TYPE_SHOW = 0; 279 280 /** Running animation will hide insets */ 281 @VisibleForTesting 282 public static final int ANIMATION_TYPE_HIDE = 1; 283 284 /** Running animation is controlled by user via {@link #controlWindowInsetsAnimation} */ 285 @VisibleForTesting 286 public static final int ANIMATION_TYPE_USER = 2; 287 288 @Retention(RetentionPolicy.SOURCE) 289 @IntDef(value = {ANIMATION_TYPE_NONE, ANIMATION_TYPE_SHOW, ANIMATION_TYPE_HIDE, 290 ANIMATION_TYPE_USER}) 291 @interface AnimationType { 292 } 293 294 /** 295 * Translation animation evaluator. 296 */ 297 private static TypeEvaluator<Insets> sEvaluator = (fraction, startValue, endValue) -> Insets.of( 298 (int) (startValue.left + fraction * (endValue.left - startValue.left)), 299 (int) (startValue.top + fraction * (endValue.top - startValue.top)), 300 (int) (startValue.right + fraction * (endValue.right - startValue.right)), 301 (int) (startValue.bottom + fraction * (endValue.bottom - startValue.bottom))); 302 303 /** 304 * The default implementation of listener, to be used by InsetsController and InsetsPolicy to 305 * animate insets. 306 */ 307 public static class InternalAnimationControlListener 308 implements WindowInsetsAnimationControlListener { 309 310 private WindowInsetsAnimationController mController; 311 private ValueAnimator mAnimator; 312 private final boolean mShow; 313 private final boolean mHasAnimationCallbacks; 314 private final @InsetsType int mRequestedTypes; 315 private final @Behavior int mBehavior; 316 private final long mDurationMs; 317 private final boolean mDisable; 318 private final int mFloatingImeBottomInset; 319 320 private ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal = 321 new ThreadLocal<AnimationHandler>() { 322 @Override 323 protected AnimationHandler initialValue() { 324 AnimationHandler handler = new AnimationHandler(); 325 handler.setProvider(new SfVsyncFrameCallbackProvider()); 326 return handler; 327 } 328 }; 329 InternalAnimationControlListener(boolean show, boolean hasAnimationCallbacks, @InsetsType int requestedTypes, @Behavior int behavior, boolean disable, int floatingImeBottomInset)330 public InternalAnimationControlListener(boolean show, boolean hasAnimationCallbacks, 331 @InsetsType int requestedTypes, @Behavior int behavior, boolean disable, 332 int floatingImeBottomInset) { 333 mShow = show; 334 mHasAnimationCallbacks = hasAnimationCallbacks; 335 mRequestedTypes = requestedTypes; 336 mBehavior = behavior; 337 mDurationMs = calculateDurationMs(); 338 mDisable = disable; 339 mFloatingImeBottomInset = floatingImeBottomInset; 340 } 341 342 @Override onReady(WindowInsetsAnimationController controller, int types)343 public void onReady(WindowInsetsAnimationController controller, int types) { 344 mController = controller; 345 if (DEBUG) Log.d(TAG, "default animation onReady types: " + types); 346 347 if (mDisable) { 348 onAnimationFinish(); 349 return; 350 } 351 mAnimator = ValueAnimator.ofFloat(0f, 1f); 352 mAnimator.setDuration(mDurationMs); 353 mAnimator.setInterpolator(new LinearInterpolator()); 354 Insets hiddenInsets = controller.getHiddenStateInsets(); 355 // IME with zero insets is a special case: it will animate-in from offscreen and end 356 // with final insets of zero and vice-versa. 357 hiddenInsets = controller.hasZeroInsetsIme() 358 ? Insets.of(hiddenInsets.left, hiddenInsets.top, hiddenInsets.right, 359 mFloatingImeBottomInset) 360 : hiddenInsets; 361 Insets start = mShow 362 ? hiddenInsets 363 : controller.getShownStateInsets(); 364 Insets end = mShow 365 ? controller.getShownStateInsets() 366 : hiddenInsets; 367 Interpolator insetsInterpolator = getInsetsInterpolator(); 368 Interpolator alphaInterpolator = getAlphaInterpolator(); 369 mAnimator.addUpdateListener(animation -> { 370 float rawFraction = animation.getAnimatedFraction(); 371 float alphaFraction = mShow 372 ? rawFraction 373 : 1 - rawFraction; 374 float insetsFraction = insetsInterpolator.getInterpolation(rawFraction); 375 controller.setInsetsAndAlpha( 376 sEvaluator.evaluate(insetsFraction, start, end), 377 alphaInterpolator.getInterpolation(alphaFraction), 378 rawFraction); 379 if (DEBUG) Log.d(TAG, "Default animation setInsetsAndAlpha fraction: " 380 + insetsFraction); 381 }); 382 mAnimator.addListener(new AnimatorListenerAdapter() { 383 384 @Override 385 public void onAnimationEnd(Animator animation) { 386 onAnimationFinish(); 387 } 388 }); 389 if (!mHasAnimationCallbacks) { 390 mAnimator.setAnimationHandler(mSfAnimationHandlerThreadLocal.get()); 391 } 392 mAnimator.start(); 393 } 394 395 @Override onFinished(WindowInsetsAnimationController controller)396 public void onFinished(WindowInsetsAnimationController controller) { 397 if (DEBUG) Log.d(TAG, "InternalAnimationControlListener onFinished types:" 398 + Type.toString(mRequestedTypes)); 399 } 400 401 @Override onCancelled(WindowInsetsAnimationController controller)402 public void onCancelled(WindowInsetsAnimationController controller) { 403 // Animator can be null when it is cancelled before onReady() completes. 404 if (mAnimator != null) { 405 mAnimator.cancel(); 406 } 407 if (DEBUG) Log.d(TAG, "InternalAnimationControlListener onCancelled types:" 408 + mRequestedTypes); 409 } 410 getInsetsInterpolator()411 protected Interpolator getInsetsInterpolator() { 412 if ((mRequestedTypes & ime()) != 0) { 413 if (mHasAnimationCallbacks) { 414 return SYNC_IME_INTERPOLATOR; 415 } else if (mShow) { 416 return LINEAR_OUT_SLOW_IN_INTERPOLATOR; 417 } else { 418 return FAST_OUT_LINEAR_IN_INTERPOLATOR; 419 } 420 } else { 421 if (mBehavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE) { 422 return SYSTEM_BARS_INSETS_INTERPOLATOR; 423 } else { 424 // Makes insets stay at the shown position. 425 return input -> mShow ? 1f : 0f; 426 } 427 } 428 } 429 getAlphaInterpolator()430 Interpolator getAlphaInterpolator() { 431 if ((mRequestedTypes & ime()) != 0) { 432 if (mHasAnimationCallbacks) { 433 return input -> 1f; 434 } else if (mShow) { 435 436 // Alpha animation takes half the time with linear interpolation; 437 return input -> Math.min(1f, 2 * input); 438 } else { 439 return FAST_OUT_LINEAR_IN_INTERPOLATOR; 440 } 441 } else { 442 if (mBehavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE) { 443 return input -> 1f; 444 } else { 445 if (mShow) { 446 return SYSTEM_BARS_ALPHA_INTERPOLATOR; 447 } else { 448 return SYSTEM_BARS_DIM_INTERPOLATOR; 449 } 450 } 451 } 452 } 453 onAnimationFinish()454 protected void onAnimationFinish() { 455 mController.finish(mShow); 456 if (DEBUG) Log.d(TAG, "onAnimationFinish showOnFinish: " + mShow); 457 } 458 459 /** 460 * To get the animation duration in MS. 461 */ getDurationMs()462 public long getDurationMs() { 463 return mDurationMs; 464 } 465 calculateDurationMs()466 private long calculateDurationMs() { 467 if ((mRequestedTypes & ime()) != 0) { 468 if (mHasAnimationCallbacks) { 469 return ANIMATION_DURATION_SYNC_IME_MS; 470 } else { 471 return ANIMATION_DURATION_UNSYNC_IME_MS; 472 } 473 } else { 474 if (mBehavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE) { 475 return mShow ? ANIMATION_DURATION_MOVE_IN_MS : ANIMATION_DURATION_MOVE_OUT_MS; 476 } else { 477 return mShow ? ANIMATION_DURATION_FADE_IN_MS : ANIMATION_DURATION_FADE_OUT_MS; 478 } 479 } 480 } 481 } 482 483 /** 484 * Represents a running animation 485 */ 486 private static class RunningAnimation { 487 RunningAnimation(InsetsAnimationControlRunner runner, int type)488 RunningAnimation(InsetsAnimationControlRunner runner, int type) { 489 this.runner = runner; 490 this.type = type; 491 } 492 493 final InsetsAnimationControlRunner runner; 494 final @AnimationType int type; 495 496 /** 497 * Whether {@link WindowInsetsAnimation.Callback#onStart(WindowInsetsAnimation, Bounds)} has 498 * been dispatched already for this animation. 499 */ 500 boolean startDispatched; 501 } 502 503 /** 504 * Represents a control request that we had to defer because we are waiting for the IME to 505 * process our show request. 506 */ 507 private static class PendingControlRequest { 508 PendingControlRequest(@nsetsType int types, WindowInsetsAnimationControlListener listener, long durationMs, Interpolator interpolator, @AnimationType int animationType, @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation, CancellationSignal cancellationSignal, boolean useInsetsAnimationThread)509 PendingControlRequest(@InsetsType int types, WindowInsetsAnimationControlListener listener, 510 long durationMs, Interpolator interpolator, @AnimationType int animationType, 511 @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation, 512 CancellationSignal cancellationSignal, boolean useInsetsAnimationThread) { 513 this.types = types; 514 this.listener = listener; 515 this.durationMs = durationMs; 516 this.interpolator = interpolator; 517 this.animationType = animationType; 518 this.layoutInsetsDuringAnimation = layoutInsetsDuringAnimation; 519 this.cancellationSignal = cancellationSignal; 520 this.useInsetsAnimationThread = useInsetsAnimationThread; 521 } 522 523 final @InsetsType int types; 524 final WindowInsetsAnimationControlListener listener; 525 final long durationMs; 526 final Interpolator interpolator; 527 final @AnimationType int animationType; 528 final @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation; 529 final CancellationSignal cancellationSignal; 530 final boolean useInsetsAnimationThread; 531 } 532 533 /** The local state */ 534 private final InsetsState mState = new InsetsState(); 535 536 /** The state dispatched from server */ 537 private final InsetsState mLastDispatchedState = new InsetsState(); 538 539 // TODO: Use other class to represent the requested visibility of each type, because the 540 // display frame and the frame in each source are not used. 541 /** The requested visibilities sent to server */ 542 private final InsetsState mRequestedState = new InsetsState(); 543 544 private final Rect mFrame = new Rect(); 545 private final BiFunction<InsetsController, Integer, InsetsSourceConsumer> mConsumerCreator; 546 private final SparseArray<InsetsSourceConsumer> mSourceConsumers = new SparseArray<>(); 547 private final Host mHost; 548 private final Handler mHandler; 549 550 private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>(); 551 private final ArrayList<RunningAnimation> mRunningAnimations = new ArrayList<>(); 552 private final ArrayList<InsetsAnimationControlImpl> mTmpFinishedControls = new ArrayList<>(); 553 private final ArraySet<InsetsSourceConsumer> mRequestedVisibilityChanged = new ArraySet<>(); 554 private WindowInsets mLastInsets; 555 556 private boolean mAnimCallbackScheduled; 557 558 private final Runnable mAnimCallback; 559 560 /** Pending control request that is waiting on IME to be ready to be shown */ 561 private PendingControlRequest mPendingImeControlRequest; 562 563 private int mWindowType; 564 private int mLastLegacySoftInputMode; 565 private int mLastLegacyWindowFlags; 566 private int mLastLegacySystemUiFlags; 567 private int mLastWindowingMode; 568 private boolean mStartingAnimation; 569 private int mCaptionInsetsHeight = 0; 570 private boolean mAnimationsDisabled; 571 572 private Runnable mPendingControlTimeout = this::abortPendingImeControlRequest; 573 private final ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners 574 = new ArrayList<>(); 575 576 /** Set of inset types for which an animation was started since last resetting this field */ 577 private @InsetsType int mLastStartedAnimTypes; 578 579 /** Set of inset types which cannot be controlled by the user animation */ 580 private @InsetsType int mDisabledUserAnimationInsetsTypes; 581 582 private Runnable mInvokeControllableInsetsChangedListeners = 583 this::invokeControllableInsetsChangedListeners; 584 InsetsController(Host host)585 public InsetsController(Host host) { 586 this(host, (controller, type) -> { 587 if (type == ITYPE_IME) { 588 return new ImeInsetsSourceConsumer(controller.mState, Transaction::new, controller); 589 } else { 590 return new InsetsSourceConsumer(type, controller.mState, Transaction::new, 591 controller); 592 } 593 }, host.getHandler()); 594 } 595 596 @VisibleForTesting InsetsController(Host host, BiFunction<InsetsController, Integer, InsetsSourceConsumer> consumerCreator, Handler handler)597 public InsetsController(Host host, 598 BiFunction<InsetsController, Integer, InsetsSourceConsumer> consumerCreator, 599 Handler handler) { 600 mHost = host; 601 mConsumerCreator = consumerCreator; 602 mHandler = handler; 603 mAnimCallback = () -> { 604 mAnimCallbackScheduled = false; 605 if (mRunningAnimations.isEmpty()) { 606 return; 607 } 608 609 final List<WindowInsetsAnimation> runningAnimations = new ArrayList<>(); 610 final InsetsState state = new InsetsState(mState, true /* copySources */); 611 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { 612 RunningAnimation runningAnimation = mRunningAnimations.get(i); 613 if (DEBUG) Log.d(TAG, "Running animation type: " + runningAnimation.type); 614 InsetsAnimationControlRunner runner = runningAnimation.runner; 615 if (runner instanceof InsetsAnimationControlImpl) { 616 InsetsAnimationControlImpl control = (InsetsAnimationControlImpl) runner; 617 618 // Keep track of running animation to be dispatched. Aggregate it here such that 619 // if it gets finished within applyChangeInsets we still dispatch it to 620 // onProgress. 621 if (runningAnimation.startDispatched) { 622 runningAnimations.add(control.getAnimation()); 623 } 624 625 if (control.applyChangeInsets(state)) { 626 mTmpFinishedControls.add(control); 627 } 628 } 629 } 630 631 WindowInsets insets = state.calculateInsets(mFrame, mState /* ignoringVisibilityState*/, 632 mLastInsets.isRound(), mLastInsets.shouldAlwaysConsumeSystemBars(), 633 mLastLegacySoftInputMode, mLastLegacyWindowFlags, mLastLegacySystemUiFlags, 634 mWindowType, mLastWindowingMode, null /* typeSideMap */); 635 mHost.dispatchWindowInsetsAnimationProgress(insets, 636 Collections.unmodifiableList(runningAnimations)); 637 if (DEBUG) { 638 for (WindowInsetsAnimation anim : runningAnimations) { 639 Log.d(TAG, String.format("Running animation type: %d, progress: %f", 640 anim.getTypeMask(), anim.getInterpolatedFraction())); 641 } 642 } 643 644 for (int i = mTmpFinishedControls.size() - 1; i >= 0; i--) { 645 dispatchAnimationEnd(mTmpFinishedControls.get(i).getAnimation()); 646 } 647 mTmpFinishedControls.clear(); 648 }; 649 } 650 651 @VisibleForTesting onFrameChanged(Rect frame)652 public void onFrameChanged(Rect frame) { 653 if (mFrame.equals(frame)) { 654 return; 655 } 656 mHost.notifyInsetsChanged(); 657 mFrame.set(frame); 658 } 659 660 @Override getState()661 public InsetsState getState() { 662 return mState; 663 } 664 665 @Override isRequestedVisible(int type)666 public boolean isRequestedVisible(int type) { 667 return getSourceConsumer(type).isRequestedVisible(); 668 } 669 getLastDispatchedState()670 public InsetsState getLastDispatchedState() { 671 return mLastDispatchedState; 672 } 673 674 @VisibleForTesting onStateChanged(InsetsState state)675 public boolean onStateChanged(InsetsState state) { 676 boolean stateChanged = !mState.equals(state, true /* excludingCaptionInsets */, 677 false /* excludeInvisibleIme */) 678 || !captionInsetsUnchanged(); 679 if (!stateChanged && mLastDispatchedState.equals(state)) { 680 return false; 681 } 682 if (DEBUG) Log.d(TAG, "onStateChanged: " + state); 683 mLastDispatchedState.set(state, true /* copySources */); 684 685 final InsetsState lastState = new InsetsState(mState, true /* copySources */); 686 updateState(state); 687 applyLocalVisibilityOverride(); 688 689 if (!mState.equals(lastState, false /* excludingCaptionInsets */, 690 true /* excludeInvisibleIme */)) { 691 if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged"); 692 mHost.notifyInsetsChanged(); 693 } 694 return true; 695 } 696 updateState(InsetsState newState)697 private void updateState(InsetsState newState) { 698 mState.setDisplayFrame(newState.getDisplayFrame()); 699 mState.setDisplayCutout(newState.getDisplayCutout()); 700 mState.setRoundedCorners(newState.getRoundedCorners()); 701 mState.setPrivacyIndicatorBounds(newState.getPrivacyIndicatorBounds()); 702 @InsetsType int disabledUserAnimationTypes = 0; 703 @InsetsType int[] cancelledUserAnimationTypes = {0}; 704 for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) { 705 InsetsSource source = newState.peekSource(type); 706 if (source == null) continue; 707 @AnimationType int animationType = getAnimationType(type); 708 if (!source.isUserControllable()) { 709 @InsetsType int insetsType = toPublicType(type); 710 // The user animation is not allowed when visible frame is empty. 711 disabledUserAnimationTypes |= insetsType; 712 if (animationType == ANIMATION_TYPE_USER) { 713 // Existing user animation needs to be cancelled. 714 animationType = ANIMATION_TYPE_NONE; 715 cancelledUserAnimationTypes[0] |= insetsType; 716 } 717 } 718 getSourceConsumer(type).updateSource(source, animationType); 719 } 720 for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) { 721 // Only update the server side insets here. 722 if (type == ITYPE_CAPTION_BAR) continue; 723 InsetsSource source = mState.peekSource(type); 724 if (source == null) continue; 725 if (newState.peekSource(type) == null) { 726 mState.removeSource(type); 727 } 728 } 729 730 updateDisabledUserAnimationTypes(disabledUserAnimationTypes); 731 732 if (cancelledUserAnimationTypes[0] != 0) { 733 mHandler.post(() -> show(cancelledUserAnimationTypes[0])); 734 } 735 } 736 updateDisabledUserAnimationTypes(@nsetsType int disabledUserAnimationTypes)737 private void updateDisabledUserAnimationTypes(@InsetsType int disabledUserAnimationTypes) { 738 @InsetsType int diff = mDisabledUserAnimationInsetsTypes ^ disabledUserAnimationTypes; 739 if (diff != 0) { 740 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { 741 InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); 742 if (consumer.getControl() != null 743 && (toPublicType(consumer.getType()) & diff) != 0) { 744 mHandler.removeCallbacks(mInvokeControllableInsetsChangedListeners); 745 mHandler.post(mInvokeControllableInsetsChangedListeners); 746 break; 747 } 748 } 749 mDisabledUserAnimationInsetsTypes = disabledUserAnimationTypes; 750 } 751 } 752 captionInsetsUnchanged()753 private boolean captionInsetsUnchanged() { 754 if (mState.peekSource(ITYPE_CAPTION_BAR) == null 755 && mCaptionInsetsHeight == 0) { 756 return true; 757 } 758 if (mState.peekSource(ITYPE_CAPTION_BAR) != null 759 && mCaptionInsetsHeight 760 == mState.peekSource(ITYPE_CAPTION_BAR).getFrame().height()) { 761 return true; 762 } 763 return false; 764 } 765 766 /** 767 * @see InsetsState#calculateInsets 768 */ 769 @VisibleForTesting calculateInsets(boolean isScreenRound, boolean alwaysConsumeSystemBars, int windowType, int windowingMode, int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags)770 public WindowInsets calculateInsets(boolean isScreenRound, boolean alwaysConsumeSystemBars, 771 int windowType, int windowingMode, int legacySoftInputMode, int legacyWindowFlags, 772 int legacySystemUiFlags) { 773 mWindowType = windowType; 774 mLastWindowingMode = windowingMode; 775 mLastLegacySoftInputMode = legacySoftInputMode; 776 mLastLegacyWindowFlags = legacyWindowFlags; 777 mLastLegacySystemUiFlags = legacySystemUiFlags; 778 mLastInsets = mState.calculateInsets(mFrame, null /* ignoringVisibilityState*/, 779 isScreenRound, alwaysConsumeSystemBars, legacySoftInputMode, legacyWindowFlags, 780 legacySystemUiFlags, windowType, windowingMode, null /* typeSideMap */); 781 return mLastInsets; 782 } 783 784 /** 785 * @see InsetsState#calculateVisibleInsets(Rect, int) 786 */ calculateVisibleInsets(@oftInputModeFlags int softInputMode)787 public Rect calculateVisibleInsets(@SoftInputModeFlags int softInputMode) { 788 return mState.calculateVisibleInsets(mFrame, softInputMode); 789 } 790 791 /** 792 * Called when the server has dispatched us a new set of inset controls. 793 */ onControlsChanged(InsetsSourceControl[] activeControls)794 public void onControlsChanged(InsetsSourceControl[] activeControls) { 795 if (activeControls != null) { 796 for (InsetsSourceControl activeControl : activeControls) { 797 if (activeControl != null) { 798 // TODO(b/122982984): Figure out why it can be null. 799 mTmpControlArray.put(activeControl.getType(), activeControl); 800 } 801 } 802 } 803 804 boolean requestedStateStale = false; 805 final int[] showTypes = new int[1]; 806 final int[] hideTypes = new int[1]; 807 808 // Ensure to update all existing source consumers 809 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { 810 final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); 811 final InsetsSourceControl control = mTmpControlArray.get(consumer.getType()); 812 813 // control may be null, but we still need to update the control to null if it got 814 // revoked. 815 consumer.setControl(control, showTypes, hideTypes); 816 } 817 818 // Ensure to create source consumers if not available yet. 819 for (int i = mTmpControlArray.size() - 1; i >= 0; i--) { 820 final InsetsSourceControl control = mTmpControlArray.valueAt(i); 821 final @InternalInsetsType int type = control.getType(); 822 final InsetsSourceConsumer consumer = getSourceConsumer(type); 823 consumer.setControl(control, showTypes, hideTypes); 824 825 if (!requestedStateStale) { 826 final boolean requestedVisible = consumer.isRequestedVisible(); 827 828 // We might have changed our requested visibilities while we don't have the control, 829 // so we need to update our requested state once we have control. Otherwise, our 830 // requested state at the server side might be incorrect. 831 final boolean requestedVisibilityChanged = 832 requestedVisible != mRequestedState.getSourceOrDefaultVisibility(type); 833 834 // The IME client visibility will be reset by insets source provider while updating 835 // control, so if IME is requested visible, we need to send the request to server. 836 final boolean imeRequestedVisible = type == ITYPE_IME && requestedVisible; 837 838 requestedStateStale = requestedVisibilityChanged || imeRequestedVisible; 839 } 840 } 841 842 if (mTmpControlArray.size() > 0) { 843 // Update surface positions for animations. 844 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { 845 mRunningAnimations.get(i).runner.updateSurfacePosition(mTmpControlArray); 846 } 847 } 848 mTmpControlArray.clear(); 849 850 // Do not override any animations that the app started in the OnControllableInsetsChanged 851 // listeners. 852 int animatingTypes = invokeControllableInsetsChangedListeners(); 853 showTypes[0] &= ~animatingTypes; 854 hideTypes[0] &= ~animatingTypes; 855 856 if (showTypes[0] != 0) { 857 applyAnimation(showTypes[0], true /* show */, false /* fromIme */); 858 } 859 if (hideTypes[0] != 0) { 860 applyAnimation(hideTypes[0], false /* show */, false /* fromIme */); 861 } 862 863 // InsetsSourceConsumer#setControl might change the requested visibility. 864 updateRequestedVisibility(); 865 } 866 867 @Override show(@nsetsType int types)868 public void show(@InsetsType int types) { 869 show(types, false /* fromIme */); 870 } 871 872 @VisibleForTesting show(@nsetsType int types, boolean fromIme)873 public void show(@InsetsType int types, boolean fromIme) { 874 if ((types & ime()) != 0) { 875 Log.d(TAG, "show(ime(), fromIme=" + fromIme + ")"); 876 } 877 if (fromIme) { 878 ImeTracing.getInstance().triggerClientDump("InsetsController#show", 879 mHost.getInputMethodManager(), null /* icProto */); 880 Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApiToImeReady", 0); 881 Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.showRequestFromIme", 0); 882 } else { 883 Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0); 884 Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.showRequestFromApiToImeReady", 0); 885 } 886 // Handle pending request ready in case there was one set. 887 if (fromIme && mPendingImeControlRequest != null) { 888 PendingControlRequest pendingRequest = mPendingImeControlRequest; 889 mPendingImeControlRequest = null; 890 mHandler.removeCallbacks(mPendingControlTimeout); 891 892 // We are about to playing the default animation. Passing a null frame indicates the 893 // controlled types should be animated regardless of the frame. 894 controlAnimationUnchecked( 895 pendingRequest.types, pendingRequest.cancellationSignal, 896 pendingRequest.listener, null /* frame */, 897 true /* fromIme */, pendingRequest.durationMs, pendingRequest.interpolator, 898 pendingRequest.animationType, 899 pendingRequest.layoutInsetsDuringAnimation, 900 pendingRequest.useInsetsAnimationThread); 901 return; 902 } 903 904 // TODO: Support a ResultReceiver for IME. 905 // TODO(b/123718661): Make show() work for multi-session IME. 906 int typesReady = 0; 907 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types); 908 for (int i = internalTypes.size() - 1; i >= 0; i--) { 909 @InternalInsetsType int internalType = internalTypes.valueAt(i); 910 @AnimationType int animationType = getAnimationType(internalType); 911 InsetsSourceConsumer consumer = getSourceConsumer(internalType); 912 if (consumer.isRequestedVisible() && animationType == ANIMATION_TYPE_NONE 913 || animationType == ANIMATION_TYPE_SHOW) { 914 // no-op: already shown or animating in (because window visibility is 915 // applied before starting animation). 916 if (DEBUG) Log.d(TAG, String.format( 917 "show ignored for type: %d animType: %d requestedVisible: %s", 918 consumer.getType(), animationType, consumer.isRequestedVisible())); 919 continue; 920 } 921 if (fromIme && animationType == ANIMATION_TYPE_USER) { 922 // App is already controlling the IME, don't cancel it. 923 continue; 924 } 925 typesReady |= InsetsState.toPublicType(consumer.getType()); 926 } 927 if (DEBUG) Log.d(TAG, "show typesReady: " + typesReady); 928 applyAnimation(typesReady, true /* show */, fromIme); 929 } 930 931 @Override hide(@nsetsType int types)932 public void hide(@InsetsType int types) { 933 hide(types, false /* fromIme */); 934 } 935 936 @VisibleForTesting hide(@nsetsType int types, boolean fromIme)937 public void hide(@InsetsType int types, boolean fromIme) { 938 if (fromIme) { 939 ImeTracing.getInstance().triggerClientDump("InsetsController#hide", 940 mHost.getInputMethodManager(), null /* icProto */); 941 Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.hideRequestFromIme", 0); 942 } else { 943 Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.hideRequestFromApi", 0); 944 } 945 int typesReady = 0; 946 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types); 947 for (int i = internalTypes.size() - 1; i >= 0; i--) { 948 @InternalInsetsType int internalType = internalTypes.valueAt(i); 949 @AnimationType int animationType = getAnimationType(internalType); 950 InsetsSourceConsumer consumer = getSourceConsumer(internalType); 951 if (!consumer.isRequestedVisible() && animationType == ANIMATION_TYPE_NONE 952 || animationType == ANIMATION_TYPE_HIDE) { 953 // no-op: already hidden or animating out. 954 continue; 955 } 956 typesReady |= InsetsState.toPublicType(consumer.getType()); 957 } 958 applyAnimation(typesReady, false /* show */, fromIme /* fromIme */); 959 } 960 961 @Override controlWindowInsetsAnimation(@nsetsType int types, long durationMillis, @Nullable Interpolator interpolator, @Nullable CancellationSignal cancellationSignal, @NonNull WindowInsetsAnimationControlListener listener)962 public void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis, 963 @Nullable Interpolator interpolator, 964 @Nullable CancellationSignal cancellationSignal, 965 @NonNull WindowInsetsAnimationControlListener listener) { 966 controlWindowInsetsAnimation(types, cancellationSignal, listener, 967 false /* fromIme */, durationMillis, interpolator, ANIMATION_TYPE_USER); 968 } 969 controlWindowInsetsAnimation(@nsetsType int types, @Nullable CancellationSignal cancellationSignal, WindowInsetsAnimationControlListener listener, boolean fromIme, long durationMs, @Nullable Interpolator interpolator, @AnimationType int animationType)970 private void controlWindowInsetsAnimation(@InsetsType int types, 971 @Nullable CancellationSignal cancellationSignal, 972 WindowInsetsAnimationControlListener listener, 973 boolean fromIme, long durationMs, @Nullable Interpolator interpolator, 974 @AnimationType int animationType) { 975 if ((mState.calculateUncontrollableInsetsFromFrame(mFrame) & types) != 0) { 976 listener.onCancelled(null); 977 return; 978 } 979 if (fromIme) { 980 ImeTracing.getInstance().triggerClientDump( 981 "InsetsController#controlWindowInsetsAnimation", 982 mHost.getInputMethodManager(), null /* icProto */); 983 } 984 985 controlAnimationUnchecked(types, cancellationSignal, listener, mFrame, fromIme, durationMs, 986 interpolator, animationType, getLayoutInsetsDuringAnimationMode(types), 987 false /* useInsetsAnimationThread */); 988 } 989 controlAnimationUnchecked(@nsetsType int types, @Nullable CancellationSignal cancellationSignal, WindowInsetsAnimationControlListener listener, @Nullable Rect frame, boolean fromIme, long durationMs, Interpolator interpolator, @AnimationType int animationType, @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation, boolean useInsetsAnimationThread)990 private void controlAnimationUnchecked(@InsetsType int types, 991 @Nullable CancellationSignal cancellationSignal, 992 WindowInsetsAnimationControlListener listener, @Nullable Rect frame, boolean fromIme, 993 long durationMs, Interpolator interpolator, 994 @AnimationType int animationType, 995 @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation, 996 boolean useInsetsAnimationThread) { 997 if ((types & mTypesBeingCancelled) != 0) { 998 throw new IllegalStateException("Cannot start a new insets animation of " 999 + Type.toString(types) 1000 + " while an existing " + Type.toString(mTypesBeingCancelled) 1001 + " is being cancelled."); 1002 } 1003 if (animationType == ANIMATION_TYPE_USER) { 1004 final @InsetsType int disabledTypes = types & mDisabledUserAnimationInsetsTypes; 1005 if (DEBUG) Log.d(TAG, "user animation disabled types: " + disabledTypes); 1006 types &= ~mDisabledUserAnimationInsetsTypes; 1007 1008 if (fromIme && (disabledTypes & ime()) != 0 1009 && !mState.getSource(ITYPE_IME).isVisible()) { 1010 // We've requested IMM to show IME, but the IME is not controllable. We need to 1011 // cancel the request. 1012 getSourceConsumer(ITYPE_IME).hide(true, animationType); 1013 } 1014 } 1015 if (types == 0) { 1016 // nothing to animate. 1017 listener.onCancelled(null); 1018 updateRequestedVisibility(); 1019 if (DEBUG) Log.d(TAG, "no types to animate in controlAnimationUnchecked"); 1020 return; 1021 } 1022 cancelExistingControllers(types); 1023 if (DEBUG) Log.d(TAG, "controlAnimation types: " + types); 1024 mLastStartedAnimTypes |= types; 1025 1026 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types); 1027 final SparseArray<InsetsSourceControl> controls = new SparseArray<>(); 1028 1029 Pair<Integer, Boolean> typesReadyPair = collectSourceControls( 1030 fromIme, internalTypes, controls, animationType); 1031 int typesReady = typesReadyPair.first; 1032 boolean imeReady = typesReadyPair.second; 1033 if (DEBUG) Log.d(TAG, String.format( 1034 "controlAnimationUnchecked, typesReady: %s imeReady: %s", typesReady, imeReady)); 1035 if (!imeReady) { 1036 // IME isn't ready, all requested types will be animated once IME is ready 1037 abortPendingImeControlRequest(); 1038 final PendingControlRequest request = new PendingControlRequest(types, 1039 listener, durationMs, 1040 interpolator, animationType, layoutInsetsDuringAnimation, cancellationSignal, 1041 useInsetsAnimationThread); 1042 mPendingImeControlRequest = request; 1043 mHandler.postDelayed(mPendingControlTimeout, PENDING_CONTROL_TIMEOUT_MS); 1044 if (DEBUG) Log.d(TAG, "Ime not ready. Create pending request"); 1045 if (cancellationSignal != null) { 1046 cancellationSignal.setOnCancelListener(() -> { 1047 if (mPendingImeControlRequest == request) { 1048 if (DEBUG) Log.d(TAG, 1049 "Cancellation signal abortPendingImeControlRequest"); 1050 abortPendingImeControlRequest(); 1051 } 1052 }); 1053 } 1054 updateRequestedVisibility(); 1055 Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0); 1056 return; 1057 } 1058 1059 if (typesReady == 0) { 1060 if (DEBUG) Log.d(TAG, "No types ready. onCancelled()"); 1061 listener.onCancelled(null); 1062 updateRequestedVisibility(); 1063 return; 1064 } 1065 1066 1067 final InsetsAnimationControlRunner runner = useInsetsAnimationThread 1068 ? new InsetsAnimationThreadControlRunner(controls, 1069 frame, mState, listener, typesReady, this, durationMs, interpolator, 1070 animationType, layoutInsetsDuringAnimation, mHost.getTranslator(), 1071 mHost.getHandler()) 1072 : new InsetsAnimationControlImpl(controls, 1073 frame, mState, listener, typesReady, this, durationMs, interpolator, 1074 animationType, layoutInsetsDuringAnimation, mHost.getTranslator()); 1075 if ((typesReady & WindowInsets.Type.ime()) != 0) { 1076 ImeTracing.getInstance().triggerClientDump("InsetsAnimationControlImpl", 1077 mHost.getInputMethodManager(), null /* icProto */); 1078 } 1079 mRunningAnimations.add(new RunningAnimation(runner, animationType)); 1080 if (DEBUG) Log.d(TAG, "Animation added to runner. useInsetsAnimationThread: " 1081 + useInsetsAnimationThread); 1082 if (cancellationSignal != null) { 1083 cancellationSignal.setOnCancelListener(() -> { 1084 cancelAnimation(runner, true /* invokeCallback */); 1085 }); 1086 } else { 1087 Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.pendingAnim", 0); 1088 } 1089 if (layoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN) { 1090 showDirectly(types, fromIme); 1091 } else { 1092 hideDirectly(types, false /* animationFinished */, animationType, fromIme); 1093 } 1094 updateRequestedVisibility(); 1095 } 1096 1097 /** 1098 * @return Pair of (types ready to animate, IME ready to animate). 1099 */ collectSourceControls(boolean fromIme, ArraySet<Integer> internalTypes, SparseArray<InsetsSourceControl> controls, @AnimationType int animationType)1100 private Pair<Integer, Boolean> collectSourceControls(boolean fromIme, 1101 ArraySet<Integer> internalTypes, SparseArray<InsetsSourceControl> controls, 1102 @AnimationType int animationType) { 1103 int typesReady = 0; 1104 boolean imeReady = true; 1105 for (int i = internalTypes.size() - 1; i >= 0; i--) { 1106 final InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i)); 1107 boolean show = animationType == ANIMATION_TYPE_SHOW 1108 || animationType == ANIMATION_TYPE_USER; 1109 boolean canRun = false; 1110 if (show) { 1111 // Show request 1112 if (fromIme) { 1113 ImeTracing.getInstance().triggerClientDump( 1114 "ImeInsetsSourceConsumer#requestShow", mHost.getInputMethodManager(), 1115 null /* icProto */); 1116 } 1117 switch(consumer.requestShow(fromIme)) { 1118 case ShowResult.SHOW_IMMEDIATELY: 1119 canRun = true; 1120 break; 1121 case ShowResult.IME_SHOW_DELAYED: 1122 imeReady = false; 1123 if (DEBUG) Log.d(TAG, "requestShow IME_SHOW_DELAYED"); 1124 break; 1125 case ShowResult.IME_SHOW_FAILED: 1126 if (WARN) Log.w(TAG, "requestShow IME_SHOW_FAILED. fromIme: " 1127 + fromIme); 1128 // IME cannot be shown (since it didn't have focus), proceed 1129 // with animation of other types. 1130 break; 1131 } 1132 } else { 1133 // Hide request 1134 // TODO: Move notifyHidden() to beginning of the hide animation 1135 // (when visibility actually changes using hideDirectly()). 1136 if (!fromIme) { 1137 consumer.notifyHidden(); 1138 } 1139 canRun = true; 1140 } 1141 if (!canRun) { 1142 if (WARN) Log.w(TAG, String.format( 1143 "collectSourceControls can't continue show for type: %s fromIme: %b", 1144 InsetsState.typeToString(consumer.getType()), fromIme)); 1145 continue; 1146 } 1147 final InsetsSourceControl control = consumer.getControl(); 1148 if (control != null && control.getLeash() != null) { 1149 controls.put(consumer.getType(), new InsetsSourceControl(control)); 1150 typesReady |= toPublicType(consumer.getType()); 1151 } else if (animationType == ANIMATION_TYPE_SHOW) { 1152 if (DEBUG) Log.d(TAG, "collectSourceControls no control for show(). fromIme: " 1153 + fromIme); 1154 // We don't have a control at the moment. However, we still want to update requested 1155 // visibility state such that in case we get control, we can apply show animation. 1156 if (fromIme) { 1157 ImeTracing.getInstance().triggerClientDump( 1158 "InsetsSourceConsumer#show", mHost.getInputMethodManager(), 1159 null /* icProto */); 1160 } 1161 consumer.show(fromIme); 1162 } else if (animationType == ANIMATION_TYPE_HIDE) { 1163 if (fromIme) { 1164 ImeTracing.getInstance().triggerClientDump( 1165 "InsetsSourceConsumer#hide", mHost.getInputMethodManager(), 1166 null /* icProto */); 1167 } 1168 consumer.hide(); 1169 } 1170 } 1171 return new Pair<>(typesReady, imeReady); 1172 } 1173 getLayoutInsetsDuringAnimationMode( @nsetsType int types)1174 private @LayoutInsetsDuringAnimation int getLayoutInsetsDuringAnimationMode( 1175 @InsetsType int types) { 1176 1177 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types); 1178 1179 // Generally, we want to layout the opposite of the current state. This is to make animation 1180 // callbacks easy to use: The can capture the layout values and then treat that as end-state 1181 // during the animation. 1182 // 1183 // However, if controlling multiple sources, we want to treat it as shown if any of the 1184 // types is currently hidden. 1185 for (int i = internalTypes.size() - 1; i >= 0; i--) { 1186 InsetsSourceConsumer consumer = mSourceConsumers.get(internalTypes.valueAt(i)); 1187 if (consumer == null) { 1188 continue; 1189 } 1190 if (!consumer.isRequestedVisible()) { 1191 return LAYOUT_INSETS_DURING_ANIMATION_SHOWN; 1192 } 1193 } 1194 return LAYOUT_INSETS_DURING_ANIMATION_HIDDEN; 1195 } 1196 cancelExistingControllers(@nsetsType int types)1197 private void cancelExistingControllers(@InsetsType int types) { 1198 final int originalmTypesBeingCancelled = mTypesBeingCancelled; 1199 mTypesBeingCancelled |= types; 1200 try { 1201 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { 1202 InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner; 1203 if ((control.getTypes() & types) != 0) { 1204 cancelAnimation(control, true /* invokeCallback */); 1205 } 1206 } 1207 if ((types & ime()) != 0) { 1208 abortPendingImeControlRequest(); 1209 } 1210 } finally { 1211 mTypesBeingCancelled = originalmTypesBeingCancelled; 1212 } 1213 } 1214 abortPendingImeControlRequest()1215 private void abortPendingImeControlRequest() { 1216 if (mPendingImeControlRequest != null) { 1217 mPendingImeControlRequest.listener.onCancelled(null); 1218 mPendingImeControlRequest = null; 1219 mHandler.removeCallbacks(mPendingControlTimeout); 1220 if (DEBUG) Log.d(TAG, "abortPendingImeControlRequest"); 1221 } 1222 } 1223 1224 @VisibleForTesting 1225 @Override notifyFinished(InsetsAnimationControlRunner runner, boolean shown)1226 public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) { 1227 cancelAnimation(runner, false /* invokeCallback */); 1228 if (DEBUG) Log.d(TAG, "notifyFinished. shown: " + shown); 1229 if (shown) { 1230 showDirectly(runner.getTypes(), true /* fromIme */); 1231 } else { 1232 hideDirectly(runner.getTypes(), true /* animationFinished */, 1233 runner.getAnimationType(), true /* fromIme */); 1234 } 1235 } 1236 1237 @Override applySurfaceParams(final SyncRtSurfaceTransactionApplier.SurfaceParams... params)1238 public void applySurfaceParams(final SyncRtSurfaceTransactionApplier.SurfaceParams... params) { 1239 mHost.applySurfaceParams(params); 1240 } 1241 notifyControlRevoked(InsetsSourceConsumer consumer)1242 void notifyControlRevoked(InsetsSourceConsumer consumer) { 1243 final @InsetsType int types = toPublicType(consumer.getType()); 1244 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { 1245 InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner; 1246 control.notifyControlRevoked(types); 1247 if (control.getControllingTypes() == 0) { 1248 cancelAnimation(control, true /* invokeCallback */); 1249 } 1250 } 1251 if (consumer.getType() == ITYPE_IME) { 1252 abortPendingImeControlRequest(); 1253 } 1254 } 1255 cancelAnimation(InsetsAnimationControlRunner control, boolean invokeCallback)1256 private void cancelAnimation(InsetsAnimationControlRunner control, boolean invokeCallback) { 1257 if (DEBUG) Log.d(TAG, String.format("cancelAnimation of types: %d, animType: %d", 1258 control.getTypes(), control.getAnimationType())); 1259 if (invokeCallback) { 1260 control.cancel(); 1261 } 1262 boolean stateChanged = false; 1263 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { 1264 RunningAnimation runningAnimation = mRunningAnimations.get(i); 1265 if (runningAnimation.runner == control) { 1266 mRunningAnimations.remove(i); 1267 ArraySet<Integer> types = toInternalType(control.getTypes()); 1268 for (int j = types.size() - 1; j >= 0; j--) { 1269 if (types.valueAt(j) == ITYPE_IME) { 1270 ImeTracing.getInstance().triggerClientDump( 1271 "InsetsSourceConsumer#notifyAnimationFinished", 1272 mHost.getInputMethodManager(), null /* icProto */); 1273 } 1274 stateChanged |= getSourceConsumer(types.valueAt(j)).notifyAnimationFinished(); 1275 } 1276 if (invokeCallback) { 1277 dispatchAnimationEnd(runningAnimation.runner.getAnimation()); 1278 } 1279 break; 1280 } 1281 } 1282 if (stateChanged) { 1283 mHost.notifyInsetsChanged(); 1284 } 1285 } 1286 applyLocalVisibilityOverride()1287 private void applyLocalVisibilityOverride() { 1288 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { 1289 final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); 1290 consumer.applyLocalVisibilityOverride(); 1291 } 1292 } 1293 1294 @VisibleForTesting getSourceConsumer(@nternalInsetsType int type)1295 public @NonNull InsetsSourceConsumer getSourceConsumer(@InternalInsetsType int type) { 1296 InsetsSourceConsumer controller = mSourceConsumers.get(type); 1297 if (controller != null) { 1298 return controller; 1299 } 1300 controller = mConsumerCreator.apply(this, type); 1301 mSourceConsumers.put(type, controller); 1302 return controller; 1303 } 1304 1305 @VisibleForTesting notifyVisibilityChanged()1306 public void notifyVisibilityChanged() { 1307 mHost.notifyInsetsChanged(); 1308 } 1309 1310 /** 1311 * @see ViewRootImpl#updateCompatSysUiVisibility(int, boolean, boolean) 1312 */ updateCompatSysUiVisibility(@nternalInsetsType int type, boolean visible, boolean hasControl)1313 public void updateCompatSysUiVisibility(@InternalInsetsType int type, boolean visible, 1314 boolean hasControl) { 1315 mHost.updateCompatSysUiVisibility(type, visible, hasControl); 1316 } 1317 1318 /** 1319 * Called when current window gains focus. 1320 */ onWindowFocusGained(boolean hasViewFocused)1321 public void onWindowFocusGained(boolean hasViewFocused) { 1322 getSourceConsumer(ITYPE_IME).onWindowFocusGained(hasViewFocused); 1323 } 1324 1325 /** 1326 * Called when current window loses focus. 1327 */ onWindowFocusLost()1328 public void onWindowFocusLost() { 1329 getSourceConsumer(ITYPE_IME).onWindowFocusLost(); 1330 } 1331 1332 @VisibleForTesting getAnimationType(@nternalInsetsType int type)1333 public @AnimationType int getAnimationType(@InternalInsetsType int type) { 1334 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { 1335 InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner; 1336 if (control.controlsInternalType(type)) { 1337 return mRunningAnimations.get(i).type; 1338 } 1339 } 1340 return ANIMATION_TYPE_NONE; 1341 } 1342 1343 @VisibleForTesting onRequestedVisibilityChanged(InsetsSourceConsumer consumer)1344 public void onRequestedVisibilityChanged(InsetsSourceConsumer consumer) { 1345 mRequestedVisibilityChanged.add(consumer); 1346 } 1347 1348 /** 1349 * Sends the requested visibilities to window manager if any of them is changed. 1350 */ updateRequestedVisibility()1351 private void updateRequestedVisibility() { 1352 boolean changed = false; 1353 for (int i = mRequestedVisibilityChanged.size() - 1; i >= 0; i--) { 1354 final InsetsSourceConsumer consumer = mRequestedVisibilityChanged.valueAt(i); 1355 final @InternalInsetsType int type = consumer.getType(); 1356 if (type == ITYPE_CAPTION_BAR) { 1357 continue; 1358 } 1359 final boolean requestedVisible = consumer.isRequestedVisible(); 1360 if (requestedVisible != mRequestedState.getSourceOrDefaultVisibility(type)) { 1361 mRequestedState.getSource(type).setVisible(requestedVisible); 1362 changed = true; 1363 } 1364 } 1365 mRequestedVisibilityChanged.clear(); 1366 if (!changed) { 1367 return; 1368 } 1369 mHost.onInsetsModified(mRequestedState); 1370 } 1371 getRequestedVisibility()1372 InsetsState getRequestedVisibility() { 1373 return mRequestedState; 1374 } 1375 1376 @VisibleForTesting applyAnimation(@nsetsType final int types, boolean show, boolean fromIme)1377 public void applyAnimation(@InsetsType final int types, boolean show, boolean fromIme) { 1378 // TODO(b/166736352): We should only skip the animation of specific types, not all types. 1379 boolean skipAnim = false; 1380 if ((types & ime()) != 0) { 1381 final InsetsSourceConsumer consumer = mSourceConsumers.get(ITYPE_IME); 1382 final InsetsSourceControl imeControl = consumer != null ? consumer.getControl() : null; 1383 // Skip showing animation once that made by system for some reason. 1384 // (e.g. starting window with IME snapshot) 1385 if (imeControl != null) { 1386 skipAnim = imeControl.getAndClearSkipAnimationOnce() && show 1387 && consumer.hasViewFocusWhenWindowFocusGain(); 1388 } 1389 } 1390 applyAnimation(types, show, fromIme, skipAnim); 1391 } 1392 1393 @VisibleForTesting applyAnimation(@nsetsType final int types, boolean show, boolean fromIme, boolean skipAnim)1394 public void applyAnimation(@InsetsType final int types, boolean show, boolean fromIme, 1395 boolean skipAnim) { 1396 if (types == 0) { 1397 // nothing to animate. 1398 if (DEBUG) Log.d(TAG, "applyAnimation, nothing to animate"); 1399 return; 1400 } 1401 1402 boolean hasAnimationCallbacks = mHost.hasAnimationCallbacks(); 1403 final InternalAnimationControlListener listener = new InternalAnimationControlListener( 1404 show, hasAnimationCallbacks, types, mHost.getSystemBarsBehavior(), 1405 skipAnim || mAnimationsDisabled, mHost.dipToPx(FLOATING_IME_BOTTOM_INSET_DP)); 1406 1407 // We are about to playing the default animation (show/hide). Passing a null frame indicates 1408 // the controlled types should be animated regardless of the frame. 1409 controlAnimationUnchecked( 1410 types, null /* cancellationSignal */, listener, null /* frame */, fromIme, 1411 listener.getDurationMs(), listener.getInsetsInterpolator(), 1412 show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE, 1413 show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN, 1414 !hasAnimationCallbacks /* useInsetsAnimationThread */); 1415 } 1416 hideDirectly( @nsetsType int types, boolean animationFinished, @AnimationType int animationType, boolean fromIme)1417 private void hideDirectly( 1418 @InsetsType int types, boolean animationFinished, @AnimationType int animationType, 1419 boolean fromIme) { 1420 if ((types & ime()) != 0) { 1421 ImeTracing.getInstance().triggerClientDump("InsetsController#hideDirectly", 1422 mHost.getInputMethodManager(), null /* icProto */); 1423 } 1424 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types); 1425 for (int i = internalTypes.size() - 1; i >= 0; i--) { 1426 getSourceConsumer(internalTypes.valueAt(i)).hide(animationFinished, animationType); 1427 } 1428 updateRequestedVisibility(); 1429 1430 if (fromIme) { 1431 Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.hideRequestFromIme", 0); 1432 } 1433 } 1434 showDirectly(@nsetsType int types, boolean fromIme)1435 private void showDirectly(@InsetsType int types, boolean fromIme) { 1436 if ((types & ime()) != 0) { 1437 ImeTracing.getInstance().triggerClientDump("InsetsController#showDirectly", 1438 mHost.getInputMethodManager(), null /* icProto */); 1439 } 1440 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types); 1441 for (int i = internalTypes.size() - 1; i >= 0; i--) { 1442 getSourceConsumer(internalTypes.valueAt(i)).show(false /* fromIme */); 1443 } 1444 updateRequestedVisibility(); 1445 1446 if (fromIme) { 1447 Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromIme", 0); 1448 } 1449 } 1450 1451 /** 1452 * Cancel on-going animation to show/hide {@link InsetsType}. 1453 */ 1454 @VisibleForTesting cancelExistingAnimations()1455 public void cancelExistingAnimations() { 1456 cancelExistingControllers(all()); 1457 } 1458 dump(String prefix, PrintWriter pw)1459 void dump(String prefix, PrintWriter pw) { 1460 pw.print(prefix); pw.println("InsetsController:"); 1461 mState.dump(prefix + " ", pw); 1462 } 1463 dumpDebug(ProtoOutputStream proto, long fieldId)1464 void dumpDebug(ProtoOutputStream proto, long fieldId) { 1465 final long token = proto.start(fieldId); 1466 mState.dumpDebug(proto, STATE); 1467 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { 1468 InsetsAnimationControlRunner runner = mRunningAnimations.get(i).runner; 1469 runner.dumpDebug(proto, CONTROL); 1470 } 1471 proto.end(token); 1472 } 1473 1474 @VisibleForTesting 1475 @Override startAnimation(InsetsAnimationControlImpl controller, WindowInsetsAnimationControlListener listener, int types, WindowInsetsAnimation animation, Bounds bounds)1476 public void startAnimation(InsetsAnimationControlImpl controller, 1477 WindowInsetsAnimationControlListener listener, int types, 1478 WindowInsetsAnimation animation, Bounds bounds) { 1479 mHost.dispatchWindowInsetsAnimationPrepare(animation); 1480 mHost.addOnPreDrawRunnable(() -> { 1481 if (controller.isCancelled()) { 1482 if (WARN) Log.w(TAG, "startAnimation canceled before preDraw"); 1483 return; 1484 } 1485 Trace.asyncTraceBegin(TRACE_TAG_VIEW, 1486 "InsetsAnimation: " + WindowInsets.Type.toString(types), types); 1487 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { 1488 RunningAnimation runningAnimation = mRunningAnimations.get(i); 1489 if (runningAnimation.runner == controller) { 1490 runningAnimation.startDispatched = true; 1491 } 1492 } 1493 Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.pendingAnim", 0); 1494 mHost.dispatchWindowInsetsAnimationStart(animation, bounds); 1495 mStartingAnimation = true; 1496 controller.mReadyDispatched = true; 1497 listener.onReady(controller, types); 1498 mStartingAnimation = false; 1499 }); 1500 } 1501 1502 @VisibleForTesting dispatchAnimationEnd(WindowInsetsAnimation animation)1503 public void dispatchAnimationEnd(WindowInsetsAnimation animation) { 1504 Trace.asyncTraceEnd(TRACE_TAG_VIEW, 1505 "InsetsAnimation: " + WindowInsets.Type.toString(animation.getTypeMask()), 1506 animation.getTypeMask()); 1507 mHost.dispatchWindowInsetsAnimationEnd(animation); 1508 } 1509 1510 @VisibleForTesting 1511 @Override scheduleApplyChangeInsets(InsetsAnimationControlRunner runner)1512 public void scheduleApplyChangeInsets(InsetsAnimationControlRunner runner) { 1513 if (mStartingAnimation || runner.getAnimationType() == ANIMATION_TYPE_USER) { 1514 mAnimCallback.run(); 1515 mAnimCallbackScheduled = false; 1516 return; 1517 } 1518 if (!mAnimCallbackScheduled) { 1519 mHost.postInsetsAnimationCallback(mAnimCallback); 1520 mAnimCallbackScheduled = true; 1521 } 1522 } 1523 1524 @Override setSystemBarsAppearance(@ppearance int appearance, @Appearance int mask)1525 public void setSystemBarsAppearance(@Appearance int appearance, @Appearance int mask) { 1526 mHost.setSystemBarsAppearance(appearance, mask); 1527 } 1528 1529 @Override getSystemBarsAppearance()1530 public @Appearance int getSystemBarsAppearance() { 1531 if (!mHost.isSystemBarsAppearanceControlled()) { 1532 // We only return the requested appearance, not the implied one. 1533 return 0; 1534 } 1535 return mHost.getSystemBarsAppearance(); 1536 } 1537 1538 @Override setCaptionInsetsHeight(int height)1539 public void setCaptionInsetsHeight(int height) { 1540 if (mCaptionInsetsHeight != height) { 1541 mCaptionInsetsHeight = height; 1542 if (mCaptionInsetsHeight != 0) { 1543 mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(mFrame.left, mFrame.top, 1544 mFrame.right, mFrame.top + mCaptionInsetsHeight)); 1545 } else { 1546 mState.removeSource(ITYPE_CAPTION_BAR); 1547 } 1548 mHost.notifyInsetsChanged(); 1549 } 1550 } 1551 1552 @Override setSystemBarsBehavior(@ehavior int behavior)1553 public void setSystemBarsBehavior(@Behavior int behavior) { 1554 mHost.setSystemBarsBehavior(behavior); 1555 } 1556 1557 @Override getSystemBarsBehavior()1558 public @Behavior int getSystemBarsBehavior() { 1559 if (!mHost.isSystemBarsBehaviorControlled()) { 1560 // We only return the requested behavior, not the implied one. 1561 return 0; 1562 } 1563 return mHost.getSystemBarsBehavior(); 1564 } 1565 1566 @Override setAnimationsDisabled(boolean disable)1567 public void setAnimationsDisabled(boolean disable) { 1568 mAnimationsDisabled = disable; 1569 } 1570 calculateControllableTypes()1571 private @InsetsType int calculateControllableTypes() { 1572 @InsetsType int result = 0; 1573 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { 1574 InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); 1575 InsetsSource source = mState.peekSource(consumer.mType); 1576 if (consumer.getControl() != null && source != null && source.isUserControllable()) { 1577 result |= toPublicType(consumer.mType); 1578 } 1579 } 1580 return result & ~mState.calculateUncontrollableInsetsFromFrame(mFrame); 1581 } 1582 1583 /** 1584 * @return The types that are now animating due to a listener invoking control/show/hide 1585 */ invokeControllableInsetsChangedListeners()1586 private @InsetsType int invokeControllableInsetsChangedListeners() { 1587 mHandler.removeCallbacks(mInvokeControllableInsetsChangedListeners); 1588 mLastStartedAnimTypes = 0; 1589 @InsetsType int types = calculateControllableTypes(); 1590 int size = mControllableInsetsChangedListeners.size(); 1591 for (int i = 0; i < size; i++) { 1592 mControllableInsetsChangedListeners.get(i).onControllableInsetsChanged(this, types); 1593 } 1594 return mLastStartedAnimTypes; 1595 } 1596 1597 @Override addOnControllableInsetsChangedListener( OnControllableInsetsChangedListener listener)1598 public void addOnControllableInsetsChangedListener( 1599 OnControllableInsetsChangedListener listener) { 1600 Objects.requireNonNull(listener); 1601 mControllableInsetsChangedListeners.add(listener); 1602 listener.onControllableInsetsChanged(this, calculateControllableTypes()); 1603 } 1604 1605 @Override removeOnControllableInsetsChangedListener( OnControllableInsetsChangedListener listener)1606 public void removeOnControllableInsetsChangedListener( 1607 OnControllableInsetsChangedListener listener) { 1608 Objects.requireNonNull(listener); 1609 mControllableInsetsChangedListeners.remove(listener); 1610 } 1611 1612 @Override releaseSurfaceControlFromRt(SurfaceControl sc)1613 public void releaseSurfaceControlFromRt(SurfaceControl sc) { 1614 mHost.releaseSurfaceControlFromRt(sc); 1615 } 1616 1617 @Override reportPerceptible(int types, boolean perceptible)1618 public void reportPerceptible(int types, boolean perceptible) { 1619 final ArraySet<Integer> internalTypes = toInternalType(types); 1620 final int size = mSourceConsumers.size(); 1621 for (int i = 0; i < size; i++) { 1622 final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); 1623 if (internalTypes.contains(consumer.getType())) { 1624 consumer.onPerceptible(perceptible); 1625 } 1626 } 1627 } 1628 getHost()1629 Host getHost() { 1630 return mHost; 1631 } 1632 } 1633