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