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