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 updateState(state); 622 623 boolean localStateChanged = !mState.equals(mLastDispatchedState, 624 true /* excludingCaptionInsets */, true /* excludeInvisibleIme */); 625 mLastDispatchedState.set(state, true /* copySources */); 626 627 applyLocalVisibilityOverride(); 628 if (localStateChanged) { 629 if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged, send state to WM: " + mState); 630 mHost.notifyInsetsChanged(); 631 updateRequestedState(); 632 } 633 return true; 634 } 635 updateState(InsetsState newState)636 private void updateState(InsetsState newState) { 637 mState.setDisplayFrame(newState.getDisplayFrame()); 638 @InsetsType int disabledUserAnimationTypes = 0; 639 @InsetsType int[] cancelledUserAnimationTypes = {0}; 640 for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) { 641 InsetsSource source = newState.peekSource(type); 642 if (source == null) continue; 643 @AnimationType int animationType = getAnimationType(type); 644 if (!source.isUserControllable()) { 645 @InsetsType int insetsType = toPublicType(type); 646 // The user animation is not allowed when visible frame is empty. 647 disabledUserAnimationTypes |= insetsType; 648 if (animationType == ANIMATION_TYPE_USER) { 649 // Existing user animation needs to be cancelled. 650 animationType = ANIMATION_TYPE_NONE; 651 cancelledUserAnimationTypes[0] |= insetsType; 652 } 653 } 654 getSourceConsumer(type).updateSource(source, animationType); 655 } 656 for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) { 657 InsetsSource source = mState.peekSource(type); 658 if (source == null) continue; 659 if (newState.peekSource(type) == null) { 660 mState.removeSource(type); 661 } 662 } 663 if (mCaptionInsetsHeight != 0) { 664 mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(mFrame.left, mFrame.top, 665 mFrame.right, mFrame.top + mCaptionInsetsHeight)); 666 } 667 668 updateDisabledUserAnimationTypes(disabledUserAnimationTypes); 669 670 if (cancelledUserAnimationTypes[0] != 0) { 671 mHandler.post(() -> show(cancelledUserAnimationTypes[0])); 672 } 673 } 674 updateDisabledUserAnimationTypes(@nsetsType int disabledUserAnimationTypes)675 private void updateDisabledUserAnimationTypes(@InsetsType int disabledUserAnimationTypes) { 676 @InsetsType int diff = mDisabledUserAnimationInsetsTypes ^ disabledUserAnimationTypes; 677 if (diff != 0) { 678 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { 679 InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); 680 if (consumer.getControl() != null 681 && (toPublicType(consumer.getType()) & diff) != 0) { 682 mHandler.removeCallbacks(mInvokeControllableInsetsChangedListeners); 683 mHandler.post(mInvokeControllableInsetsChangedListeners); 684 break; 685 } 686 } 687 mDisabledUserAnimationInsetsTypes = disabledUserAnimationTypes; 688 } 689 } 690 captionInsetsUnchanged()691 private boolean captionInsetsUnchanged() { 692 if (mState.peekSource(ITYPE_CAPTION_BAR) == null 693 && mCaptionInsetsHeight == 0) { 694 return true; 695 } 696 if (mState.peekSource(ITYPE_CAPTION_BAR) != null 697 && mCaptionInsetsHeight 698 == mState.peekSource(ITYPE_CAPTION_BAR).getFrame().height()) { 699 return true; 700 } 701 return false; 702 } 703 704 /** 705 * @see InsetsState#calculateInsets 706 */ 707 @VisibleForTesting calculateInsets(boolean isScreenRound, boolean alwaysConsumeSystemBars, DisplayCutout cutout, int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags)708 public WindowInsets calculateInsets(boolean isScreenRound, 709 boolean alwaysConsumeSystemBars, DisplayCutout cutout, 710 int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags) { 711 mLastLegacySoftInputMode = legacySoftInputMode; 712 mLastLegacyWindowFlags = legacyWindowFlags; 713 mLastLegacySystemUiFlags = legacySystemUiFlags; 714 mLastDisplayCutout = cutout; 715 mLastInsets = mState.calculateInsets(mFrame, null /* ignoringVisibilityState*/, 716 isScreenRound, alwaysConsumeSystemBars, cutout, 717 legacySoftInputMode, legacyWindowFlags, legacySystemUiFlags, 718 null /* typeSideMap */); 719 return mLastInsets; 720 } 721 722 /** 723 * @see InsetsState#calculateVisibleInsets(Rect, int) 724 */ calculateVisibleInsets(@oftInputModeFlags int softInputMode)725 public Rect calculateVisibleInsets(@SoftInputModeFlags int softInputMode) { 726 return mState.calculateVisibleInsets(mFrame, softInputMode); 727 } 728 729 /** 730 * Called when the server has dispatched us a new set of inset controls. 731 */ onControlsChanged(InsetsSourceControl[] activeControls)732 public void onControlsChanged(InsetsSourceControl[] activeControls) { 733 if (activeControls != null) { 734 for (InsetsSourceControl activeControl : activeControls) { 735 if (activeControl != null) { 736 // TODO(b/122982984): Figure out why it can be null. 737 mTmpControlArray.put(activeControl.getType(), activeControl); 738 } 739 } 740 } 741 742 boolean requestedStateStale = false; 743 final int[] showTypes = new int[1]; 744 final int[] hideTypes = new int[1]; 745 746 // Ensure to update all existing source consumers 747 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { 748 final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); 749 final InsetsSourceControl control = mTmpControlArray.get(consumer.getType()); 750 751 // control may be null, but we still need to update the control to null if it got 752 // revoked. 753 consumer.setControl(control, showTypes, hideTypes); 754 } 755 756 // Ensure to create source consumers if not available yet. 757 for (int i = mTmpControlArray.size() - 1; i >= 0; i--) { 758 final InsetsSourceControl control = mTmpControlArray.valueAt(i); 759 final @InternalInsetsType int type = control.getType(); 760 final InsetsSourceConsumer consumer = getSourceConsumer(type); 761 consumer.setControl(control, showTypes, hideTypes); 762 763 if (!requestedStateStale) { 764 final boolean requestedVisible = consumer.isRequestedVisible(); 765 766 // We might have changed our requested visibilities while we don't have the control, 767 // so we need to update our requested state once we have control. Otherwise, our 768 // requested state at the server side might be incorrect. 769 final boolean requestedVisibilityChanged = 770 requestedVisible != mRequestedState.getSourceOrDefaultVisibility(type); 771 772 // The IME client visibility will be reset by insets source provider while updating 773 // control, so if IME is requested visible, we need to send the request to server. 774 final boolean imeRequestedVisible = type == ITYPE_IME && requestedVisible; 775 776 requestedStateStale = requestedVisibilityChanged || imeRequestedVisible; 777 } 778 779 } 780 mTmpControlArray.clear(); 781 782 // Do not override any animations that the app started in the OnControllableInsetsChanged 783 // listeners. 784 int animatingTypes = invokeControllableInsetsChangedListeners(); 785 showTypes[0] &= ~animatingTypes; 786 hideTypes[0] &= ~animatingTypes; 787 788 if (showTypes[0] != 0) { 789 applyAnimation(showTypes[0], true /* show */, false /* fromIme */); 790 } 791 if (hideTypes[0] != 0) { 792 applyAnimation(hideTypes[0], false /* show */, false /* fromIme */); 793 } 794 if (requestedStateStale) { 795 updateRequestedState(); 796 } 797 } 798 799 @Override show(@nsetsType int types)800 public void show(@InsetsType int types) { 801 show(types, false /* fromIme */); 802 } 803 804 @VisibleForTesting show(@nsetsType int types, boolean fromIme)805 public void show(@InsetsType int types, boolean fromIme) { 806 // Handle pending request ready in case there was one set. 807 if (fromIme && mPendingImeControlRequest != null) { 808 PendingControlRequest pendingRequest = mPendingImeControlRequest; 809 mPendingImeControlRequest = null; 810 mHandler.removeCallbacks(mPendingControlTimeout); 811 controlAnimationUnchecked( 812 pendingRequest.types, pendingRequest.cancellationSignal, 813 pendingRequest.listener, mFrame, 814 true /* fromIme */, pendingRequest.durationMs, pendingRequest.interpolator, 815 pendingRequest.animationType, 816 pendingRequest.layoutInsetsDuringAnimation, 817 pendingRequest.useInsetsAnimationThread); 818 return; 819 } 820 821 // TODO: Support a ResultReceiver for IME. 822 // TODO(b/123718661): Make show() work for multi-session IME. 823 int typesReady = 0; 824 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types); 825 for (int i = internalTypes.size() - 1; i >= 0; i--) { 826 @InternalInsetsType int internalType = internalTypes.valueAt(i); 827 @AnimationType int animationType = getAnimationType(internalType); 828 InsetsSourceConsumer consumer = getSourceConsumer(internalType); 829 if (consumer.isRequestedVisible() && animationType == ANIMATION_TYPE_NONE 830 || animationType == ANIMATION_TYPE_SHOW) { 831 // no-op: already shown or animating in (because window visibility is 832 // applied before starting animation). 833 if (DEBUG) Log.d(TAG, String.format( 834 "show ignored for type: %d animType: %d requestedVisible: %s", 835 consumer.getType(), animationType, consumer.isRequestedVisible())); 836 continue; 837 } 838 if (fromIme && animationType == ANIMATION_TYPE_USER) { 839 // App is already controlling the IME, don't cancel it. 840 continue; 841 } 842 typesReady |= InsetsState.toPublicType(consumer.getType()); 843 } 844 if (DEBUG) Log.d(TAG, "show typesReady: " + typesReady); 845 applyAnimation(typesReady, true /* show */, fromIme); 846 } 847 848 @Override hide(@nsetsType int types)849 public void hide(@InsetsType int types) { 850 hide(types, false /* fromIme */); 851 } 852 hide(@nsetsType int types, boolean fromIme)853 void hide(@InsetsType int types, boolean fromIme) { 854 int typesReady = 0; 855 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types); 856 for (int i = internalTypes.size() - 1; i >= 0; i--) { 857 @InternalInsetsType int internalType = internalTypes.valueAt(i); 858 @AnimationType int animationType = getAnimationType(internalType); 859 InsetsSourceConsumer consumer = getSourceConsumer(internalType); 860 if (!consumer.isRequestedVisible() && animationType == ANIMATION_TYPE_NONE 861 || animationType == ANIMATION_TYPE_HIDE) { 862 // no-op: already hidden or animating out. 863 continue; 864 } 865 typesReady |= InsetsState.toPublicType(consumer.getType()); 866 } 867 applyAnimation(typesReady, false /* show */, fromIme /* fromIme */); 868 } 869 870 @Override controlWindowInsetsAnimation(@nsetsType int types, long durationMillis, @Nullable Interpolator interpolator, @Nullable CancellationSignal cancellationSignal, @NonNull WindowInsetsAnimationControlListener listener)871 public void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis, 872 @Nullable Interpolator interpolator, 873 @Nullable CancellationSignal cancellationSignal, 874 @NonNull WindowInsetsAnimationControlListener listener) { 875 controlWindowInsetsAnimation(types, cancellationSignal, listener, 876 false /* fromIme */, durationMillis, interpolator, ANIMATION_TYPE_USER); 877 } 878 controlWindowInsetsAnimation(@nsetsType int types, @Nullable CancellationSignal cancellationSignal, WindowInsetsAnimationControlListener listener, boolean fromIme, long durationMs, @Nullable Interpolator interpolator, @AnimationType int animationType)879 private void controlWindowInsetsAnimation(@InsetsType int types, 880 @Nullable CancellationSignal cancellationSignal, 881 WindowInsetsAnimationControlListener listener, 882 boolean fromIme, long durationMs, @Nullable Interpolator interpolator, 883 @AnimationType int animationType) { 884 if ((mState.calculateUncontrollableInsetsFromFrame(mFrame) & types) != 0) { 885 listener.onCancelled(null); 886 return; 887 } 888 889 controlAnimationUnchecked(types, cancellationSignal, listener, mFrame, fromIme, durationMs, 890 interpolator, animationType, getLayoutInsetsDuringAnimationMode(types), 891 false /* useInsetsAnimationThread */); 892 } 893 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)894 private void controlAnimationUnchecked(@InsetsType int types, 895 @Nullable CancellationSignal cancellationSignal, 896 WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme, 897 long durationMs, Interpolator interpolator, 898 @AnimationType int animationType, 899 @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation, 900 boolean useInsetsAnimationThread) { 901 if ((types & mTypesBeingCancelled) != 0) { 902 throw new IllegalStateException("Cannot start a new insets animation of " 903 + Type.toString(types) 904 + " while an existing " + Type.toString(mTypesBeingCancelled) 905 + " is being cancelled."); 906 } 907 if (animationType == ANIMATION_TYPE_USER) { 908 final @InsetsType int disabledTypes = types & mDisabledUserAnimationInsetsTypes; 909 if (DEBUG) Log.d(TAG, "user animation disabled types: " + disabledTypes); 910 types &= ~mDisabledUserAnimationInsetsTypes; 911 912 if (fromIme && (disabledTypes & ime()) != 0 913 && !mState.getSource(ITYPE_IME).isVisible()) { 914 // We've requested IMM to show IME, but the IME is not controllable. We need to 915 // cancel the request. 916 getSourceConsumer(ITYPE_IME).hide(true, animationType); 917 } 918 } 919 if (types == 0) { 920 // nothing to animate. 921 listener.onCancelled(null); 922 if (DEBUG) Log.d(TAG, "no types to animate in controlAnimationUnchecked"); 923 return; 924 } 925 cancelExistingControllers(types); 926 if (DEBUG) Log.d(TAG, "controlAnimation types: " + types); 927 mLastStartedAnimTypes |= types; 928 929 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types); 930 final SparseArray<InsetsSourceControl> controls = new SparseArray<>(); 931 932 Pair<Integer, Boolean> typesReadyPair = collectSourceControls( 933 fromIme, internalTypes, controls, animationType); 934 int typesReady = typesReadyPair.first; 935 boolean imeReady = typesReadyPair.second; 936 if (DEBUG) Log.d(TAG, String.format( 937 "controlAnimationUnchecked, typesReady: %s imeReady: %s", typesReady, imeReady)); 938 if (!imeReady) { 939 // IME isn't ready, all requested types will be animated once IME is ready 940 abortPendingImeControlRequest(); 941 final PendingControlRequest request = new PendingControlRequest(types, 942 listener, durationMs, 943 interpolator, animationType, layoutInsetsDuringAnimation, cancellationSignal, 944 useInsetsAnimationThread); 945 mPendingImeControlRequest = request; 946 mHandler.postDelayed(mPendingControlTimeout, PENDING_CONTROL_TIMEOUT_MS); 947 if (DEBUG) Log.d(TAG, "Ime not ready. Create pending request"); 948 if (cancellationSignal != null) { 949 cancellationSignal.setOnCancelListener(() -> { 950 if (mPendingImeControlRequest == request) { 951 if (DEBUG) Log.d(TAG, 952 "Cancellation signal abortPendingImeControlRequest"); 953 abortPendingImeControlRequest(); 954 } 955 }); 956 } 957 return; 958 } 959 960 if (typesReady == 0) { 961 if (DEBUG) Log.d(TAG, "No types ready. onCancelled()"); 962 listener.onCancelled(null); 963 return; 964 } 965 966 967 final InsetsAnimationControlRunner runner = useInsetsAnimationThread 968 ? new InsetsAnimationThreadControlRunner(controls, 969 frame, mState, listener, typesReady, this, durationMs, interpolator, 970 animationType, mHost.getHandler()) 971 : new InsetsAnimationControlImpl(controls, 972 frame, mState, listener, typesReady, this, durationMs, interpolator, 973 animationType); 974 mRunningAnimations.add(new RunningAnimation(runner, animationType)); 975 if (DEBUG) Log.d(TAG, "Animation added to runner. useInsetsAnimationThread: " 976 + useInsetsAnimationThread); 977 if (cancellationSignal != null) { 978 cancellationSignal.setOnCancelListener(() -> { 979 cancelAnimation(runner, true /* invokeCallback */); 980 }); 981 } 982 if (layoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN) { 983 showDirectly(types); 984 } else { 985 hideDirectly(types, false /* animationFinished */, animationType); 986 } 987 } 988 989 /** 990 * @return Pair of (types ready to animate, IME ready to animate). 991 */ collectSourceControls(boolean fromIme, ArraySet<Integer> internalTypes, SparseArray<InsetsSourceControl> controls, @AnimationType int animationType)992 private Pair<Integer, Boolean> collectSourceControls(boolean fromIme, 993 ArraySet<Integer> internalTypes, SparseArray<InsetsSourceControl> controls, 994 @AnimationType int animationType) { 995 int typesReady = 0; 996 boolean imeReady = true; 997 for (int i = internalTypes.size() - 1; i >= 0; i--) { 998 final InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i)); 999 boolean show = animationType == ANIMATION_TYPE_SHOW 1000 || animationType == ANIMATION_TYPE_USER; 1001 boolean canRun = false; 1002 if (show) { 1003 // Show request 1004 switch(consumer.requestShow(fromIme)) { 1005 case ShowResult.SHOW_IMMEDIATELY: 1006 canRun = true; 1007 break; 1008 case ShowResult.IME_SHOW_DELAYED: 1009 imeReady = false; 1010 if (DEBUG) Log.d(TAG, "requestShow IME_SHOW_DELAYED"); 1011 break; 1012 case ShowResult.IME_SHOW_FAILED: 1013 if (WARN) Log.w(TAG, "requestShow IME_SHOW_FAILED. fromIme: " 1014 + fromIme); 1015 // IME cannot be shown (since it didn't have focus), proceed 1016 // with animation of other types. 1017 break; 1018 } 1019 } else { 1020 // Hide request 1021 // TODO: Move notifyHidden() to beginning of the hide animation 1022 // (when visibility actually changes using hideDirectly()). 1023 if (!fromIme) { 1024 consumer.notifyHidden(); 1025 } 1026 canRun = true; 1027 } 1028 if (!canRun) { 1029 if (WARN) Log.w(TAG, String.format( 1030 "collectSourceControls can't continue show for type: %s fromIme: %b", 1031 InsetsState.typeToString(consumer.getType()), fromIme)); 1032 continue; 1033 } 1034 final InsetsSourceControl control = consumer.getControl(); 1035 if (control != null) { 1036 controls.put(consumer.getType(), new InsetsSourceControl(control)); 1037 typesReady |= toPublicType(consumer.getType()); 1038 } else if (animationType == ANIMATION_TYPE_SHOW) { 1039 if (DEBUG) Log.d(TAG, "collectSourceControls no control for show(). fromIme: " 1040 + fromIme); 1041 // We don't have a control at the moment. However, we still want to update requested 1042 // visibility state such that in case we get control, we can apply show animation. 1043 consumer.show(fromIme); 1044 } else if (animationType == ANIMATION_TYPE_HIDE) { 1045 consumer.hide(); 1046 } 1047 } 1048 return new Pair<>(typesReady, imeReady); 1049 } 1050 getLayoutInsetsDuringAnimationMode( @nsetsType int types)1051 private @LayoutInsetsDuringAnimation int getLayoutInsetsDuringAnimationMode( 1052 @InsetsType int types) { 1053 1054 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types); 1055 1056 // Generally, we want to layout the opposite of the current state. This is to make animation 1057 // callbacks easy to use: The can capture the layout values and then treat that as end-state 1058 // during the animation. 1059 // 1060 // However, if controlling multiple sources, we want to treat it as shown if any of the 1061 // types is currently hidden. 1062 for (int i = internalTypes.size() - 1; i >= 0; i--) { 1063 InsetsSourceConsumer consumer = mSourceConsumers.get(internalTypes.valueAt(i)); 1064 if (consumer == null) { 1065 continue; 1066 } 1067 if (!consumer.isRequestedVisible()) { 1068 return LAYOUT_INSETS_DURING_ANIMATION_SHOWN; 1069 } 1070 } 1071 return LAYOUT_INSETS_DURING_ANIMATION_HIDDEN; 1072 } 1073 cancelExistingControllers(@nsetsType int types)1074 private void cancelExistingControllers(@InsetsType int types) { 1075 final int originalmTypesBeingCancelled = mTypesBeingCancelled; 1076 mTypesBeingCancelled |= types; 1077 try { 1078 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { 1079 InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner; 1080 if ((control.getTypes() & types) != 0) { 1081 cancelAnimation(control, true /* invokeCallback */); 1082 } 1083 } 1084 if ((types & ime()) != 0) { 1085 abortPendingImeControlRequest(); 1086 } 1087 } finally { 1088 mTypesBeingCancelled = originalmTypesBeingCancelled; 1089 } 1090 } 1091 abortPendingImeControlRequest()1092 private void abortPendingImeControlRequest() { 1093 if (mPendingImeControlRequest != null) { 1094 mPendingImeControlRequest.listener.onCancelled(null); 1095 mPendingImeControlRequest = null; 1096 mHandler.removeCallbacks(mPendingControlTimeout); 1097 if (DEBUG) Log.d(TAG, "abortPendingImeControlRequest"); 1098 } 1099 } 1100 1101 @VisibleForTesting 1102 @Override notifyFinished(InsetsAnimationControlRunner runner, boolean shown)1103 public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) { 1104 cancelAnimation(runner, false /* invokeCallback */); 1105 if (DEBUG) Log.d(TAG, "notifyFinished. shown: " + shown); 1106 if (shown) { 1107 showDirectly(runner.getTypes()); 1108 } else { 1109 hideDirectly(runner.getTypes(), true /* animationFinished */, 1110 runner.getAnimationType()); 1111 } 1112 } 1113 1114 @Override applySurfaceParams(final SyncRtSurfaceTransactionApplier.SurfaceParams... params)1115 public void applySurfaceParams(final SyncRtSurfaceTransactionApplier.SurfaceParams... params) { 1116 mHost.applySurfaceParams(params); 1117 } 1118 notifyControlRevoked(InsetsSourceConsumer consumer)1119 void notifyControlRevoked(InsetsSourceConsumer consumer) { 1120 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { 1121 InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner; 1122 if ((control.getTypes() & toPublicType(consumer.getType())) != 0) { 1123 cancelAnimation(control, true /* invokeCallback */); 1124 } 1125 } 1126 if (consumer.getType() == ITYPE_IME) { 1127 abortPendingImeControlRequest(); 1128 } 1129 } 1130 cancelAnimation(InsetsAnimationControlRunner control, boolean invokeCallback)1131 private void cancelAnimation(InsetsAnimationControlRunner control, boolean invokeCallback) { 1132 if (DEBUG) Log.d(TAG, String.format("cancelAnimation of types: %d, animType: %d", 1133 control.getTypes(), control.getAnimationType())); 1134 if (invokeCallback) { 1135 control.cancel(); 1136 } 1137 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { 1138 RunningAnimation runningAnimation = mRunningAnimations.get(i); 1139 if (runningAnimation.runner == control) { 1140 mRunningAnimations.remove(i); 1141 ArraySet<Integer> types = toInternalType(control.getTypes()); 1142 for (int j = types.size() - 1; j >= 0; j--) { 1143 if (getSourceConsumer(types.valueAt(j)).notifyAnimationFinished()) { 1144 mHost.notifyInsetsChanged(); 1145 } 1146 } 1147 if (invokeCallback && runningAnimation.startDispatched) { 1148 dispatchAnimationEnd(runningAnimation.runner.getAnimation()); 1149 } 1150 break; 1151 } 1152 } 1153 } 1154 applyLocalVisibilityOverride()1155 private void applyLocalVisibilityOverride() { 1156 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { 1157 final InsetsSourceConsumer controller = mSourceConsumers.valueAt(i); 1158 controller.applyLocalVisibilityOverride(); 1159 } 1160 } 1161 1162 @VisibleForTesting getSourceConsumer(@nternalInsetsType int type)1163 public @NonNull InsetsSourceConsumer getSourceConsumer(@InternalInsetsType int type) { 1164 InsetsSourceConsumer controller = mSourceConsumers.get(type); 1165 if (controller != null) { 1166 return controller; 1167 } 1168 controller = mConsumerCreator.apply(this, type); 1169 mSourceConsumers.put(type, controller); 1170 return controller; 1171 } 1172 1173 @VisibleForTesting notifyVisibilityChanged()1174 public void notifyVisibilityChanged() { 1175 mHost.notifyInsetsChanged(); 1176 updateRequestedState(); 1177 } 1178 1179 /** 1180 * @see ViewRootImpl#updateCompatSysUiVisibility(int, boolean, boolean) 1181 */ updateCompatSysUiVisibility(@nternalInsetsType int type, boolean visible, boolean hasControl)1182 public void updateCompatSysUiVisibility(@InternalInsetsType int type, boolean visible, 1183 boolean hasControl) { 1184 mHost.updateCompatSysUiVisibility(type, visible, hasControl); 1185 } 1186 1187 /** 1188 * Called when current window gains focus. 1189 */ onWindowFocusGained()1190 public void onWindowFocusGained() { 1191 getSourceConsumer(ITYPE_IME).onWindowFocusGained(); 1192 } 1193 1194 /** 1195 * Called when current window loses focus. 1196 */ onWindowFocusLost()1197 public void onWindowFocusLost() { 1198 getSourceConsumer(ITYPE_IME).onWindowFocusLost(); 1199 } 1200 1201 /** 1202 * Used by {@link ImeInsetsSourceConsumer} when IME decides to be shown/hidden. 1203 * @hide 1204 */ 1205 @VisibleForTesting applyImeVisibility(boolean setVisible)1206 public void applyImeVisibility(boolean setVisible) { 1207 if (setVisible) { 1208 show(Type.IME, true /* fromIme */); 1209 } else { 1210 hide(Type.IME); 1211 } 1212 } 1213 1214 @VisibleForTesting getAnimationType(@nternalInsetsType int type)1215 public @AnimationType int getAnimationType(@InternalInsetsType int type) { 1216 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { 1217 InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner; 1218 if (control.controlsInternalType(type)) { 1219 return mRunningAnimations.get(i).type; 1220 } 1221 } 1222 return ANIMATION_TYPE_NONE; 1223 } 1224 1225 /** 1226 * Sends the local visibility state back to window manager if it is changed. 1227 */ updateRequestedState()1228 private void updateRequestedState() { 1229 boolean changed = false; 1230 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { 1231 final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); 1232 final @InternalInsetsType int type = consumer.getType(); 1233 if (type == ITYPE_CAPTION_BAR) { 1234 continue; 1235 } 1236 if (consumer.getControl() != null) { 1237 final InsetsSource localSource = mState.getSource(type); 1238 if (!localSource.equals(mRequestedState.peekSource(type))) { 1239 // Our requested state is stale. Update it here and send it to window manager. 1240 mRequestedState.addSource(new InsetsSource(localSource)); 1241 changed = true; 1242 } 1243 if (!localSource.equals(mLastDispatchedState.peekSource(type))) { 1244 // The server state is not what we expected. This can happen while we don't have 1245 // the control. Since we have the control now, we need to send our request again 1246 // to modify the server state. 1247 changed = true; 1248 } 1249 } 1250 } 1251 if (!changed) { 1252 return; 1253 } 1254 mHost.onInsetsModified(mRequestedState); 1255 } 1256 1257 @VisibleForTesting applyAnimation(@nsetsType final int types, boolean show, boolean fromIme)1258 public void applyAnimation(@InsetsType final int types, boolean show, boolean fromIme) { 1259 if (types == 0) { 1260 // nothing to animate. 1261 if (DEBUG) Log.d(TAG, "applyAnimation, nothing to animate"); 1262 return; 1263 } 1264 1265 boolean hasAnimationCallbacks = mHost.hasAnimationCallbacks(); 1266 final InternalAnimationControlListener listener = new InternalAnimationControlListener( 1267 show, hasAnimationCallbacks, types, mAnimationsDisabled, 1268 mHost.dipToPx(InternalAnimationControlListener.FLOATING_IME_BOTTOM_INSET)); 1269 1270 // Show/hide animations always need to be relative to the display frame, in order that shown 1271 // and hidden state insets are correct. 1272 controlAnimationUnchecked( 1273 types, null /* cancellationSignal */, listener, mState.getDisplayFrame(), fromIme, 1274 listener.getDurationMs(), listener.getInterpolator(), 1275 show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE, 1276 show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN, 1277 !hasAnimationCallbacks /* useInsetsAnimationThread */); 1278 1279 } 1280 hideDirectly( @nsetsType int types, boolean animationFinished, @AnimationType int animationType)1281 private void hideDirectly( 1282 @InsetsType int types, boolean animationFinished, @AnimationType int animationType) { 1283 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types); 1284 for (int i = internalTypes.size() - 1; i >= 0; i--) { 1285 getSourceConsumer(internalTypes.valueAt(i)).hide(animationFinished, animationType); 1286 } 1287 } 1288 showDirectly(@nsetsType int types)1289 private void showDirectly(@InsetsType int types) { 1290 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types); 1291 for (int i = internalTypes.size() - 1; i >= 0; i--) { 1292 getSourceConsumer(internalTypes.valueAt(i)).show(false /* fromIme */); 1293 } 1294 } 1295 1296 /** 1297 * Cancel on-going animation to show/hide {@link InsetsType}. 1298 */ 1299 @VisibleForTesting cancelExistingAnimations()1300 public void cancelExistingAnimations() { 1301 cancelExistingControllers(all()); 1302 } 1303 dump(String prefix, PrintWriter pw)1304 void dump(String prefix, PrintWriter pw) { 1305 pw.println(prefix); pw.println("InsetsController:"); 1306 mState.dump(prefix + " ", pw); 1307 } 1308 1309 @VisibleForTesting 1310 @Override startAnimation(InsetsAnimationControlImpl controller, WindowInsetsAnimationControlListener listener, int types, WindowInsetsAnimation animation, Bounds bounds)1311 public void startAnimation(InsetsAnimationControlImpl controller, 1312 WindowInsetsAnimationControlListener listener, int types, 1313 WindowInsetsAnimation animation, Bounds bounds) { 1314 mHost.dispatchWindowInsetsAnimationPrepare(animation); 1315 mHost.addOnPreDrawRunnable(() -> { 1316 if (controller.isCancelled()) { 1317 if (WARN) Log.w(TAG, "startAnimation canceled before preDraw"); 1318 return; 1319 } 1320 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, 1321 "InsetsAnimation: " + WindowInsets.Type.toString(types), types); 1322 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { 1323 RunningAnimation runningAnimation = mRunningAnimations.get(i); 1324 if (runningAnimation.runner == controller) { 1325 runningAnimation.startDispatched = true; 1326 } 1327 } 1328 mHost.dispatchWindowInsetsAnimationStart(animation, bounds); 1329 mStartingAnimation = true; 1330 controller.mReadyDispatched = true; 1331 listener.onReady(controller, types); 1332 mStartingAnimation = false; 1333 }); 1334 } 1335 1336 @VisibleForTesting dispatchAnimationEnd(WindowInsetsAnimation animation)1337 public void dispatchAnimationEnd(WindowInsetsAnimation animation) { 1338 Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, 1339 "InsetsAnimation: " + WindowInsets.Type.toString(animation.getTypeMask()), 1340 animation.getTypeMask()); 1341 mHost.dispatchWindowInsetsAnimationEnd(animation); 1342 } 1343 1344 @VisibleForTesting 1345 @Override scheduleApplyChangeInsets(InsetsAnimationControlRunner runner)1346 public void scheduleApplyChangeInsets(InsetsAnimationControlRunner runner) { 1347 if (mStartingAnimation || runner.getAnimationType() == ANIMATION_TYPE_USER) { 1348 mAnimCallback.run(); 1349 mAnimCallbackScheduled = false; 1350 return; 1351 } 1352 if (!mAnimCallbackScheduled) { 1353 mHost.postInsetsAnimationCallback(mAnimCallback); 1354 mAnimCallbackScheduled = true; 1355 } 1356 } 1357 1358 @Override setSystemBarsAppearance(@ppearance int appearance, @Appearance int mask)1359 public void setSystemBarsAppearance(@Appearance int appearance, @Appearance int mask) { 1360 mHost.setSystemBarsAppearance(appearance, mask); 1361 } 1362 1363 @Override getSystemBarsAppearance()1364 public @Appearance int getSystemBarsAppearance() { 1365 return mHost.getSystemBarsAppearance(); 1366 } 1367 1368 @Override setCaptionInsetsHeight(int height)1369 public void setCaptionInsetsHeight(int height) { 1370 mCaptionInsetsHeight = height; 1371 } 1372 1373 @Override setSystemBarsBehavior(@ehavior int behavior)1374 public void setSystemBarsBehavior(@Behavior int behavior) { 1375 mHost.setSystemBarsBehavior(behavior); 1376 } 1377 1378 @Override getSystemBarsBehavior()1379 public @Appearance int getSystemBarsBehavior() { 1380 return mHost.getSystemBarsBehavior(); 1381 } 1382 1383 @Override setAnimationsDisabled(boolean disable)1384 public void setAnimationsDisabled(boolean disable) { 1385 mAnimationsDisabled = disable; 1386 } 1387 calculateControllableTypes()1388 private @InsetsType int calculateControllableTypes() { 1389 @InsetsType int result = 0; 1390 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { 1391 InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); 1392 InsetsSource source = mState.peekSource(consumer.mType); 1393 if (consumer.getControl() != null && source != null && source.isUserControllable()) { 1394 result |= toPublicType(consumer.mType); 1395 } 1396 } 1397 return result & ~mState.calculateUncontrollableInsetsFromFrame(mFrame); 1398 } 1399 1400 /** 1401 * @return The types that are now animating due to a listener invoking control/show/hide 1402 */ invokeControllableInsetsChangedListeners()1403 private @InsetsType int invokeControllableInsetsChangedListeners() { 1404 mHandler.removeCallbacks(mInvokeControllableInsetsChangedListeners); 1405 mLastStartedAnimTypes = 0; 1406 @InsetsType int types = calculateControllableTypes(); 1407 int size = mControllableInsetsChangedListeners.size(); 1408 for (int i = 0; i < size; i++) { 1409 mControllableInsetsChangedListeners.get(i).onControllableInsetsChanged(this, types); 1410 } 1411 return mLastStartedAnimTypes; 1412 } 1413 1414 @Override addOnControllableInsetsChangedListener( OnControllableInsetsChangedListener listener)1415 public void addOnControllableInsetsChangedListener( 1416 OnControllableInsetsChangedListener listener) { 1417 Objects.requireNonNull(listener); 1418 mControllableInsetsChangedListeners.add(listener); 1419 listener.onControllableInsetsChanged(this, calculateControllableTypes()); 1420 } 1421 1422 @Override removeOnControllableInsetsChangedListener( OnControllableInsetsChangedListener listener)1423 public void removeOnControllableInsetsChangedListener( 1424 OnControllableInsetsChangedListener listener) { 1425 Objects.requireNonNull(listener); 1426 mControllableInsetsChangedListeners.remove(listener); 1427 } 1428 1429 @Override releaseSurfaceControlFromRt(SurfaceControl sc)1430 public void releaseSurfaceControlFromRt(SurfaceControl sc) { 1431 mHost.releaseSurfaceControlFromRt(sc); 1432 } 1433 1434 @Override reportPerceptible(int types, boolean perceptible)1435 public void reportPerceptible(int types, boolean perceptible) { 1436 final ArraySet<Integer> internalTypes = toInternalType(types); 1437 final int size = mSourceConsumers.size(); 1438 for (int i = 0; i < size; i++) { 1439 final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); 1440 if (internalTypes.contains(consumer.getType())) { 1441 consumer.onPerceptible(perceptible); 1442 } 1443 } 1444 } 1445 getHost()1446 Host getHost() { 1447 return mHost; 1448 } 1449 } 1450