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