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