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