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.inputmethodservice.InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR; 20 import static android.os.Trace.TRACE_TAG_VIEW; 21 import static android.view.InsetsControllerProto.CONTROL; 22 import static android.view.InsetsControllerProto.STATE; 23 import static android.view.InsetsSource.ID_IME; 24 import static android.view.InsetsSource.ID_IME_CAPTION_BAR; 25 import static android.view.WindowInsets.Type.FIRST; 26 import static android.view.WindowInsets.Type.LAST; 27 import static android.view.WindowInsets.Type.all; 28 import static android.view.WindowInsets.Type.captionBar; 29 import static android.view.WindowInsets.Type.ime; 30 31 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; 32 33 import android.animation.Animator; 34 import android.animation.AnimatorListenerAdapter; 35 import android.animation.TypeEvaluator; 36 import android.animation.ValueAnimator; 37 import android.annotation.IntDef; 38 import android.annotation.NonNull; 39 import android.annotation.Nullable; 40 import android.app.ActivityThread; 41 import android.content.Context; 42 import android.content.res.CompatibilityInfo; 43 import android.graphics.Insets; 44 import android.graphics.Point; 45 import android.graphics.Rect; 46 import android.os.CancellationSignal; 47 import android.os.Handler; 48 import android.os.IBinder; 49 import android.os.Trace; 50 import android.text.TextUtils; 51 import android.util.IntArray; 52 import android.util.Log; 53 import android.util.Pair; 54 import android.util.SparseArray; 55 import android.util.proto.ProtoOutputStream; 56 import android.view.InsetsSourceConsumer.ShowResult; 57 import android.view.SurfaceControl.Transaction; 58 import android.view.WindowInsets.Type; 59 import android.view.WindowInsets.Type.InsetsType; 60 import android.view.WindowInsetsAnimation.Bounds; 61 import android.view.WindowManager.LayoutParams.SoftInputModeFlags; 62 import android.view.animation.Interpolator; 63 import android.view.animation.LinearInterpolator; 64 import android.view.animation.PathInterpolator; 65 import android.view.inputmethod.Flags; 66 import android.view.inputmethod.ImeTracker; 67 import android.view.inputmethod.ImeTracker.InputMethodJankContext; 68 import android.view.inputmethod.InputMethodManager; 69 70 import com.android.internal.annotations.VisibleForTesting; 71 import com.android.internal.inputmethod.ImeTracing; 72 import com.android.internal.inputmethod.SoftInputShowHideReason; 73 import com.android.internal.util.function.TriFunction; 74 75 import java.io.PrintWriter; 76 import java.lang.annotation.Retention; 77 import java.lang.annotation.RetentionPolicy; 78 import java.util.ArrayList; 79 import java.util.Collections; 80 import java.util.List; 81 import java.util.Objects; 82 83 /** 84 * Implements {@link WindowInsetsController} on the client. 85 * @hide 86 */ 87 public class InsetsController implements WindowInsetsController, InsetsAnimationControlCallbacks { 88 89 private int mTypesBeingCancelled; 90 91 public interface Host { 92 getHandler()93 Handler getHandler(); 94 95 /** 96 * Notifies host that {@link InsetsController#getState()} has changed. 97 */ notifyInsetsChanged()98 void notifyInsetsChanged(); 99 dispatchWindowInsetsAnimationPrepare(@onNull WindowInsetsAnimation animation)100 void dispatchWindowInsetsAnimationPrepare(@NonNull WindowInsetsAnimation animation); dispatchWindowInsetsAnimationStart( @onNull WindowInsetsAnimation animation, @NonNull Bounds bounds)101 Bounds dispatchWindowInsetsAnimationStart( 102 @NonNull WindowInsetsAnimation animation, @NonNull Bounds bounds); dispatchWindowInsetsAnimationProgress(@onNull WindowInsets insets, @NonNull List<WindowInsetsAnimation> runningAnimations)103 WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull WindowInsets insets, 104 @NonNull List<WindowInsetsAnimation> runningAnimations); dispatchWindowInsetsAnimationEnd(@onNull WindowInsetsAnimation animation)105 void dispatchWindowInsetsAnimationEnd(@NonNull WindowInsetsAnimation animation); 106 107 /** 108 * Requests host to apply surface params in synchronized manner. 109 */ applySurfaceParams(final SyncRtSurfaceTransactionApplier.SurfaceParams... params)110 void applySurfaceParams(final SyncRtSurfaceTransactionApplier.SurfaceParams... params); 111 112 /** 113 * @see ViewRootImpl#updateCompatSysUiVisibility(int, int, int) 114 */ updateCompatSysUiVisibility(@nsetsType int visibleTypes, @InsetsType int requestedVisibleTypes, @InsetsType int controllableTypes)115 default void updateCompatSysUiVisibility(@InsetsType int visibleTypes, 116 @InsetsType int requestedVisibleTypes, @InsetsType int controllableTypes) { } 117 118 /** 119 * Called when the requested visibilities of insets have been modified by the client. 120 * The visibilities should be reported back to WM. 121 * 122 * @param types Bitwise flags of types requested visible. 123 */ updateRequestedVisibleTypes(@nsetsType int types)124 void updateRequestedVisibleTypes(@InsetsType int types); 125 126 /** 127 * @return Whether the host has any callbacks it wants to synchronize the animations with. 128 * If there are no callbacks, the animation will be off-loaded to another thread and 129 * slightly different animation curves are picked. 130 */ hasAnimationCallbacks()131 boolean hasAnimationCallbacks(); 132 133 /** 134 * @see WindowInsetsController#setSystemBarsAppearance 135 */ setSystemBarsAppearance(@ppearance int appearance, @Appearance int mask)136 void setSystemBarsAppearance(@Appearance int appearance, @Appearance int mask); 137 138 /** 139 * @see WindowInsetsController#getSystemBarsAppearance() 140 */ getSystemBarsAppearance()141 @Appearance int getSystemBarsAppearance(); 142 143 /** 144 * @see WindowInsetsController#setSystemBarsBehavior 145 */ setSystemBarsBehavior(@ehavior int behavior)146 void setSystemBarsBehavior(@Behavior int behavior); 147 148 /** 149 * @see WindowInsetsController#getSystemBarsBehavior 150 */ getSystemBarsBehavior()151 @Behavior int getSystemBarsBehavior(); 152 153 /** 154 * Releases a surface and ensure that this is done after {@link #applySurfaceParams} has 155 * finished applying params. 156 */ releaseSurfaceControlFromRt(SurfaceControl surfaceControl)157 void releaseSurfaceControlFromRt(SurfaceControl surfaceControl); 158 159 /** 160 * If this host is a view hierarchy, adds a pre-draw runnable to ensure proper ordering as 161 * described in {@link WindowInsetsAnimation.Callback#onPrepare}. 162 * 163 * If this host isn't a view hierarchy, the runnable can be executed immediately. 164 */ addOnPreDrawRunnable(Runnable r)165 void addOnPreDrawRunnable(Runnable r); 166 167 /** 168 * Adds a runnbale to be executed during {@link Choreographer#CALLBACK_INSETS_ANIMATION} 169 * phase. 170 */ postInsetsAnimationCallback(Runnable r)171 void postInsetsAnimationCallback(Runnable r); 172 173 /** 174 * Obtains {@link InputMethodManager} instance from host. 175 */ getInputMethodManager()176 InputMethodManager getInputMethodManager(); 177 178 /** 179 * @return title of the rootView, if it has one. 180 * Note: this method is for debugging purposes only. 181 */ 182 @Nullable getRootViewTitle()183 String getRootViewTitle(); 184 185 /** 186 * @return the context related to the rootView. 187 */ 188 @Nullable getRootViewContext()189 default Context getRootViewContext() { 190 return null; 191 } 192 193 /** @see ViewRootImpl#dipToPx */ dipToPx(int dips)194 int dipToPx(int dips); 195 196 /** 197 * @return token associated with the host, if it has one. 198 */ 199 @Nullable getWindowToken()200 IBinder getWindowToken(); 201 202 /** 203 * @return Translator associated with the host, if it has one. 204 */ 205 @Nullable getTranslator()206 default CompatibilityInfo.Translator getTranslator() { 207 return null; 208 } 209 210 /** 211 * Notifies when the state of running animation is changed. The state is either "running" or 212 * "idle". 213 * 214 * @param running {@code true} if there is any animation running; {@code false} otherwise. 215 */ notifyAnimationRunningStateChanged(boolean running)216 default void notifyAnimationRunningStateChanged(boolean running) {} 217 218 /** @see ViewRootImpl#isHandlingPointerEvent */ isHandlingPointerEvent()219 default boolean isHandlingPointerEvent() { 220 return false; 221 } 222 } 223 224 private static final String TAG = "InsetsController"; 225 private static final int ANIMATION_DURATION_MOVE_IN_MS = 275; 226 private static final int ANIMATION_DURATION_MOVE_OUT_MS = 340; 227 private static final int ANIMATION_DURATION_FADE_IN_MS = 500; 228 private static final int ANIMATION_DURATION_FADE_OUT_MS = 1500; 229 230 /** Visible for WindowManagerWrapper */ 231 public static final int ANIMATION_DURATION_RESIZE = 300; 232 233 private static final int ANIMATION_DELAY_DIM_MS = 500; 234 235 private static final int ANIMATION_DURATION_SYNC_IME_MS = 285; 236 private static final int ANIMATION_DURATION_UNSYNC_IME_MS = 200; 237 238 private static final int PENDING_CONTROL_TIMEOUT_MS = 2000; 239 240 private static final Interpolator SYSTEM_BARS_INSETS_INTERPOLATOR = 241 new PathInterpolator(0.4f, 0f, 0.2f, 1f); 242 private static final Interpolator SYSTEM_BARS_ALPHA_INTERPOLATOR = 243 new PathInterpolator(0.3f, 0f, 1f, 1f); 244 private static final Interpolator SYSTEM_BARS_DIM_INTERPOLATOR = alphaFraction -> { 245 // While playing dim animation, alphaFraction is changed from 1f to 0f. Here changes it to 246 // time-based fraction for computing delay and interpolation. 247 float fraction = 1 - alphaFraction; 248 final float fractionDelay = (float) ANIMATION_DELAY_DIM_MS / ANIMATION_DURATION_FADE_OUT_MS; 249 if (fraction <= fractionDelay) { 250 return 1f; 251 } else { 252 float innerFraction = (fraction - fractionDelay) / (1f - fractionDelay); 253 return 1f - SYSTEM_BARS_ALPHA_INTERPOLATOR.getInterpolation(innerFraction); 254 } 255 }; 256 private static final Interpolator SYNC_IME_INTERPOLATOR = 257 new PathInterpolator(0.2f, 0f, 0f, 1f); 258 private static final Interpolator LINEAR_OUT_SLOW_IN_INTERPOLATOR = 259 new PathInterpolator(0, 0, 0.2f, 1f); 260 private static final Interpolator FAST_OUT_LINEAR_IN_INTERPOLATOR = 261 new PathInterpolator(0.4f, 0f, 1f, 1f); 262 263 /** Visible for WindowManagerWrapper */ 264 public static final Interpolator RESIZE_INTERPOLATOR = new LinearInterpolator(); 265 266 /** The amount IME will move up/down when animating in floating mode. */ 267 private static final int FLOATING_IME_BOTTOM_INSET_DP = -80; 268 269 private static final int ID_CAPTION_BAR = 270 InsetsSource.createId(null /* owner */, 0 /* index */, captionBar()); 271 272 static final boolean DEBUG = false; 273 static final boolean WARN = false; 274 275 /** 276 * Layout mode during insets animation: The views should be laid out as if the changing inset 277 * types are fully shown. Before starting the animation, {@link View#onApplyWindowInsets} will 278 * be called as if the changing insets types are shown, which will result in the views being 279 * laid out as if the insets are fully shown. 280 */ 281 public static final int LAYOUT_INSETS_DURING_ANIMATION_SHOWN = 0; 282 283 /** 284 * Layout mode during insets animation: The views should be laid out as if the changing inset 285 * types are fully hidden. Before starting the animation, {@link View#onApplyWindowInsets} will 286 * be called as if the changing insets types are hidden, which will result in the views being 287 * laid out as if the insets are fully hidden. 288 */ 289 public static final int LAYOUT_INSETS_DURING_ANIMATION_HIDDEN = 1; 290 291 /** 292 * Determines the behavior of how the views should be laid out during an insets animation that 293 * is controlled by the application by calling {@link #controlWindowInsetsAnimation}. 294 * <p> 295 * When the animation is system-initiated, the layout mode is always chosen such that the 296 * pre-animation layout will represent the opposite of the starting state, i.e. when insets 297 * are appearing, {@link #LAYOUT_INSETS_DURING_ANIMATION_SHOWN} will be used. When insets 298 * are disappearing, {@link #LAYOUT_INSETS_DURING_ANIMATION_HIDDEN} will be used. 299 */ 300 @Retention(RetentionPolicy.SOURCE) 301 @IntDef(value = {LAYOUT_INSETS_DURING_ANIMATION_SHOWN, 302 LAYOUT_INSETS_DURING_ANIMATION_HIDDEN}) 303 @interface LayoutInsetsDuringAnimation { 304 } 305 306 /** Not running an animation. */ 307 @VisibleForTesting 308 public static final int ANIMATION_TYPE_NONE = -1; 309 310 /** Running animation will show insets */ 311 public static final int ANIMATION_TYPE_SHOW = 0; 312 313 /** Running animation will hide insets */ 314 public static final int ANIMATION_TYPE_HIDE = 1; 315 316 /** Running animation is controlled by user via {@link #controlWindowInsetsAnimation} */ 317 @VisibleForTesting(visibility = PACKAGE) 318 public static final int ANIMATION_TYPE_USER = 2; 319 320 /** Running animation will resize insets */ 321 @VisibleForTesting 322 public static final int ANIMATION_TYPE_RESIZE = 3; 323 324 @Retention(RetentionPolicy.SOURCE) 325 @IntDef(value = {ANIMATION_TYPE_NONE, ANIMATION_TYPE_SHOW, ANIMATION_TYPE_HIDE, 326 ANIMATION_TYPE_USER, ANIMATION_TYPE_RESIZE}) 327 public @interface AnimationType { 328 } 329 330 /** 331 * Translation animation evaluator. 332 */ 333 private static TypeEvaluator<Insets> sEvaluator = (fraction, startValue, endValue) -> Insets.of( 334 (int) (startValue.left + fraction * (endValue.left - startValue.left)), 335 (int) (startValue.top + fraction * (endValue.top - startValue.top)), 336 (int) (startValue.right + fraction * (endValue.right - startValue.right)), 337 (int) (startValue.bottom + fraction * (endValue.bottom - startValue.bottom))); 338 339 /** Logging listener. */ 340 private WindowInsetsAnimationControlListener mLoggingListener; 341 342 /** Context for {@link android.view.inputmethod.ImeTracker.ImeJankTracker} to monitor jank. */ 343 private final InputMethodJankContext mJankContext = new InputMethodJankContext() { 344 @Override 345 public Context getDisplayContext() { 346 return mHost != null ? mHost.getRootViewContext() : null; 347 } 348 349 @Override 350 public SurfaceControl getTargetSurfaceControl() { 351 final InsetsSourceControl imeSourceControl = getImeSourceConsumer().getControl(); 352 return imeSourceControl != null ? imeSourceControl.getLeash() : null; 353 } 354 355 @Override 356 public String getHostPackageName() { 357 return mHost != null ? mHost.getRootViewContext().getPackageName() : null; 358 } 359 }; 360 361 /** 362 * The default implementation of listener, to be used by InsetsController and InsetsPolicy to 363 * animate insets. 364 */ 365 public static class InternalAnimationControlListener 366 implements WindowInsetsAnimationControlListener { 367 368 private WindowInsetsAnimationController mController; 369 private ValueAnimator mAnimator; 370 private final boolean mShow; 371 private final boolean mHasAnimationCallbacks; 372 private final @InsetsType int mRequestedTypes; 373 private final @Behavior int mBehavior; 374 private final long mDurationMs; 375 private final boolean mDisable; 376 private final int mFloatingImeBottomInset; 377 private final WindowInsetsAnimationControlListener mLoggingListener; 378 private final InputMethodJankContext mInputMethodJankContext; 379 InternalAnimationControlListener(boolean show, boolean hasAnimationCallbacks, @InsetsType int requestedTypes, @Behavior int behavior, boolean disable, int floatingImeBottomInset, WindowInsetsAnimationControlListener loggingListener, @Nullable InputMethodJankContext jankContext)380 public InternalAnimationControlListener(boolean show, boolean hasAnimationCallbacks, 381 @InsetsType int requestedTypes, @Behavior int behavior, boolean disable, 382 int floatingImeBottomInset, WindowInsetsAnimationControlListener loggingListener, 383 @Nullable InputMethodJankContext jankContext) { 384 mShow = show; 385 mHasAnimationCallbacks = hasAnimationCallbacks; 386 mRequestedTypes = requestedTypes; 387 mBehavior = behavior; 388 mDurationMs = calculateDurationMs(); 389 mDisable = disable; 390 mFloatingImeBottomInset = floatingImeBottomInset; 391 mLoggingListener = loggingListener; 392 mInputMethodJankContext = jankContext; 393 } 394 395 @Override onReady(WindowInsetsAnimationController controller, int types)396 public void onReady(WindowInsetsAnimationController controller, int types) { 397 mController = controller; 398 if (DEBUG) Log.d(TAG, "default animation onReady types: " + types); 399 if (mLoggingListener != null) { 400 mLoggingListener.onReady(controller, types); 401 } 402 403 if (mDisable) { 404 onAnimationFinish(); 405 return; 406 } 407 mAnimator = ValueAnimator.ofFloat(0f, 1f); 408 mAnimator.setDuration(mDurationMs); 409 mAnimator.setInterpolator(new LinearInterpolator()); 410 Insets hiddenInsets = controller.getHiddenStateInsets(); 411 // IME with zero insets is a special case: it will animate-in from offscreen and end 412 // with final insets of zero and vice-versa. 413 hiddenInsets = controller.hasZeroInsetsIme() 414 ? Insets.of(hiddenInsets.left, hiddenInsets.top, hiddenInsets.right, 415 mFloatingImeBottomInset) 416 : hiddenInsets; 417 Insets start = mShow 418 ? hiddenInsets 419 : controller.getShownStateInsets(); 420 Insets end = mShow 421 ? controller.getShownStateInsets() 422 : hiddenInsets; 423 Interpolator insetsInterpolator = getInsetsInterpolator(); 424 Interpolator alphaInterpolator = getAlphaInterpolator(); 425 mAnimator.addUpdateListener(animation -> { 426 float rawFraction = animation.getAnimatedFraction(); 427 float alphaFraction = mShow 428 ? rawFraction 429 : 1 - rawFraction; 430 float insetsFraction = insetsInterpolator.getInterpolation(rawFraction); 431 controller.setInsetsAndAlpha( 432 sEvaluator.evaluate(insetsFraction, start, end), 433 alphaInterpolator.getInterpolation(alphaFraction), 434 rawFraction); 435 if (DEBUG) Log.d(TAG, "Default animation setInsetsAndAlpha fraction: " 436 + insetsFraction); 437 }); 438 mAnimator.addListener(new AnimatorListenerAdapter() { 439 @Override 440 public void onAnimationStart(Animator animation) { 441 if (mInputMethodJankContext == null) return; 442 ImeTracker.forJank().onRequestAnimation( 443 mInputMethodJankContext, 444 getAnimationType(), 445 !mHasAnimationCallbacks); 446 } 447 448 @Override 449 public void onAnimationCancel(Animator animation) { 450 if (mInputMethodJankContext == null) return; 451 ImeTracker.forJank().onCancelAnimation(getAnimationType()); 452 } 453 454 @Override 455 public void onAnimationEnd(Animator animation) { 456 onAnimationFinish(); 457 if (mInputMethodJankContext == null) return; 458 ImeTracker.forJank().onFinishAnimation(getAnimationType()); 459 } 460 }); 461 mAnimator.start(); 462 } 463 464 @Override onFinished(WindowInsetsAnimationController controller)465 public void onFinished(WindowInsetsAnimationController controller) { 466 if (DEBUG) Log.d(TAG, "InternalAnimationControlListener onFinished types:" 467 + Type.toString(mRequestedTypes)); 468 if (mLoggingListener != null) { 469 mLoggingListener.onFinished(controller); 470 } 471 } 472 473 @Override onCancelled(WindowInsetsAnimationController controller)474 public void onCancelled(WindowInsetsAnimationController controller) { 475 // Animator can be null when it is cancelled before onReady() completes. 476 if (mAnimator != null) { 477 mAnimator.cancel(); 478 } 479 if (DEBUG) Log.d(TAG, "InternalAnimationControlListener onCancelled types:" 480 + mRequestedTypes); 481 if (mLoggingListener != null) { 482 mLoggingListener.onCancelled(controller); 483 } 484 } 485 getInsetsInterpolator()486 protected Interpolator getInsetsInterpolator() { 487 if ((mRequestedTypes & ime()) != 0) { 488 if (mHasAnimationCallbacks) { 489 return SYNC_IME_INTERPOLATOR; 490 } else if (mShow) { 491 return LINEAR_OUT_SLOW_IN_INTERPOLATOR; 492 } else { 493 return FAST_OUT_LINEAR_IN_INTERPOLATOR; 494 } 495 } else { 496 if (mBehavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE) { 497 return SYSTEM_BARS_INSETS_INTERPOLATOR; 498 } else { 499 // Makes insets stay at the shown position. 500 return input -> mShow ? 1f : 0f; 501 } 502 } 503 } 504 getAlphaInterpolator()505 Interpolator getAlphaInterpolator() { 506 if ((mRequestedTypes & ime()) != 0) { 507 if (mHasAnimationCallbacks) { 508 return input -> 1f; 509 } else if (mShow) { 510 511 // Alpha animation takes half the time with linear interpolation; 512 return input -> Math.min(1f, 2 * input); 513 } else { 514 return FAST_OUT_LINEAR_IN_INTERPOLATOR; 515 } 516 } else { 517 if (mBehavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE) { 518 return input -> 1f; 519 } else { 520 if (mShow) { 521 return SYSTEM_BARS_ALPHA_INTERPOLATOR; 522 } else { 523 return SYSTEM_BARS_DIM_INTERPOLATOR; 524 } 525 } 526 } 527 } 528 onAnimationFinish()529 protected void onAnimationFinish() { 530 mController.finish(mShow); 531 if (DEBUG) Log.d(TAG, "onAnimationFinish showOnFinish: " + mShow); 532 } 533 534 /** 535 * To get the animation duration in MS. 536 */ getDurationMs()537 public long getDurationMs() { 538 return mDurationMs; 539 } 540 calculateDurationMs()541 private long calculateDurationMs() { 542 if ((mRequestedTypes & ime()) != 0) { 543 if (mHasAnimationCallbacks) { 544 return ANIMATION_DURATION_SYNC_IME_MS; 545 } else { 546 return ANIMATION_DURATION_UNSYNC_IME_MS; 547 } 548 } else { 549 if (mBehavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE) { 550 return mShow ? ANIMATION_DURATION_MOVE_IN_MS : ANIMATION_DURATION_MOVE_OUT_MS; 551 } else { 552 return mShow ? ANIMATION_DURATION_FADE_IN_MS : ANIMATION_DURATION_FADE_OUT_MS; 553 } 554 } 555 } 556 557 /** 558 * Returns the current animation type. 559 */ 560 @AnimationType getAnimationType()561 private int getAnimationType() { 562 return mShow ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE; 563 } 564 } 565 566 /** 567 * Represents a running animation 568 */ 569 private static class RunningAnimation { 570 RunningAnimation(InsetsAnimationControlRunner runner, int type)571 RunningAnimation(InsetsAnimationControlRunner runner, int type) { 572 this.runner = runner; 573 this.type = type; 574 } 575 576 final InsetsAnimationControlRunner runner; 577 final @AnimationType int type; 578 579 /** 580 * Whether {@link WindowInsetsAnimation.Callback#onStart(WindowInsetsAnimation, Bounds)} has 581 * been dispatched already for this animation. 582 */ 583 boolean startDispatched; 584 } 585 586 /** 587 * Represents a control request that we had to defer because we are waiting for the IME to 588 * process our show request. 589 */ 590 private static class PendingControlRequest { 591 PendingControlRequest(@nsetsType int types, WindowInsetsAnimationControlListener listener, long durationMs, Interpolator interpolator, @AnimationType int animationType, @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation, CancellationSignal cancellationSignal, boolean useInsetsAnimationThread)592 PendingControlRequest(@InsetsType int types, WindowInsetsAnimationControlListener listener, 593 long durationMs, Interpolator interpolator, @AnimationType int animationType, 594 @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation, 595 CancellationSignal cancellationSignal, boolean useInsetsAnimationThread) { 596 this.types = types; 597 this.listener = listener; 598 this.durationMs = durationMs; 599 this.interpolator = interpolator; 600 this.animationType = animationType; 601 this.layoutInsetsDuringAnimation = layoutInsetsDuringAnimation; 602 this.cancellationSignal = cancellationSignal; 603 this.useInsetsAnimationThread = useInsetsAnimationThread; 604 } 605 606 @InsetsType int types; 607 final WindowInsetsAnimationControlListener listener; 608 final long durationMs; 609 final Interpolator interpolator; 610 final @AnimationType int animationType; 611 final @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation; 612 final CancellationSignal cancellationSignal; 613 final boolean useInsetsAnimationThread; 614 } 615 616 /** The local state */ 617 private final InsetsState mState = new InsetsState(); 618 619 /** The state dispatched from server */ 620 private final InsetsState mLastDispatchedState = new InsetsState(); 621 622 private final Rect mFrame = new Rect(); 623 private final TriFunction<InsetsController, Integer, Integer, InsetsSourceConsumer> 624 mConsumerCreator; 625 private final SparseArray<InsetsSourceConsumer> mSourceConsumers = new SparseArray<>(); 626 private final InsetsSourceConsumer mImeSourceConsumer; 627 private final Host mHost; 628 private final Handler mHandler; 629 630 private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>(); 631 private final ArrayList<RunningAnimation> mRunningAnimations = new ArrayList<>(); 632 private WindowInsets mLastInsets; 633 634 private boolean mAnimCallbackScheduled; 635 636 private final Runnable mAnimCallback; 637 638 /** Pending control request that is waiting on IME to be ready to be shown */ 639 @Nullable 640 private PendingControlRequest mPendingImeControlRequest; 641 642 private int mWindowType; 643 private int mLastLegacySoftInputMode; 644 private int mLastLegacyWindowFlags; 645 private int mLastLegacySystemUiFlags; 646 private int mLastActivityType; 647 private boolean mStartingAnimation; 648 private int mCaptionInsetsHeight = 0; 649 private int mImeCaptionBarInsetsHeight = 0; 650 private boolean mAnimationsDisabled; 651 private boolean mCompatSysUiVisibilityStaled; 652 private @Appearance int mAppearanceControlled; 653 private @Appearance int mAppearanceFromResource; 654 private boolean mBehaviorControlled; 655 private boolean mIsPredictiveBackImeHideAnimInProgress; 656 657 private final Runnable mPendingControlTimeout = this::abortPendingImeControlRequest; 658 private final ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners 659 = new ArrayList<>(); 660 661 /** Set of inset types for which an animation was started since last resetting this field */ 662 private @InsetsType int mLastStartedAnimTypes; 663 664 /** Set of inset types which are existing */ 665 private @InsetsType int mExistingTypes = 0; 666 667 /** Set of inset types which are visible */ 668 private @InsetsType int mVisibleTypes = WindowInsets.Type.defaultVisible(); 669 670 /** Set of inset types which are requested visible */ 671 private @InsetsType int mRequestedVisibleTypes = WindowInsets.Type.defaultVisible(); 672 673 /** Set of inset types which are requested visible which are reported to the host */ 674 private @InsetsType int mReportedRequestedVisibleTypes = WindowInsets.Type.defaultVisible(); 675 676 /** Set of inset types that we have controls of */ 677 private @InsetsType int mControllableTypes; 678 679 /** 680 * Set of inset types that are about to be cancelled. 681 * Used in {@link InsetsSourceConsumer#onAnimationStateChanged} 682 */ 683 private @InsetsType int mCancelledForNewAnimationTypes; 684 685 private final Runnable mInvokeControllableInsetsChangedListeners = 686 this::invokeControllableInsetsChangedListeners; 687 688 private final InsetsState.OnTraverseCallbacks mRemoveGoneSources = 689 new InsetsState.OnTraverseCallbacks() { 690 691 private final IntArray mPendingRemoveIndexes = new IntArray(); 692 693 @Override 694 public void onIdNotFoundInState2(int index1, InsetsSource source1) { 695 if (source1.getId() == ID_IME_CAPTION_BAR) { 696 return; 697 } 698 699 // Don't change the indexes of the sources while traversing. Remove it later. 700 mPendingRemoveIndexes.add(index1); 701 } 702 703 @Override 704 public void onFinish(InsetsState state1, InsetsState state2) { 705 for (int i = mPendingRemoveIndexes.size() - 1; i >= 0; i--) { 706 state1.removeSourceAt(mPendingRemoveIndexes.get(i)); 707 } 708 mPendingRemoveIndexes.clear(); 709 } 710 }; 711 712 private final InsetsState.OnTraverseCallbacks mStartResizingAnimationIfNeeded = 713 new InsetsState.OnTraverseCallbacks() { 714 715 private @InsetsType int mTypes; 716 private InsetsState mFromState; 717 private InsetsState mToState; 718 719 @Override 720 public void onStart(InsetsState state1, InsetsState state2) { 721 mTypes = 0; 722 mFromState = null; 723 mToState = null; 724 } 725 726 @Override 727 public void onIdMatch(InsetsSource source1, InsetsSource source2) { 728 final Rect frame1 = source1.getFrame(); 729 final Rect frame2 = source2.getFrame(); 730 if (!source1.hasFlags(InsetsSource.FLAG_ANIMATE_RESIZING) 731 || !source2.hasFlags(InsetsSource.FLAG_ANIMATE_RESIZING) 732 || !source1.isVisible() || !source2.isVisible() 733 || frame1.equals(frame2) || frame1.isEmpty() || frame2.isEmpty() 734 || !(Rect.intersects(mFrame, source1.getFrame()) 735 || Rect.intersects(mFrame, source2.getFrame()))) { 736 return; 737 } 738 mTypes |= source1.getType(); 739 if (mFromState == null) { 740 mFromState = new InsetsState(); 741 } 742 if (mToState == null) { 743 mToState = new InsetsState(); 744 } 745 mFromState.addSource(new InsetsSource(source1)); 746 mToState.addSource(new InsetsSource(source2)); 747 } 748 749 @Override 750 public void onFinish(InsetsState state1, InsetsState state2) { 751 if (mTypes == 0) { 752 return; 753 } 754 cancelExistingControllers(mTypes); 755 final InsetsAnimationControlRunner runner = new InsetsResizeAnimationRunner( 756 mFrame, mFromState, mToState, RESIZE_INTERPOLATOR, 757 ANIMATION_DURATION_RESIZE, mTypes, InsetsController.this); 758 if (mRunningAnimations.isEmpty()) { 759 mHost.notifyAnimationRunningStateChanged(true); 760 } 761 mRunningAnimations.add(new RunningAnimation(runner, runner.getAnimationType())); 762 } 763 }; 764 InsetsController(Host host)765 public InsetsController(Host host) { 766 this(host, (controller, id, type) -> { 767 if (type == ime()) { 768 return new ImeInsetsSourceConsumer(id, controller.mState, 769 Transaction::new, controller); 770 } else { 771 return new InsetsSourceConsumer(id, type, controller.mState, 772 Transaction::new, controller); 773 } 774 }, host.getHandler()); 775 } 776 777 @VisibleForTesting InsetsController(Host host, TriFunction<InsetsController, Integer, Integer, InsetsSourceConsumer> consumerCreator, Handler handler)778 public InsetsController(Host host, 779 TriFunction<InsetsController, Integer, Integer, InsetsSourceConsumer> consumerCreator, 780 Handler handler) { 781 mHost = host; 782 mConsumerCreator = consumerCreator; 783 mHandler = handler; 784 mAnimCallback = () -> { 785 mAnimCallbackScheduled = false; 786 if (mRunningAnimations.isEmpty()) { 787 return; 788 } 789 790 final List<WindowInsetsAnimation> runningAnimations = new ArrayList<>(); 791 final List<WindowInsetsAnimation> finishedAnimations = new ArrayList<>(); 792 final InsetsState state = new InsetsState(mState, true /* copySources */); 793 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { 794 RunningAnimation runningAnimation = mRunningAnimations.get(i); 795 if (DEBUG) Log.d(TAG, "Running animation type: " + runningAnimation.type); 796 final InsetsAnimationControlRunner runner = runningAnimation.runner; 797 if (runner instanceof WindowInsetsAnimationController) { 798 799 // Keep track of running animation to be dispatched. Aggregate it here such that 800 // if it gets finished within applyChangeInsets we still dispatch it to 801 // onProgress. 802 if (runningAnimation.startDispatched) { 803 runningAnimations.add(runner.getAnimation()); 804 } 805 806 if (((InternalInsetsAnimationController) runner).applyChangeInsets(state)) { 807 finishedAnimations.add(runner.getAnimation()); 808 } 809 } 810 } 811 812 WindowInsets insets = state.calculateInsets(mFrame, 813 mState /* ignoringVisibilityState */, mLastInsets.isRound(), 814 mLastLegacySoftInputMode, mLastLegacyWindowFlags, mLastLegacySystemUiFlags, 815 mWindowType, mLastActivityType, null /* idSideMap */); 816 mHost.dispatchWindowInsetsAnimationProgress(insets, 817 Collections.unmodifiableList(runningAnimations)); 818 if (DEBUG) { 819 for (WindowInsetsAnimation anim : runningAnimations) { 820 Log.d(TAG, String.format("Running animation on insets type: %d, progress: %f", 821 anim.getTypeMask(), anim.getInterpolatedFraction())); 822 } 823 } 824 825 for (int i = finishedAnimations.size() - 1; i >= 0; i--) { 826 dispatchAnimationEnd(finishedAnimations.get(i)); 827 } 828 }; 829 830 // Make mImeSourceConsumer always non-null. 831 mImeSourceConsumer = getSourceConsumer(ID_IME, ime()); 832 } 833 834 @VisibleForTesting onFrameChanged(Rect frame)835 public void onFrameChanged(Rect frame) { 836 if (mFrame.equals(frame)) { 837 return; 838 } 839 if (mImeCaptionBarInsetsHeight != 0) { 840 setImeCaptionBarInsetsHeight(mImeCaptionBarInsetsHeight); 841 } 842 mHost.notifyInsetsChanged(); 843 mFrame.set(frame); 844 } 845 846 @Override getState()847 public InsetsState getState() { 848 return mState; 849 } 850 851 @Override getRequestedVisibleTypes()852 public @InsetsType int getRequestedVisibleTypes() { 853 return mRequestedVisibleTypes; 854 } 855 getLastDispatchedState()856 public InsetsState getLastDispatchedState() { 857 return mLastDispatchedState; 858 } 859 onStateChanged(InsetsState state)860 public boolean onStateChanged(InsetsState state) { 861 boolean stateChanged = !mState.equals(state, false /* excludesCaptionBar */, 862 false /* excludesInvisibleIme */); 863 if (!stateChanged && mLastDispatchedState.equals(state)) { 864 return false; 865 } 866 if (DEBUG) Log.d(TAG, "onStateChanged: " + state); 867 868 final InsetsState lastState = new InsetsState(mState, true /* copySources */); 869 updateState(state); 870 applyLocalVisibilityOverride(); 871 updateCompatSysUiVisibility(); 872 873 if (!mState.equals(lastState, false /* excludesCaptionBar */, 874 true /* excludesInvisibleIme */)) { 875 if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged"); 876 mHost.notifyInsetsChanged(); 877 if (mLastDispatchedState.getDisplayFrame().equals(state.getDisplayFrame())) { 878 // Here compares the raw states instead of the overridden ones because we don't want 879 // to animate an insets source that its mServerVisible is false. 880 InsetsState.traverse(mLastDispatchedState, state, mStartResizingAnimationIfNeeded); 881 } 882 } 883 mLastDispatchedState.set(state, true /* copySources */); 884 return true; 885 } 886 updateState(InsetsState newState)887 private void updateState(InsetsState newState) { 888 mState.set(newState, 0 /* types */); 889 @InsetsType int existingTypes = 0; 890 @InsetsType int visibleTypes = 0; 891 @InsetsType int[] cancelledUserAnimationTypes = {0}; 892 for (int i = 0, size = newState.sourceSize(); i < size; i++) { 893 final InsetsSource source = newState.sourceAt(i); 894 @InsetsType int type = source.getType(); 895 @AnimationType int animationType = getAnimationType(type); 896 final InsetsSourceConsumer consumer = mSourceConsumers.get(source.getId()); 897 if (consumer != null) { 898 consumer.updateSource(source, animationType); 899 } else { 900 mState.addSource(source); 901 } 902 existingTypes |= type; 903 if (source.isVisible()) { 904 visibleTypes |= type; 905 } 906 } 907 908 // If a type doesn't have a source, treat it as visible if it is visible by default. 909 visibleTypes |= WindowInsets.Type.defaultVisible() & ~existingTypes; 910 911 if (mVisibleTypes != visibleTypes) { 912 if (WindowInsets.Type.hasCompatSystemBars(mVisibleTypes ^ visibleTypes)) { 913 mCompatSysUiVisibilityStaled = true; 914 } 915 mVisibleTypes = visibleTypes; 916 } 917 if (mExistingTypes != existingTypes) { 918 if (WindowInsets.Type.hasCompatSystemBars(mExistingTypes ^ existingTypes)) { 919 mCompatSysUiVisibilityStaled = true; 920 } 921 mExistingTypes = existingTypes; 922 } 923 InsetsState.traverse(mState, newState, mRemoveGoneSources); 924 925 if (cancelledUserAnimationTypes[0] != 0) { 926 mHandler.post(() -> show(cancelledUserAnimationTypes[0])); 927 } 928 } 929 930 /** 931 * @see InsetsState#calculateInsets(Rect, InsetsState, boolean, int, int, int, int, int, 932 * android.util.SparseIntArray) 933 */ 934 @VisibleForTesting calculateInsets(boolean isScreenRound, int windowType, int activityType, int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags)935 public WindowInsets calculateInsets(boolean isScreenRound, int windowType, int activityType, 936 int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags) { 937 mWindowType = windowType; 938 mLastActivityType = activityType; 939 mLastLegacySoftInputMode = legacySoftInputMode; 940 mLastLegacyWindowFlags = legacyWindowFlags; 941 mLastLegacySystemUiFlags = legacySystemUiFlags; 942 mLastInsets = mState.calculateInsets(mFrame, null /* ignoringVisibilityState */, 943 isScreenRound, legacySoftInputMode, legacyWindowFlags, 944 legacySystemUiFlags, windowType, activityType, null /* idSideMap */); 945 return mLastInsets; 946 } 947 948 /** 949 * @see InsetsState#calculateVisibleInsets(Rect, int, int, int, int) 950 */ calculateVisibleInsets(int windowType, int activityType, @SoftInputModeFlags int softInputMode, int windowFlags)951 public Insets calculateVisibleInsets(int windowType, int activityType, 952 @SoftInputModeFlags int softInputMode, int windowFlags) { 953 return mState.calculateVisibleInsets(mFrame, windowType, activityType, softInputMode, 954 windowFlags); 955 } 956 957 /** 958 * Called when the server has dispatched us a new set of inset controls. 959 */ onControlsChanged(InsetsSourceControl[] activeControls)960 public void onControlsChanged(InsetsSourceControl[] activeControls) { 961 if (activeControls != null) { 962 for (InsetsSourceControl activeControl : activeControls) { 963 if (activeControl != null) { 964 // TODO(b/122982984): Figure out why it can be null. 965 mTmpControlArray.put(activeControl.getId(), activeControl); 966 } 967 } 968 } 969 970 @InsetsType int controllableTypes = 0; 971 int consumedControlCount = 0; 972 final @InsetsType int[] showTypes = new int[1]; 973 final @InsetsType int[] hideTypes = new int[1]; 974 975 // Ensure to update all existing source consumers 976 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { 977 final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); 978 if (consumer.getId() == ID_IME_CAPTION_BAR) { 979 // The inset control for the IME caption bar will never be dispatched 980 // by the server. 981 continue; 982 } 983 984 final InsetsSourceControl control = mTmpControlArray.get(consumer.getId()); 985 if (control != null) { 986 controllableTypes |= control.getType(); 987 consumedControlCount++; 988 } 989 990 // control may be null, but we still need to update the control to null if it got 991 // revoked. 992 consumer.setControl(control, showTypes, hideTypes); 993 } 994 995 // Ensure to create source consumers if not available yet. 996 if (consumedControlCount != mTmpControlArray.size()) { 997 for (int i = mTmpControlArray.size() - 1; i >= 0; i--) { 998 final InsetsSourceControl control = mTmpControlArray.valueAt(i); 999 getSourceConsumer(control.getId(), control.getType()) 1000 .setControl(control, showTypes, hideTypes); 1001 } 1002 } 1003 1004 if (mTmpControlArray.size() > 0) { 1005 // Update surface positions for animations. 1006 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { 1007 mRunningAnimations.get(i).runner.updateSurfacePosition(mTmpControlArray); 1008 } 1009 } 1010 mTmpControlArray.clear(); 1011 1012 // Do not override any animations that the app started in the OnControllableInsetsChanged 1013 // listeners. 1014 int animatingTypes = invokeControllableInsetsChangedListeners(); 1015 showTypes[0] &= ~animatingTypes; 1016 hideTypes[0] &= ~animatingTypes; 1017 1018 if (Flags.refactorInsetsController()) { 1019 if (mPendingImeControlRequest != null && getImeSourceConsumer().getControl() != null 1020 && getImeSourceConsumer().getControl().getLeash() != null) { 1021 // TODO we need to pass the statsToken 1022 handlePendingControlRequest(null); 1023 } else { 1024 if (showTypes[0] != 0) { 1025 applyAnimation(showTypes[0], true /* show */, false /* fromIme */, 1026 null /* statsToken */); 1027 } 1028 if (hideTypes[0] != 0) { 1029 applyAnimation(hideTypes[0], false /* show */, false /* fromIme */, 1030 null /* statsToken */); 1031 } 1032 } 1033 } else { 1034 if (showTypes[0] != 0) { 1035 final var statsToken = 1036 (showTypes[0] & ime()) == 0 ? null : ImeTracker.forLogging().onStart( 1037 ImeTracker.TYPE_SHOW, ImeTracker.ORIGIN_CLIENT, 1038 SoftInputShowHideReason.CONTROLS_CHANGED, 1039 mHost.isHandlingPointerEvent() /* fromUser */); 1040 applyAnimation(showTypes[0], true /* show */, false /* fromIme */, statsToken); 1041 } 1042 if (hideTypes[0] != 0) { 1043 final var statsToken = 1044 (hideTypes[0] & ime()) == 0 ? null : ImeTracker.forLogging().onStart( 1045 ImeTracker.TYPE_HIDE, ImeTracker.ORIGIN_CLIENT, 1046 SoftInputShowHideReason.CONTROLS_CHANGED, 1047 mHost.isHandlingPointerEvent() /* fromUser */); 1048 applyAnimation(hideTypes[0], false /* show */, false /* fromIme */, statsToken); 1049 } 1050 } 1051 1052 if (mControllableTypes != controllableTypes) { 1053 if (WindowInsets.Type.hasCompatSystemBars(mControllableTypes ^ controllableTypes)) { 1054 mCompatSysUiVisibilityStaled = true; 1055 } 1056 mControllableTypes = controllableTypes; 1057 } 1058 1059 if (Flags.refactorInsetsController()) { 1060 // The local visibility override takes into account whether we have control. 1061 applyLocalVisibilityOverride(); 1062 } 1063 1064 // InsetsSourceConsumer#setControl might change the requested visibility. 1065 reportRequestedVisibleTypes(); 1066 } 1067 1068 @VisibleForTesting(visibility = PACKAGE) setPredictiveBackImeHideAnimInProgress(boolean isInProgress)1069 public void setPredictiveBackImeHideAnimInProgress(boolean isInProgress) { 1070 mIsPredictiveBackImeHideAnimInProgress = isInProgress; 1071 if (isInProgress) { 1072 // The InsetsAnimationControlRunner has layoutInsetsDuringAnimation set to SHOWN during 1073 // predictive back. Let's set it to HIDDEN once the predictive back animation enters the 1074 // post-commit phase. 1075 // That prevents flickers in case the animation is cancelled by an incoming show request 1076 // during the hide animation. 1077 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { 1078 final InsetsAnimationControlRunner runner = mRunningAnimations.get(i).runner; 1079 if ((runner.getTypes() & ime()) != 0) { 1080 runner.updateLayoutInsetsDuringAnimation(LAYOUT_INSETS_DURING_ANIMATION_HIDDEN); 1081 break; 1082 } 1083 } 1084 } 1085 } 1086 isPredictiveBackImeHideAnimInProgress()1087 boolean isPredictiveBackImeHideAnimInProgress() { 1088 return mIsPredictiveBackImeHideAnimInProgress; 1089 } 1090 1091 @Override show(@nsetsType int types)1092 public void show(@InsetsType int types) { 1093 show(types, false /* fromIme */, null /* statsToken */); 1094 } 1095 1096 @VisibleForTesting(visibility = PACKAGE) show(@nsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken)1097 public void show(@InsetsType int types, boolean fromIme, 1098 @Nullable ImeTracker.Token statsToken) { 1099 if ((types & ime()) != 0) { 1100 Log.d(TAG, "show(ime(), fromIme=" + fromIme + ")"); 1101 1102 if (statsToken == null) { 1103 statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_SHOW, 1104 ImeTracker.ORIGIN_CLIENT, 1105 SoftInputShowHideReason.SHOW_SOFT_INPUT_BY_INSETS_API, 1106 mHost.isHandlingPointerEvent() /* fromUser */); 1107 } 1108 } 1109 1110 if (fromIme) { 1111 ImeTracing.getInstance().triggerClientDump("InsetsController#show", 1112 mHost.getInputMethodManager(), null /* icProto */); 1113 Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApiToImeReady", 0); 1114 Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.showRequestFromIme", 0); 1115 } else { 1116 Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0); 1117 Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.showRequestFromApiToImeReady", 0); 1118 } 1119 if (!Flags.refactorInsetsController()) { 1120 // Handle pending request ready in case there was one set. 1121 if (fromIme && mPendingImeControlRequest != null) { 1122 if ((types & Type.ime()) != 0) { 1123 ImeTracker.forLatency().onShown(statsToken, ActivityThread::currentApplication); 1124 } 1125 handlePendingControlRequest(statsToken); 1126 return; 1127 } 1128 } 1129 1130 // TODO: Support a ResultReceiver for IME. 1131 // TODO(b/123718661): Make show() work for multi-session IME. 1132 @InsetsType int typesReady = 0; 1133 final boolean imeVisible = mState.isSourceOrDefaultVisible( 1134 mImeSourceConsumer.getId(), ime()); 1135 for (@InsetsType int type = FIRST; type <= LAST; type = type << 1) { 1136 if ((types & type) == 0) { 1137 continue; 1138 } 1139 @AnimationType final int animationType = getAnimationType(type); 1140 final boolean requestedVisible = (type & mRequestedVisibleTypes) != 0; 1141 final boolean isIme = type == ime(); 1142 var alreadyVisible = requestedVisible && (!isIme || imeVisible) 1143 && animationType == ANIMATION_TYPE_NONE; 1144 var alreadyAnimatingShow = animationType == ANIMATION_TYPE_SHOW; 1145 if (alreadyVisible || alreadyAnimatingShow) { 1146 // no-op: already shown or animating in (because window visibility is 1147 // applied before starting animation). 1148 if (DEBUG) Log.d(TAG, String.format( 1149 "show ignored for type: %d animType: %d requestedVisible: %s", 1150 type, animationType, requestedVisible)); 1151 if (isIme) { 1152 ImeTracker.forLogging().onCancelled(statsToken, 1153 ImeTracker.PHASE_CLIENT_APPLY_ANIMATION); 1154 } 1155 continue; 1156 } 1157 if (!Flags.refactorInsetsController()) { 1158 if (fromIme && animationType == ANIMATION_TYPE_USER 1159 && !mIsPredictiveBackImeHideAnimInProgress) { 1160 // App is already controlling the IME, don't cancel it. 1161 if (isIme) { 1162 ImeTracker.forLogging().onFailed( 1163 statsToken, ImeTracker.PHASE_CLIENT_APPLY_ANIMATION); 1164 } 1165 continue; 1166 } 1167 } 1168 if (isIme) { 1169 ImeTracker.forLogging().onProgress( 1170 statsToken, ImeTracker.PHASE_CLIENT_APPLY_ANIMATION); 1171 } 1172 typesReady |= type; 1173 } 1174 if (DEBUG) Log.d(TAG, "show typesReady: " + typesReady); 1175 if ((Flags.refactorInsetsController() || fromIme) && (typesReady & Type.ime()) != 0) { 1176 ImeTracker.forLatency().onShown(statsToken, ActivityThread::currentApplication); 1177 } 1178 applyAnimation(typesReady, true /* show */, fromIme, statsToken); 1179 } 1180 1181 /** 1182 * Handle the {@link #mPendingImeControlRequest} when: 1183 * <ul> 1184 * <li> The IME insets is ready to show. 1185 * <li> The IME insets has being requested invisible. 1186 * </ul> 1187 */ handlePendingControlRequest(@ullable ImeTracker.Token statsToken)1188 private void handlePendingControlRequest(@Nullable ImeTracker.Token statsToken) { 1189 PendingControlRequest pendingRequest = mPendingImeControlRequest; 1190 mPendingImeControlRequest = null; 1191 mHandler.removeCallbacks(mPendingControlTimeout); 1192 1193 // We are about to playing the default animation. Passing a null frame indicates the 1194 // controlled types should be animated regardless of the frame. 1195 controlAnimationUnchecked( 1196 pendingRequest.types, pendingRequest.cancellationSignal, 1197 pendingRequest.listener, null /* frame */, 1198 true /* fromIme */, pendingRequest.durationMs, pendingRequest.interpolator, 1199 pendingRequest.animationType, 1200 pendingRequest.layoutInsetsDuringAnimation, 1201 pendingRequest.useInsetsAnimationThread, statsToken); 1202 } 1203 1204 @Override hide(@nsetsType int types)1205 public void hide(@InsetsType int types) { 1206 hide(types, false /* fromIme */, null /* statsToken */); 1207 } 1208 1209 @VisibleForTesting hide(@nsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken)1210 public void hide(@InsetsType int types, boolean fromIme, 1211 @Nullable ImeTracker.Token statsToken) { 1212 if ((types & ime()) != 0) { 1213 Log.d(TAG, "hide(ime(), fromIme=" + fromIme + ")"); 1214 1215 if (statsToken == null) { 1216 statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE, 1217 ImeTracker.ORIGIN_CLIENT, 1218 SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API, 1219 mHost.isHandlingPointerEvent() /* fromUser */); 1220 } 1221 } 1222 if (fromIme) { 1223 ImeTracing.getInstance().triggerClientDump("InsetsController#hide", 1224 mHost.getInputMethodManager(), null /* icProto */); 1225 Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.hideRequestFromIme", 0); 1226 } else { 1227 Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.hideRequestFromApi", 0); 1228 } 1229 @InsetsType int typesReady = 0; 1230 boolean hasImeRequestedHidden = false; 1231 final boolean hadPendingImeControlRequest = mPendingImeControlRequest != null; 1232 for (@InsetsType int type = FIRST; type <= LAST; type = type << 1) { 1233 if ((types & type) == 0) { 1234 continue; 1235 } 1236 final boolean isImeAnimation = type == ime(); 1237 if (Flags.refactorInsetsController()) { 1238 if (isImeAnimation) { 1239 // When the IME is requested to be hidden, but already hidden, we don't show 1240 // an animation again (mRequestedVisibleTypes are reported at the end of the IME 1241 // hide animation but set at the beginning) 1242 if ((mRequestedVisibleTypes & ime()) == 0) { 1243 continue; 1244 } 1245 } 1246 } 1247 @AnimationType final int animationType = getAnimationType(type); 1248 final boolean requestedVisible = (type & mRequestedVisibleTypes) != 0; 1249 if (mPendingImeControlRequest != null && !requestedVisible) { 1250 // Remove the hide insets type from the pending show request. 1251 mPendingImeControlRequest.types &= ~type; 1252 if (mPendingImeControlRequest.types == 0) { 1253 abortPendingImeControlRequest(); 1254 } 1255 } 1256 if (!Flags.refactorInsetsController()) { 1257 if (isImeAnimation && !requestedVisible && animationType == ANIMATION_TYPE_NONE) { 1258 hasImeRequestedHidden = true; 1259 // Ensure to request hide IME in case there is any pending requested visible 1260 // being applied from setControl when receiving the insets control. 1261 if (hadPendingImeControlRequest 1262 || getImeSourceConsumer().isRequestedVisibleAwaitingControl()) { 1263 getImeSourceConsumer().requestHide(fromIme, statsToken); 1264 } 1265 } 1266 } 1267 if (!requestedVisible && animationType == ANIMATION_TYPE_NONE 1268 || animationType == ANIMATION_TYPE_HIDE || (animationType 1269 == ANIMATION_TYPE_USER && mIsPredictiveBackImeHideAnimInProgress)) { 1270 // no-op: already hidden or animating out (because window visibility is 1271 // applied before starting animation). 1272 if (isImeAnimation) { 1273 ImeTracker.forLogging().onCancelled(statsToken, 1274 ImeTracker.PHASE_CLIENT_APPLY_ANIMATION); 1275 } 1276 continue; 1277 } 1278 if (isImeAnimation) { 1279 ImeTracker.forLogging().onProgress( 1280 statsToken, ImeTracker.PHASE_CLIENT_APPLY_ANIMATION); 1281 } 1282 typesReady |= type; 1283 } 1284 if (hasImeRequestedHidden && mPendingImeControlRequest != null) { 1285 // Handle the pending show request for other insets types since the IME insets 1286 // has being requested hidden. 1287 handlePendingControlRequest(statsToken); 1288 getImeSourceConsumer().removeSurface(); 1289 } 1290 applyAnimation(typesReady, false /* show */, fromIme, statsToken); 1291 } 1292 1293 @Override controlWindowInsetsAnimation(@nsetsType int types, long durationMillis, @Nullable Interpolator interpolator, @Nullable CancellationSignal cancellationSignal, @NonNull WindowInsetsAnimationControlListener listener)1294 public void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis, 1295 @Nullable Interpolator interpolator, 1296 @Nullable CancellationSignal cancellationSignal, 1297 @NonNull WindowInsetsAnimationControlListener listener) { 1298 controlWindowInsetsAnimation(types, cancellationSignal, listener, 1299 false /* fromIme */, durationMillis, interpolator, ANIMATION_TYPE_USER, 1300 false /* fromPredictiveBack */); 1301 } 1302 1303 @VisibleForTesting(visibility = PACKAGE) controlWindowInsetsAnimation(@nsetsType int types, @Nullable CancellationSignal cancellationSignal, WindowInsetsAnimationControlListener listener, boolean fromIme, long durationMs, @Nullable Interpolator interpolator, @AnimationType int animationType, boolean fromPredictiveBack)1304 public void controlWindowInsetsAnimation(@InsetsType int types, 1305 @Nullable CancellationSignal cancellationSignal, 1306 WindowInsetsAnimationControlListener listener, 1307 boolean fromIme, long durationMs, @Nullable Interpolator interpolator, 1308 @AnimationType int animationType, boolean fromPredictiveBack) { 1309 if ((mState.calculateUncontrollableInsetsFromFrame(mFrame) & types) != 0) { 1310 listener.onCancelled(null); 1311 return; 1312 } 1313 if (fromIme) { 1314 ImeTracing.getInstance().triggerClientDump( 1315 "InsetsController#controlWindowInsetsAnimation", 1316 mHost.getInputMethodManager(), null /* icProto */); 1317 } 1318 1319 // TODO(b/342111149): Create statsToken here once ImeTracker#onStart becomes async. 1320 controlAnimationUnchecked(types, cancellationSignal, listener, mFrame, fromIme, durationMs, 1321 interpolator, animationType, 1322 getLayoutInsetsDuringAnimationMode(types, fromPredictiveBack), 1323 false /* useInsetsAnimationThread */, null); 1324 } 1325 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, @Nullable ImeTracker.Token statsToken)1326 private void controlAnimationUnchecked(@InsetsType int types, 1327 @Nullable CancellationSignal cancellationSignal, 1328 WindowInsetsAnimationControlListener listener, @Nullable Rect frame, boolean fromIme, 1329 long durationMs, Interpolator interpolator, 1330 @AnimationType int animationType, 1331 @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation, 1332 boolean useInsetsAnimationThread, @Nullable ImeTracker.Token statsToken) { 1333 final boolean visible = layoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN; 1334 1335 // Basically, we accept the requested visibilities from the upstream callers... 1336 setRequestedVisibleTypes(visible ? types : 0, types); 1337 1338 // However, we might reject the request in some cases, such as delaying showing IME or 1339 // rejecting showing IME. 1340 controlAnimationUncheckedInner(types, cancellationSignal, listener, frame, fromIme, 1341 durationMs, interpolator, animationType, layoutInsetsDuringAnimation, 1342 useInsetsAnimationThread, statsToken); 1343 1344 // We are finishing setting the requested visible types. Report them to the server 1345 // and/or the app. 1346 reportRequestedVisibleTypes(); 1347 } 1348 controlAnimationUncheckedInner(@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, @Nullable ImeTracker.Token statsToken)1349 private void controlAnimationUncheckedInner(@InsetsType int types, 1350 @Nullable CancellationSignal cancellationSignal, 1351 WindowInsetsAnimationControlListener listener, @Nullable Rect frame, boolean fromIme, 1352 long durationMs, Interpolator interpolator, 1353 @AnimationType int animationType, 1354 @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation, 1355 boolean useInsetsAnimationThread, @Nullable ImeTracker.Token statsToken) { 1356 if ((types & mTypesBeingCancelled) != 0) { 1357 final boolean monitoredAnimation = 1358 animationType == ANIMATION_TYPE_SHOW || animationType == ANIMATION_TYPE_HIDE; 1359 if (monitoredAnimation && (types & Type.ime()) != 0) { 1360 if (animationType == ANIMATION_TYPE_SHOW) { 1361 ImeTracker.forLatency().onShowCancelled(statsToken, 1362 ImeTracker.PHASE_CLIENT_ANIMATION_CANCEL, 1363 ActivityThread::currentApplication); 1364 } else { 1365 ImeTracker.forLatency().onHideCancelled(statsToken, 1366 ImeTracker.PHASE_CLIENT_ANIMATION_CANCEL, 1367 ActivityThread::currentApplication); 1368 } 1369 ImeTracker.forLogging().onCancelled(statsToken, 1370 ImeTracker.PHASE_CLIENT_CONTROL_ANIMATION); 1371 } 1372 throw new IllegalStateException("Cannot start a new insets animation of " 1373 + Type.toString(types) 1374 + " while an existing " + Type.toString(mTypesBeingCancelled) 1375 + " is being cancelled."); 1376 } 1377 ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_CONTROL_ANIMATION); 1378 if (types == 0) { 1379 // nothing to animate. 1380 listener.onCancelled(null); 1381 if (DEBUG) Log.d(TAG, "no types to animate in controlAnimationUnchecked"); 1382 Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0); 1383 Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApiToImeReady", 0); 1384 return; 1385 } 1386 if (DEBUG) Log.d(TAG, "controlAnimation types: " + types); 1387 mLastStartedAnimTypes |= types; 1388 1389 final SparseArray<InsetsSourceControl> controls = new SparseArray<>(); 1390 @InsetsType int typesReady; 1391 1392 if (Flags.refactorInsetsController()) { 1393 // Ime will not be contained in typesReady nor in controls, if we don't have a leash 1394 Pair<Integer, Integer> typesReadyPair = collectSourceControlsV2(types, controls); 1395 typesReady = typesReadyPair.first; 1396 @InsetsType int typesWithoutLeash = typesReadyPair.second; 1397 if (animationType == ANIMATION_TYPE_USER) { 1398 // When using an app-driven animation, the IME won't have a leash (because the 1399 // window isn't created yet). If we have a control, but no leash, defers the 1400 // request until the leash gets created. 1401 // The mRequestedVisibleTypes were set just before, so we check the currently 1402 // visible types 1403 if ((types & ime()) != 0 && (types & typesWithoutLeash) != 0) { 1404 // If we have control but no leash for any of the controlling sources, we 1405 // wait until the leashes are ready. Thus, creating a PendingControlRequest 1406 // is always for showing, not hiding. 1407 // TODO (b/323319146) remove layoutInsetsDuringAnimation from 1408 // PendingControlRequest, as it is now only used for showing 1409 final PendingControlRequest request = new PendingControlRequest(types, 1410 listener, durationMs, 1411 interpolator, animationType, LAYOUT_INSETS_DURING_ANIMATION_SHOWN, 1412 cancellationSignal, false /* useInsetsAnimationThread */); 1413 mPendingImeControlRequest = request; 1414 // only add a timeout when the control is not currently showing 1415 mHandler.postDelayed(mPendingControlTimeout, PENDING_CONTROL_TIMEOUT_MS); 1416 1417 if (DEBUG) Log.d(TAG, "Ime not ready. Create pending request"); 1418 if (cancellationSignal != null) { 1419 cancellationSignal.setOnCancelListener(() -> { 1420 if (mPendingImeControlRequest == request) { 1421 if (DEBUG) { 1422 Log.d(TAG, "Cancellation signal abortPendingImeControlRequest"); 1423 } 1424 abortPendingImeControlRequest(); 1425 } 1426 }); 1427 } 1428 } 1429 // We need to wait until all types are ready 1430 if (typesReady != types) { 1431 return; 1432 } 1433 } 1434 } else { 1435 Pair<Integer, Boolean> typesReadyPair = collectSourceControls( 1436 fromIme, types, controls, animationType, statsToken); 1437 typesReady = typesReadyPair.first; 1438 boolean imeReady = typesReadyPair.second; 1439 if (DEBUG) { 1440 Log.d(TAG, TextUtils.formatSimple( 1441 "controlAnimationUnchecked, typesReady: %s imeReady: %s", typesReady, 1442 imeReady)); 1443 } 1444 if (!imeReady) { 1445 // IME isn't ready, all requested types will be animated once IME is ready 1446 abortPendingImeControlRequest(); 1447 final PendingControlRequest request = new PendingControlRequest(types, 1448 listener, durationMs, 1449 interpolator, animationType, layoutInsetsDuringAnimation, 1450 cancellationSignal, 1451 useInsetsAnimationThread); 1452 mPendingImeControlRequest = request; 1453 mHandler.postDelayed(mPendingControlTimeout, PENDING_CONTROL_TIMEOUT_MS); 1454 if (DEBUG) Log.d(TAG, "Ime not ready. Create pending request"); 1455 if (cancellationSignal != null) { 1456 cancellationSignal.setOnCancelListener(() -> { 1457 if (mPendingImeControlRequest == request) { 1458 if (DEBUG) { 1459 Log.d(TAG, "Cancellation signal abortPendingImeControlRequest"); 1460 } 1461 abortPendingImeControlRequest(); 1462 } 1463 }); 1464 } 1465 1466 // The leashes are copied, but they won't be used. 1467 releaseControls(controls); 1468 1469 // The requested visibilities should be delayed as well. Otherwise, we might 1470 // override the insets visibility before playing animation. 1471 setRequestedVisibleTypes(mReportedRequestedVisibleTypes, types); 1472 1473 Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0); 1474 if (!fromIme) { 1475 Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApiToImeReady", 0); 1476 } 1477 return; 1478 } 1479 } 1480 1481 if (typesReady == 0) { 1482 if (Flags.refactorInsetsController()) { 1483 // if no types are ready, we need to wait for receiving new controls 1484 Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0); 1485 listener.onCancelled(null); 1486 } else { 1487 if (DEBUG) Log.d(TAG, "No types ready. onCancelled()"); 1488 listener.onCancelled(null); 1489 Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0); 1490 if (!fromIme) { 1491 Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApiToImeReady", 0); 1492 } 1493 } 1494 return; 1495 } 1496 1497 if (Flags.refactorInsetsController()) { 1498 mCancelledForNewAnimationTypes = typesReady; 1499 cancelExistingControllers(typesReady); 1500 mCancelledForNewAnimationTypes = 0; 1501 } else { 1502 cancelExistingControllers(typesReady); 1503 } 1504 1505 final InsetsAnimationControlRunner runner = useInsetsAnimationThread 1506 ? new InsetsAnimationThreadControlRunner(controls, 1507 frame, mState, listener, typesReady, this, durationMs, interpolator, 1508 animationType, layoutInsetsDuringAnimation, mHost.getTranslator(), 1509 mHost.getHandler(), statsToken) 1510 : new InsetsAnimationControlImpl(controls, 1511 frame, mState, listener, typesReady, this, durationMs, interpolator, 1512 animationType, layoutInsetsDuringAnimation, mHost.getTranslator(), 1513 statsToken); 1514 if ((typesReady & WindowInsets.Type.ime()) != 0) { 1515 ImeTracing.getInstance().triggerClientDump("InsetsAnimationControlImpl", 1516 mHost.getInputMethodManager(), null /* icProto */); 1517 if (animationType == ANIMATION_TYPE_HIDE) { 1518 ImeTracker.forLatency().onHidden(statsToken, ActivityThread::currentApplication); 1519 } 1520 } 1521 ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_ANIMATION_RUNNING); 1522 if (mRunningAnimations.isEmpty()) { 1523 mHost.notifyAnimationRunningStateChanged(true); 1524 } 1525 mRunningAnimations.add(new RunningAnimation(runner, animationType)); 1526 if (DEBUG) Log.d(TAG, "Animation added to runner. useInsetsAnimationThread: " 1527 + useInsetsAnimationThread); 1528 if (cancellationSignal != null) { 1529 cancellationSignal.setOnCancelListener(() -> { 1530 cancelAnimation(runner, true /* invokeCallback */); 1531 }); 1532 } else { 1533 Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.pendingAnim", 0); 1534 } 1535 1536 if (Flags.refactorInsetsController()) { 1537 onAnimationStateChanged(typesReady, true /* running */); 1538 } else { 1539 onAnimationStateChanged(types, true /* running */); 1540 } 1541 1542 if (fromIme) { 1543 switch (animationType) { 1544 case ANIMATION_TYPE_SHOW: 1545 Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromIme", 0); 1546 break; 1547 case ANIMATION_TYPE_HIDE: 1548 Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.hideRequestFromIme", 0); 1549 break; 1550 } 1551 } else if (animationType == ANIMATION_TYPE_HIDE) { 1552 Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.hideRequestFromApi", 0); 1553 } 1554 } 1555 releaseControls(SparseArray<InsetsSourceControl> controls)1556 static void releaseControls(SparseArray<InsetsSourceControl> controls) { 1557 for (int i = controls.size() - 1; i >= 0; i--) { 1558 controls.valueAt(i).release(SurfaceControl::release); 1559 } 1560 } 1561 1562 // TODO(b/242962223): Make this setter restrictive. 1563 @Override setSystemDrivenInsetsAnimationLoggingListener( @ullable WindowInsetsAnimationControlListener listener)1564 public void setSystemDrivenInsetsAnimationLoggingListener( 1565 @Nullable WindowInsetsAnimationControlListener listener) { 1566 mLoggingListener = listener; 1567 } 1568 1569 /** 1570 * @return Pair of (types ready to animate, IME ready to animate). 1571 */ collectSourceControls(boolean fromIme, @InsetsType int types, SparseArray<InsetsSourceControl> controls, @AnimationType int animationType, @Nullable ImeTracker.Token statsToken)1572 private Pair<Integer, Boolean> collectSourceControls(boolean fromIme, @InsetsType int types, 1573 SparseArray<InsetsSourceControl> controls, @AnimationType int animationType, 1574 @Nullable ImeTracker.Token statsToken) { 1575 ImeTracker.forLogging().onProgress(statsToken, 1576 ImeTracker.PHASE_CLIENT_COLLECT_SOURCE_CONTROLS); 1577 1578 @InsetsType int typesReady = 0; 1579 boolean imeReady = true; 1580 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { 1581 final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); 1582 if ((consumer.getType() & types) == 0) { 1583 continue; 1584 } 1585 boolean show = animationType == ANIMATION_TYPE_SHOW 1586 || animationType == ANIMATION_TYPE_USER; 1587 boolean canRun = true; 1588 if (show) { 1589 // Show request 1590 switch(consumer.requestShow(fromIme, statsToken)) { 1591 case ShowResult.SHOW_IMMEDIATELY: 1592 break; 1593 case ShowResult.IME_SHOW_DELAYED: 1594 imeReady = false; 1595 if (DEBUG) Log.d(TAG, "requestShow IME_SHOW_DELAYED"); 1596 break; 1597 case ShowResult.IME_SHOW_FAILED: 1598 if (WARN) Log.w(TAG, "requestShow IME_SHOW_FAILED. fromIme: " 1599 + fromIme); 1600 // IME cannot be shown (since it didn't have focus), proceed 1601 // with animation of other types. 1602 canRun = false; 1603 1604 // Reject the show request. 1605 setRequestedVisibleTypes(0 /* visibleTypes */, consumer.getType()); 1606 break; 1607 } 1608 } else { 1609 consumer.requestHide(fromIme, statsToken); 1610 } 1611 if (!canRun) { 1612 if (WARN) Log.w(TAG, String.format( 1613 "collectSourceControls can't continue show for type: %s fromIme: %b", 1614 WindowInsets.Type.toString(consumer.getType()), fromIme)); 1615 continue; 1616 } 1617 final InsetsSourceControl control = consumer.getControl(); 1618 if (control != null 1619 && (control.getLeash() != null || control.getId() == ID_IME_CAPTION_BAR)) { 1620 controls.put(control.getId(), new InsetsSourceControl(control)); 1621 typesReady |= consumer.getType(); 1622 } else if (fromIme) { 1623 Log.w(TAG, "collectSourceControls can't continue for type: ime," 1624 + " fromIme: true requires a control with a leash but we have " 1625 + ((control == null) 1626 ? "control: null" 1627 : "control: non-null and control.getLeash(): null")); 1628 ImeTracker.forLogging().onFailed(statsToken, 1629 ImeTracker.PHASE_CLIENT_COLLECT_SOURCE_CONTROLS); 1630 } 1631 } 1632 return new Pair<>(typesReady, imeReady); 1633 } 1634 1635 /** 1636 * @return Pair of (types ready to animate, types that we have control for, but no leash). 1637 */ collectSourceControlsV2(@nsetsType int types, SparseArray<InsetsSourceControl> controls)1638 private Pair<Integer, Integer> collectSourceControlsV2(@InsetsType int types, 1639 SparseArray<InsetsSourceControl> controls) { 1640 @InsetsType int typesReady = 0; 1641 int typesWithoutLeash = 0; 1642 1643 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { 1644 final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); 1645 if ((consumer.getType() & types) == 0) { 1646 continue; 1647 } 1648 1649 final InsetsSourceControl control = consumer.getControl(); 1650 if (control != null) { 1651 if (control.getLeash() != null || control.getId() == ID_IME_CAPTION_BAR) { 1652 controls.put(control.getId(), new InsetsSourceControl(control)); 1653 typesReady |= consumer.getType(); 1654 } else { 1655 typesWithoutLeash |= consumer.getType(); 1656 } 1657 } 1658 } 1659 return new Pair<>(typesReady, typesWithoutLeash); 1660 } 1661 getLayoutInsetsDuringAnimationMode( @nsetsType int types, boolean fromPredictiveBack)1662 private @LayoutInsetsDuringAnimation int getLayoutInsetsDuringAnimationMode( 1663 @InsetsType int types, boolean fromPredictiveBack) { 1664 if (fromPredictiveBack) { 1665 // When insets are animated by predictive back, we want insets to be shown to prevent a 1666 // jump cut from shown to hidden at the start of the predictive back animation 1667 return LAYOUT_INSETS_DURING_ANIMATION_SHOWN; 1668 } 1669 // Generally, we want to layout the opposite of the current state. This is to make animation 1670 // callbacks easy to use: The can capture the layout values and then treat that as end-state 1671 // during the animation. 1672 // 1673 // However, if controlling multiple sources, we want to treat it as shown if any of the 1674 // types is currently hidden. 1675 return (mRequestedVisibleTypes & types) != types 1676 ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN 1677 : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN; 1678 } 1679 cancelExistingControllers(@nsetsType int types)1680 private void cancelExistingControllers(@InsetsType int types) { 1681 final int originalmTypesBeingCancelled = mTypesBeingCancelled; 1682 mTypesBeingCancelled |= types; 1683 try { 1684 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { 1685 InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner; 1686 if ((control.getTypes() & types) != 0) { 1687 cancelAnimation(control, true /* invokeCallback */); 1688 } 1689 } 1690 if ((types & ime()) != 0) { 1691 abortPendingImeControlRequest(); 1692 } 1693 } finally { 1694 mTypesBeingCancelled = originalmTypesBeingCancelled; 1695 } 1696 } 1697 abortPendingImeControlRequest()1698 private void abortPendingImeControlRequest() { 1699 if (mPendingImeControlRequest != null) { 1700 mPendingImeControlRequest.listener.onCancelled(null); 1701 mPendingImeControlRequest = null; 1702 mHandler.removeCallbacks(mPendingControlTimeout); 1703 if (DEBUG) Log.d(TAG, "abortPendingImeControlRequest"); 1704 } 1705 } 1706 1707 @VisibleForTesting 1708 @Override notifyFinished(InsetsAnimationControlRunner runner, boolean shown)1709 public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) { 1710 setRequestedVisibleTypes(shown ? runner.getTypes() : 0, runner.getTypes()); 1711 cancelAnimation(runner, false /* invokeCallback */); 1712 if (DEBUG) Log.d(TAG, "notifyFinished. shown: " + shown); 1713 if (runner.getAnimationType() == ANIMATION_TYPE_RESIZE) { 1714 // The resize animation doesn't show or hide the insets. We shouldn't change the 1715 // requested visibility. 1716 return; 1717 } 1718 final ImeTracker.Token statsToken = runner.getStatsToken(); 1719 if (runner.getAnimationType() == ANIMATION_TYPE_USER) { 1720 ImeTracker.forLogging().onUserFinished(statsToken, shown); 1721 } else if (shown) { 1722 ImeTracker.forLogging().onProgress(statsToken, 1723 ImeTracker.PHASE_CLIENT_ANIMATION_FINISHED_SHOW); 1724 ImeTracker.forLogging().onShown(statsToken); 1725 } else { 1726 ImeTracker.forLogging().onProgress(statsToken, 1727 ImeTracker.PHASE_CLIENT_ANIMATION_FINISHED_HIDE); 1728 ImeTracker.forLogging().onHidden(statsToken); 1729 } 1730 reportRequestedVisibleTypes(); 1731 } 1732 1733 @Override applySurfaceParams(final SyncRtSurfaceTransactionApplier.SurfaceParams... params)1734 public void applySurfaceParams(final SyncRtSurfaceTransactionApplier.SurfaceParams... params) { 1735 mHost.applySurfaceParams(params); 1736 } 1737 notifyControlRevoked(InsetsSourceConsumer consumer)1738 void notifyControlRevoked(InsetsSourceConsumer consumer) { 1739 final @InsetsType int type = consumer.getType(); 1740 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { 1741 InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner; 1742 control.notifyControlRevoked(type); 1743 if (control.getControllingTypes() == 0) { 1744 cancelAnimation(control, true /* invokeCallback */); 1745 } 1746 } 1747 if (type == ime()) { 1748 abortPendingImeControlRequest(); 1749 } 1750 if (consumer.getType() != ime()) { 1751 // IME consumer should always be there since we need to communicate with 1752 // InputMethodManager no matter we have the control or not. 1753 mSourceConsumers.remove(consumer.getId()); 1754 } 1755 } 1756 cancelAnimation(InsetsAnimationControlRunner control, boolean invokeCallback)1757 private void cancelAnimation(InsetsAnimationControlRunner control, boolean invokeCallback) { 1758 if (invokeCallback) { 1759 ImeTracker.forLogging().onCancelled(control.getStatsToken(), 1760 ImeTracker.PHASE_CLIENT_ANIMATION_CANCEL); 1761 control.cancel(); 1762 } else { 1763 // Succeeds if invokeCallback is false (i.e. when called from notifyFinished). 1764 ImeTracker.forLogging().onProgress(control.getStatsToken(), 1765 ImeTracker.PHASE_CLIENT_ANIMATION_CANCEL); 1766 } 1767 if (DEBUG) { 1768 Log.d(TAG, TextUtils.formatSimple( 1769 "cancelAnimation of types: %d, animType: %d, host: %s", 1770 control.getTypes(), control.getAnimationType(), mHost.getRootViewTitle())); 1771 } 1772 @InsetsType int removedTypes = 0; 1773 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { 1774 RunningAnimation runningAnimation = mRunningAnimations.get(i); 1775 if (runningAnimation.runner == control) { 1776 mRunningAnimations.remove(i); 1777 removedTypes = control.getTypes(); 1778 if (invokeCallback) { 1779 dispatchAnimationEnd(runningAnimation.runner.getAnimation()); 1780 } else { 1781 if (Flags.refactorInsetsController()) { 1782 if (removedTypes == ime() 1783 && control.getAnimationType() == ANIMATION_TYPE_HIDE) { 1784 if (mHost != null) { 1785 // if the (hide) animation is cancelled, the 1786 // requestedVisibleTypes should be reported at this point. 1787 reportRequestedVisibleTypes(); 1788 mHost.getInputMethodManager().removeImeSurface( 1789 mHost.getWindowToken()); 1790 } 1791 } 1792 } 1793 } 1794 break; 1795 } 1796 } 1797 if (mRunningAnimations.isEmpty()) { 1798 mHost.notifyAnimationRunningStateChanged(false); 1799 } 1800 onAnimationStateChanged(removedTypes, false /* running */); 1801 } 1802 onAnimationStateChanged(@nsetsType int types, boolean running)1803 void onAnimationStateChanged(@InsetsType int types, boolean running) { 1804 boolean insetsChanged = false; 1805 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { 1806 final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); 1807 if ((consumer.getType() & types) != 0) { 1808 insetsChanged |= consumer.onAnimationStateChanged(running); 1809 } 1810 } 1811 if (insetsChanged) { 1812 notifyVisibilityChanged(); 1813 } 1814 } 1815 applyLocalVisibilityOverride()1816 private void applyLocalVisibilityOverride() { 1817 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { 1818 final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); 1819 consumer.applyLocalVisibilityOverride(); 1820 } 1821 } 1822 getCancelledForNewAnimationTypes()1823 @InsetsType int getCancelledForNewAnimationTypes() { 1824 return mCancelledForNewAnimationTypes; 1825 } 1826 1827 @VisibleForTesting getSourceConsumer(int id, int type)1828 public @NonNull InsetsSourceConsumer getSourceConsumer(int id, int type) { 1829 InsetsSourceConsumer consumer = mSourceConsumers.get(id); 1830 if (consumer != null) { 1831 return consumer; 1832 } 1833 if (type == ime() && mImeSourceConsumer != null) { 1834 // WindowInsets.Type.ime() should be only provided by one source. 1835 mSourceConsumers.remove(mImeSourceConsumer.getId()); 1836 consumer = mImeSourceConsumer; 1837 consumer.setId(id); 1838 } else { 1839 consumer = mConsumerCreator.apply(this, id, type); 1840 } 1841 mSourceConsumers.put(id, consumer); 1842 return consumer; 1843 } 1844 1845 @VisibleForTesting getImeSourceConsumer()1846 public @NonNull InsetsSourceConsumer getImeSourceConsumer() { 1847 return mImeSourceConsumer; 1848 } 1849 notifyVisibilityChanged()1850 void notifyVisibilityChanged() { 1851 mHost.notifyInsetsChanged(); 1852 } 1853 1854 /** 1855 * @see ViewRootImpl#updateCompatSysUiVisibility(int, int, int) 1856 */ updateCompatSysUiVisibility()1857 public void updateCompatSysUiVisibility() { 1858 if (mCompatSysUiVisibilityStaled) { 1859 mCompatSysUiVisibilityStaled = false; 1860 mHost.updateCompatSysUiVisibility( 1861 // Treat non-existing types as controllable types for compatibility. 1862 mVisibleTypes, mRequestedVisibleTypes, mControllableTypes | ~mExistingTypes); 1863 } 1864 } 1865 1866 /** 1867 * Called when current window gains focus. 1868 */ onWindowFocusGained(boolean hasViewFocused)1869 public void onWindowFocusGained(boolean hasViewFocused) { 1870 mImeSourceConsumer.onWindowFocusGained(hasViewFocused); 1871 } 1872 1873 /** 1874 * Called when current window loses focus. 1875 */ onWindowFocusLost()1876 public void onWindowFocusLost() { 1877 mImeSourceConsumer.onWindowFocusLost(); 1878 } 1879 1880 @VisibleForTesting getAnimationType(@nsetsType int type)1881 public @AnimationType int getAnimationType(@InsetsType int type) { 1882 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { 1883 InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner; 1884 if (control.controlsType(type)) { 1885 return mRunningAnimations.get(i).type; 1886 } 1887 } 1888 return ANIMATION_TYPE_NONE; 1889 } 1890 1891 @VisibleForTesting(visibility = PACKAGE) setRequestedVisibleTypes(@nsetsType int visibleTypes, @InsetsType int mask)1892 public void setRequestedVisibleTypes(@InsetsType int visibleTypes, @InsetsType int mask) { 1893 final @InsetsType int requestedVisibleTypes = 1894 (mRequestedVisibleTypes & ~mask) | (visibleTypes & mask); 1895 if (mRequestedVisibleTypes != requestedVisibleTypes) { 1896 mRequestedVisibleTypes = requestedVisibleTypes; 1897 } 1898 } 1899 1900 /** 1901 * @return Types of currently running animations that are controlled by the user. 1902 */ computeUserAnimatingTypes()1903 public @InsetsType int computeUserAnimatingTypes() { 1904 int animatingTypes = 0; 1905 for (int i = 0; i < mRunningAnimations.size(); i++) { 1906 if (mRunningAnimations.get(i).runner.getAnimationType() == ANIMATION_TYPE_USER) { 1907 animatingTypes |= mRunningAnimations.get(i).runner.getTypes(); 1908 } 1909 } 1910 return animatingTypes; 1911 } 1912 computeAnimatingTypes()1913 private @InsetsType int computeAnimatingTypes() { 1914 int animatingTypes = 0; 1915 for (int i = 0; i < mRunningAnimations.size(); i++) { 1916 animatingTypes |= mRunningAnimations.get(i).runner.getTypes(); 1917 } 1918 return animatingTypes; 1919 } 1920 1921 /** 1922 * Called when finishing setting requested visible types or finishing setting controls. 1923 */ reportRequestedVisibleTypes()1924 private void reportRequestedVisibleTypes() { 1925 final @InsetsType int typesToReport; 1926 if (Flags.refactorInsetsController()) { 1927 // If the IME is currently animating out, it is still visible, therefore we only 1928 // report its requested visibility at the end of the animation, otherwise we would 1929 // lose the leash, and it would disappear during the animation 1930 // TODO(b/326377046) revisit this part and see if we can make it more general 1931 typesToReport = mRequestedVisibleTypes | (computeAnimatingTypes() & ime()); 1932 } else { 1933 typesToReport = mRequestedVisibleTypes; 1934 } 1935 1936 if (typesToReport != mReportedRequestedVisibleTypes) { 1937 final @InsetsType int diff = typesToReport ^ mReportedRequestedVisibleTypes; 1938 if (WindowInsets.Type.hasCompatSystemBars(diff)) { 1939 mCompatSysUiVisibilityStaled = true; 1940 } 1941 mReportedRequestedVisibleTypes = mRequestedVisibleTypes; 1942 mHost.updateRequestedVisibleTypes(mReportedRequestedVisibleTypes); 1943 } 1944 updateCompatSysUiVisibility(); 1945 } 1946 1947 @VisibleForTesting applyAnimation(@nsetsType final int types, boolean show, boolean fromIme, @Nullable ImeTracker.Token statsToken)1948 public void applyAnimation(@InsetsType final int types, boolean show, boolean fromIme, 1949 @Nullable ImeTracker.Token statsToken) { 1950 // TODO(b/166736352): We should only skip the animation of specific types, not all types. 1951 boolean skipAnim = false; 1952 if ((types & ime()) != 0) { 1953 final InsetsSourceControl imeControl = mImeSourceConsumer.getControl(); 1954 // Skip showing animation once that made by system for some reason. 1955 // (e.g. starting window with IME snapshot) 1956 if (imeControl != null) { 1957 skipAnim = imeControl.getAndClearSkipAnimationOnce() && show 1958 && mImeSourceConsumer.hasViewFocusWhenWindowFocusGain(); 1959 } 1960 } 1961 applyAnimation(types, show, fromIme, skipAnim, statsToken); 1962 } 1963 1964 @VisibleForTesting applyAnimation(@nsetsType final int types, boolean show, boolean fromIme, boolean skipAnim, @Nullable ImeTracker.Token statsToken)1965 public void applyAnimation(@InsetsType final int types, boolean show, boolean fromIme, 1966 boolean skipAnim, @Nullable ImeTracker.Token statsToken) { 1967 if (types == 0) { 1968 // nothing to animate. 1969 if (DEBUG) Log.d(TAG, "applyAnimation, nothing to animate. Stopping here"); 1970 Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0); 1971 if (!Flags.refactorInsetsController()) { 1972 if (!fromIme) { 1973 Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApiToImeReady", 0); 1974 } 1975 } 1976 return; 1977 } 1978 1979 boolean hasAnimationCallbacks = mHost.hasAnimationCallbacks(); 1980 final InternalAnimationControlListener listener = new InternalAnimationControlListener( 1981 show, hasAnimationCallbacks, types, mHost.getSystemBarsBehavior(), 1982 skipAnim || mAnimationsDisabled, mHost.dipToPx(FLOATING_IME_BOTTOM_INSET_DP), 1983 mLoggingListener, mJankContext); 1984 1985 // We are about to playing the default animation (show/hide). Passing a null frame indicates 1986 // the controlled types should be animated regardless of the frame. 1987 controlAnimationUnchecked( 1988 types, null /* cancellationSignal */, listener, null /* frame */, fromIme, 1989 listener.getDurationMs(), listener.getInsetsInterpolator(), 1990 show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE, 1991 show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN, 1992 !hasAnimationCallbacks /* useInsetsAnimationThread */, statsToken); 1993 } 1994 1995 /** 1996 * Cancel on-going animation to show/hide {@link InsetsType}. 1997 */ 1998 @VisibleForTesting cancelExistingAnimations()1999 public void cancelExistingAnimations() { 2000 cancelExistingControllers(all()); 2001 } 2002 dump(String prefix, PrintWriter pw)2003 void dump(String prefix, PrintWriter pw) { 2004 final String innerPrefix = prefix + " "; 2005 pw.println(prefix + "InsetsController:"); 2006 mState.dump(innerPrefix, pw); 2007 pw.println(innerPrefix + "mIsPredictiveBackImeHideAnimInProgress=" 2008 + mIsPredictiveBackImeHideAnimInProgress); 2009 } 2010 dumpDebug(ProtoOutputStream proto, long fieldId)2011 void dumpDebug(ProtoOutputStream proto, long fieldId) { 2012 final long token = proto.start(fieldId); 2013 mState.dumpDebug(proto, STATE); 2014 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { 2015 InsetsAnimationControlRunner runner = mRunningAnimations.get(i).runner; 2016 runner.dumpDebug(proto, CONTROL); 2017 } 2018 proto.end(token); 2019 } 2020 2021 @VisibleForTesting 2022 @Override 2023 public <T extends InsetsAnimationControlRunner & InternalInsetsAnimationController> startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types, WindowInsetsAnimation animation, Bounds bounds)2024 void startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types, 2025 WindowInsetsAnimation animation, Bounds bounds) { 2026 mHost.dispatchWindowInsetsAnimationPrepare(animation); 2027 mHost.addOnPreDrawRunnable(() -> { 2028 if (runner.isCancelled()) { 2029 if (WARN) Log.w(TAG, "startAnimation canceled before preDraw"); 2030 return; 2031 } 2032 Trace.asyncTraceBegin(TRACE_TAG_VIEW, 2033 "InsetsAnimation: " + WindowInsets.Type.toString(types), types); 2034 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { 2035 RunningAnimation runningAnimation = mRunningAnimations.get(i); 2036 if (runningAnimation.runner == runner) { 2037 runningAnimation.startDispatched = true; 2038 } 2039 } 2040 Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.pendingAnim", 0); 2041 mHost.dispatchWindowInsetsAnimationStart(animation, bounds); 2042 mStartingAnimation = true; 2043 if (runner.getAnimationType() == ANIMATION_TYPE_USER) { 2044 ImeTracker.forLogging().onDispatched(runner.getStatsToken()); 2045 } 2046 runner.setReadyDispatched(true); 2047 listener.onReady(runner, types); 2048 mStartingAnimation = false; 2049 }); 2050 } 2051 2052 @VisibleForTesting dispatchAnimationEnd(WindowInsetsAnimation animation)2053 public void dispatchAnimationEnd(WindowInsetsAnimation animation) { 2054 Trace.asyncTraceEnd(TRACE_TAG_VIEW, 2055 "InsetsAnimation: " + WindowInsets.Type.toString(animation.getTypeMask()), 2056 animation.getTypeMask()); 2057 mHost.dispatchWindowInsetsAnimationEnd(animation); 2058 } 2059 2060 @VisibleForTesting 2061 @Override scheduleApplyChangeInsets(InsetsAnimationControlRunner runner)2062 public void scheduleApplyChangeInsets(InsetsAnimationControlRunner runner) { 2063 if (mStartingAnimation || runner.getAnimationType() == ANIMATION_TYPE_USER) { 2064 mAnimCallback.run(); 2065 mAnimCallbackScheduled = false; 2066 return; 2067 } 2068 if (!mAnimCallbackScheduled) { 2069 mHost.postInsetsAnimationCallback(mAnimCallback); 2070 mAnimCallbackScheduled = true; 2071 } 2072 } 2073 2074 @Override setSystemBarsAppearance(@ppearance int appearance, @Appearance int mask)2075 public void setSystemBarsAppearance(@Appearance int appearance, @Appearance int mask) { 2076 mAppearanceControlled |= mask; 2077 mHost.setSystemBarsAppearance(appearance, mask); 2078 } 2079 2080 @Override setSystemBarsAppearanceFromResource(@ppearance int appearance, @Appearance int mask)2081 public void setSystemBarsAppearanceFromResource(@Appearance int appearance, 2082 @Appearance int mask) { 2083 mAppearanceFromResource = (mAppearanceFromResource & ~mask) | (appearance & mask); 2084 2085 // Don't change the flags which are already controlled by setSystemBarsAppearance. 2086 mHost.setSystemBarsAppearance(appearance, mask & ~mAppearanceControlled); 2087 } 2088 2089 @Override getSystemBarsAppearance()2090 public @Appearance int getSystemBarsAppearance() { 2091 // We only return the requested appearance, not the implied one. 2092 return (mHost.getSystemBarsAppearance() & mAppearanceControlled) 2093 | (mAppearanceFromResource & ~mAppearanceControlled); 2094 } 2095 getAppearanceControlled()2096 public @Appearance int getAppearanceControlled() { 2097 return mAppearanceControlled; 2098 } 2099 2100 @Override setImeCaptionBarInsetsHeight(int height)2101 public void setImeCaptionBarInsetsHeight(int height) { 2102 if (!ENABLE_HIDE_IME_CAPTION_BAR) { 2103 return; 2104 } 2105 Rect newFrame = new Rect(mFrame.left, mFrame.bottom - height, mFrame.right, mFrame.bottom); 2106 InsetsSource source = mState.peekSource(ID_IME_CAPTION_BAR); 2107 if (mImeCaptionBarInsetsHeight != height 2108 || (source != null && !newFrame.equals(source.getFrame()))) { 2109 mImeCaptionBarInsetsHeight = height; 2110 if (mImeCaptionBarInsetsHeight != 0) { 2111 mState.getOrCreateSource(ID_IME_CAPTION_BAR, captionBar()) 2112 .setFrame(newFrame); 2113 getSourceConsumer(ID_IME_CAPTION_BAR, captionBar()).setControl( 2114 new InsetsSourceControl(ID_IME_CAPTION_BAR, captionBar(), 2115 null /* leash */, false /* initialVisible */, 2116 new Point(), Insets.NONE), 2117 new int[1], new int[1]); 2118 } else { 2119 mState.removeSource(ID_IME_CAPTION_BAR); 2120 InsetsSourceConsumer sourceConsumer = mSourceConsumers.get(ID_IME_CAPTION_BAR); 2121 if (sourceConsumer != null) { 2122 sourceConsumer.setControl(null, new int[1], new int[1]); 2123 } 2124 } 2125 mHost.notifyInsetsChanged(); 2126 } 2127 } 2128 2129 @Override setSystemBarsBehavior(@ehavior int behavior)2130 public void setSystemBarsBehavior(@Behavior int behavior) { 2131 mBehaviorControlled = true; 2132 mHost.setSystemBarsBehavior(behavior); 2133 } 2134 2135 @Override getSystemBarsBehavior()2136 public @Behavior int getSystemBarsBehavior() { 2137 if (!mBehaviorControlled) { 2138 // We only return the requested behavior, not the implied one. 2139 return BEHAVIOR_DEFAULT; 2140 } 2141 return mHost.getSystemBarsBehavior(); 2142 } 2143 isBehaviorControlled()2144 public boolean isBehaviorControlled() { 2145 return mBehaviorControlled; 2146 } 2147 2148 @Override setAnimationsDisabled(boolean disable)2149 public void setAnimationsDisabled(boolean disable) { 2150 mAnimationsDisabled = disable; 2151 } 2152 calculateControllableTypes()2153 private @InsetsType int calculateControllableTypes() { 2154 @InsetsType int result = 0; 2155 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { 2156 InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); 2157 InsetsSource source = mState.peekSource(consumer.getId()); 2158 if (consumer.getControl() != null && source != null) { 2159 result |= consumer.getType(); 2160 } 2161 } 2162 return result & ~mState.calculateUncontrollableInsetsFromFrame(mFrame); 2163 } 2164 2165 /** 2166 * @return The types that are now animating due to a listener invoking control/show/hide 2167 */ invokeControllableInsetsChangedListeners()2168 private @InsetsType int invokeControllableInsetsChangedListeners() { 2169 mHandler.removeCallbacks(mInvokeControllableInsetsChangedListeners); 2170 mLastStartedAnimTypes = 0; 2171 @InsetsType int types = calculateControllableTypes(); 2172 int size = mControllableInsetsChangedListeners.size(); 2173 for (int i = 0; i < size; i++) { 2174 mControllableInsetsChangedListeners.get(i).onControllableInsetsChanged(this, types); 2175 } 2176 return mLastStartedAnimTypes; 2177 } 2178 2179 @Override addOnControllableInsetsChangedListener( OnControllableInsetsChangedListener listener)2180 public void addOnControllableInsetsChangedListener( 2181 OnControllableInsetsChangedListener listener) { 2182 Objects.requireNonNull(listener); 2183 mControllableInsetsChangedListeners.add(listener); 2184 listener.onControllableInsetsChanged(this, calculateControllableTypes()); 2185 } 2186 2187 @Override removeOnControllableInsetsChangedListener( OnControllableInsetsChangedListener listener)2188 public void removeOnControllableInsetsChangedListener( 2189 OnControllableInsetsChangedListener listener) { 2190 Objects.requireNonNull(listener); 2191 mControllableInsetsChangedListeners.remove(listener); 2192 } 2193 2194 @Override releaseSurfaceControlFromRt(SurfaceControl sc)2195 public void releaseSurfaceControlFromRt(SurfaceControl sc) { 2196 mHost.releaseSurfaceControlFromRt(sc); 2197 } 2198 2199 @Override reportPerceptible(@nsetsType int types, boolean perceptible)2200 public void reportPerceptible(@InsetsType int types, boolean perceptible) { 2201 final int size = mSourceConsumers.size(); 2202 for (int i = 0; i < size; i++) { 2203 final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); 2204 if ((consumer.getType() & types) != 0) { 2205 consumer.onPerceptible(perceptible); 2206 } 2207 } 2208 } 2209 2210 @VisibleForTesting(visibility = PACKAGE) getHost()2211 public Host getHost() { 2212 return mHost; 2213 } 2214 } 2215