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