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