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