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