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