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