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