blob: c6be91fa1bf5dd113b57add6925d2194dd3b0cb0 [file] [log] [blame]
Jorim Jaggif96c90a2018-09-26 16:55:15 +02001/*
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
17package android.view;
18
Yunfan Chenfae0aea2020-02-22 20:57:57 +090019import static android.view.InsetsState.ITYPE_CAPTION_BAR;
Tiger Huang332793b2019-10-29 23:21:27 +080020import static android.view.InsetsState.ITYPE_IME;
Jorim Jaggi33a21832020-04-06 14:15:46 +020021import static android.view.InsetsState.toInternalType;
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +010022import static android.view.InsetsState.toPublicType;
23import static android.view.WindowInsets.Type.all;
Jorim Jaggid7f10ed2020-01-08 21:41:55 +010024import static android.view.WindowInsets.Type.ime;
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -080025
Jorim Jaggi6d5c8012020-02-28 01:40:27 +010026import android.animation.AnimationHandler;
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -080027import android.animation.Animator;
28import android.animation.AnimatorListenerAdapter;
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -080029import android.animation.TypeEvaluator;
Jorim Jaggi5875cca2020-03-17 13:44:57 +010030import android.animation.ValueAnimator;
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -080031import android.annotation.IntDef;
Jorim Jaggib6030952018-10-23 18:31:52 +020032import android.annotation.NonNull;
Jorim Jaggidd3304e2020-01-20 17:24:51 +010033import android.annotation.Nullable;
Jorim Jaggi02a741f2018-12-12 17:40:19 -080034import android.graphics.Insets;
Jorim Jaggif96c90a2018-09-26 16:55:15 +020035import android.graphics.Rect;
Adrian Roos3406fb92020-02-10 18:38:59 -080036import android.os.CancellationSignal;
Jorim Jaggid7f10ed2020-01-08 21:41:55 +010037import android.os.Handler;
Adrian Roosc22eec92020-06-12 18:48:10 +020038import android.os.IBinder;
Jorim Jaggicb28ae62020-05-14 17:46:32 +020039import android.os.Trace;
Jorim Jaggib6030952018-10-23 18:31:52 +020040import android.util.ArraySet;
Taran Singh85661e32020-05-07 14:45:34 -070041import android.util.Log;
Tarandeep Singh46d59f02019-01-29 18:09:15 -080042import android.util.Pair;
Jorim Jaggib6030952018-10-23 18:31:52 +020043import android.util.SparseArray;
Tarandeep Singh46d59f02019-01-29 18:09:15 -080044import android.view.InsetsSourceConsumer.ShowResult;
Tiger Huang332793b2019-10-29 23:21:27 +080045import android.view.InsetsState.InternalInsetsType;
Jorim Jaggib6030952018-10-23 18:31:52 +020046import android.view.SurfaceControl.Transaction;
Tarandeep Singh46d59f02019-01-29 18:09:15 -080047import android.view.WindowInsets.Type;
Tiger Huang332793b2019-10-29 23:21:27 +080048import android.view.WindowInsets.Type.InsetsType;
Adrian Roosdb5b0c22020-02-12 15:05:27 -080049import android.view.WindowInsetsAnimation.Bounds;
Jorim Jaggi4e04eb22020-01-09 16:42:14 +010050import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
Jorim Jaggi79742592019-01-18 17:36:10 +010051import android.view.animation.Interpolator;
Jorim Jaggi5875cca2020-03-17 13:44:57 +010052import android.view.animation.LinearInterpolator;
Jorim Jaggi79742592019-01-18 17:36:10 +010053import android.view.animation.PathInterpolator;
Jorim Jaggibf87c152020-04-22 17:18:25 +020054import android.view.inputmethod.InputMethodManager;
Jorim Jaggib6030952018-10-23 18:31:52 +020055
56import com.android.internal.annotations.VisibleForTesting;
Jorim Jaggi6d5c8012020-02-28 01:40:27 +010057import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
Jorim Jaggif96c90a2018-09-26 16:55:15 +020058
59import java.io.PrintWriter;
Jorim Jaggia51168a2019-12-27 15:17:44 +010060import java.lang.annotation.Retention;
61import java.lang.annotation.RetentionPolicy;
Jorim Jaggi5bb571d2018-11-06 14:42:04 +010062import java.util.ArrayList;
Adrian Roosdb5b0c22020-02-12 15:05:27 -080063import java.util.Collections;
64import java.util.List;
Jorim Jaggied35b172020-03-06 00:13:57 +010065import java.util.Objects;
Jorim Jaggid7f10ed2020-01-08 21:41:55 +010066import java.util.function.BiFunction;
Jorim Jaggif96c90a2018-09-26 16:55:15 +020067
68/**
69 * Implements {@link WindowInsetsController} on the client.
Jorim Jaggib6030952018-10-23 18:31:52 +020070 * @hide
Jorim Jaggif96c90a2018-09-26 16:55:15 +020071 */
Yunfan Chen02abf552019-12-05 14:51:09 +090072public class InsetsController implements WindowInsetsController, InsetsAnimationControlCallbacks {
Jorim Jaggif96c90a2018-09-26 16:55:15 +020073
Adrian Roosa79b8852020-05-26 21:25:15 +020074 private int mTypesBeingCancelled;
75
Jorim Jaggibf87c152020-04-22 17:18:25 +020076 public interface Host {
77
78 Handler getHandler();
79
80 /**
81 * Notifies host that {@link InsetsController#getState()} has changed.
82 */
83 void notifyInsetsChanged();
84
85 void dispatchWindowInsetsAnimationPrepare(@NonNull WindowInsetsAnimation animation);
86 Bounds dispatchWindowInsetsAnimationStart(
87 @NonNull WindowInsetsAnimation animation, @NonNull Bounds bounds);
88 WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull WindowInsets insets,
89 @NonNull List<WindowInsetsAnimation> runningAnimations);
90 void dispatchWindowInsetsAnimationEnd(@NonNull WindowInsetsAnimation animation);
91
92 /**
93 * Requests host to apply surface params in synchronized manner.
94 */
95 void applySurfaceParams(final SyncRtSurfaceTransactionApplier.SurfaceParams... params);
96
97 /**
98 * @see ViewRootImpl#updateCompatSysUiVisibility(int, boolean, boolean)
99 */
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 */
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 */
113 boolean hasAnimationCallbacks();
114
115 /**
116 * @see WindowInsetsController#setSystemBarsAppearance
117 */
118 void setSystemBarsAppearance(@Appearance int appearance, @Appearance int mask);
119
120 /**
121 * @see WindowInsetsController#getSystemBarsAppearance()
122 */
123 @Appearance int getSystemBarsAppearance();
124
125 /**
126 * @see WindowInsetsController#setSystemBarsBehavior
127 */
128 void setSystemBarsBehavior(@Behavior int behavior);
129
130 /**
131 * @see WindowInsetsController#getSystemBarsBehavior
132 */
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 */
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 */
147 void addOnPreDrawRunnable(Runnable r);
148
149 /**
150 * Adds a runnbale to be executed during {@link Choreographer#CALLBACK_INSETS_ANIMATION}
151 * phase.
152 */
153 void postInsetsAnimationCallback(Runnable r);
154
155 /**
156 * Obtains {@link InputMethodManager} instance from host.
157 */
158 InputMethodManager getInputMethodManager();
Taran Singh85661e32020-05-07 14:45:34 -0700159
160 /**
161 * @return title of the rootView, if it has one.
162 * Note: this method is for debugging purposes only.
163 */
164 @Nullable
165 String getRootViewTitle();
Taran Singh42914102020-06-10 13:42:49 -0700166
167 /** @see ViewRootImpl#dipToPx */
168 int dipToPx(int dips);
Adrian Roosc22eec92020-06-12 18:48:10 +0200169
170 /**
171 * @return token associated with the host, if it has one.
172 */
173 @Nullable
174 IBinder getWindowToken();
Jorim Jaggibf87c152020-04-22 17:18:25 +0200175 }
176
Taran Singh85661e32020-05-07 14:45:34 -0700177 private static final String TAG = "InsetsController";
Jorim Jaggi79742592019-01-18 17:36:10 +0100178 private static final int ANIMATION_DURATION_SHOW_MS = 275;
179 private static final int ANIMATION_DURATION_HIDE_MS = 340;
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100180
181 private static final int ANIMATION_DURATION_SYNC_IME_MS = 285;
182 private static final int ANIMATION_DURATION_UNSYNC_IME_MS = 200;
183
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100184 private static final int PENDING_CONTROL_TIMEOUT_MS = 2000;
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800185
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100186 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);
Tarandeep Singh54554e22019-11-01 14:43:05 -0700194
Taran Singh85661e32020-05-07 14:45:34 -0700195 static final boolean DEBUG = false;
196 static final boolean WARN = false;
197
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800198 /**
Jorim Jaggia51168a2019-12-27 15:17:44 +0100199 * 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 */
Yunfan Chenb5d2db72019-12-06 15:43:43 +0900204 public static final int LAYOUT_INSETS_DURING_ANIMATION_SHOWN = 0;
Jorim Jaggia51168a2019-12-27 15:17:44 +0100205
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 */
Yunfan Chenb5d2db72019-12-06 15:43:43 +0900212 public static final int LAYOUT_INSETS_DURING_ANIMATION_HIDDEN = 1;
Jorim Jaggia51168a2019-12-27 15:17:44 +0100213
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
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +0100229 /** 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
Jorim Jaggia51168a2019-12-27 15:17:44 +0100251 /**
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800252 * Translation animation evaluator.
253 */
254 private static TypeEvaluator<Insets> sEvaluator = (fraction, startValue, endValue) -> Insets.of(
Jorim Jaggi956ca412019-01-07 14:49:14 +0100255 (int) (startValue.left + fraction * (endValue.left - startValue.left)),
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800256 (int) (startValue.top + fraction * (endValue.top - startValue.top)),
Jorim Jaggi956ca412019-01-07 14:49:14 +0100257 (int) (startValue.right + fraction * (endValue.right - startValue.right)),
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800258 (int) (startValue.bottom + fraction * (endValue.bottom - startValue.bottom)));
259
260 /**
Yunfan Chen4523d5d2020-01-16 16:54:00 +0900261 * 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 {
Tarandeep Singh54554e22019-11-01 14:43:05 -0700266
Taran Singh42914102020-06-10 13:42:49 -0700267 /** The amount IME will move up/down when animating in floating mode. */
268 protected static final int FLOATING_IME_BOTTOM_INSET = -80;
269
Tarandeep Singh54554e22019-11-01 14:43:05 -0700270 private WindowInsetsAnimationController mController;
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100271 private ValueAnimator mAnimator;
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100272 private final boolean mShow;
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100273 private final boolean mHasAnimationCallbacks;
274 private final @InsetsType int mRequestedTypes;
275 private final long mDurationMs;
Tiger Huangf2321192020-05-21 22:16:14 +0800276 private final boolean mDisable;
Taran Singh42914102020-06-10 13:42:49 -0700277 private final int mFloatingImeBottomInset;
Tarandeep Singh54554e22019-11-01 14:43:05 -0700278
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100279 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
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100289 public InternalAnimationControlListener(boolean show, boolean hasAnimationCallbacks,
Taran Singh42914102020-06-10 13:42:49 -0700290 int requestedTypes, boolean disable, int floatingImeBottomInset) {
Tarandeep Singh54554e22019-11-01 14:43:05 -0700291 mShow = show;
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100292 mHasAnimationCallbacks = hasAnimationCallbacks;
293 mRequestedTypes = requestedTypes;
294 mDurationMs = calculateDurationMs();
Tiger Huangf2321192020-05-21 22:16:14 +0800295 mDisable = disable;
Taran Singh42914102020-06-10 13:42:49 -0700296 mFloatingImeBottomInset = floatingImeBottomInset;
Tarandeep Singh54554e22019-11-01 14:43:05 -0700297 }
298
299 @Override
300 public void onReady(WindowInsetsAnimationController controller, int types) {
301 mController = controller;
Taran Singh85661e32020-05-07 14:45:34 -0700302 if (DEBUG) Log.d(TAG, "default animation onReady types: " + types);
Jorim Jaggia51168a2019-12-27 15:17:44 +0100303
Tiger Huangf2321192020-05-21 22:16:14 +0800304 if (mDisable) {
305 onAnimationFinish();
306 return;
307 }
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100308 mAnimator = ValueAnimator.ofFloat(0f, 1f);
309 mAnimator.setDuration(mDurationMs);
310 mAnimator.setInterpolator(new LinearInterpolator());
Taran Singh42914102020-06-10 13:42:49 -0700311 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;
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100318 Insets start = mShow
Taran Singh42914102020-06-10 13:42:49 -0700319 ? hiddenInsets
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100320 : controller.getShownStateInsets();
321 Insets end = mShow
322 ? controller.getShownStateInsets()
Taran Singh42914102020-06-10 13:42:49 -0700323 : hiddenInsets;
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100324 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);
Taran Singh85661e32020-05-07 14:45:34 -0700336 if (DEBUG) Log.d(TAG, "Default animation setInsetsAndAlpha fraction: "
337 + insetsFraction);
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100338 });
Tarandeep Singh54554e22019-11-01 14:43:05 -0700339 mAnimator.addListener(new AnimatorListenerAdapter() {
340
341 @Override
342 public void onAnimationEnd(Animator animation) {
343 onAnimationFinish();
344 }
345 });
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100346 if (!mHasAnimationCallbacks) {
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100347 mAnimator.setAnimationHandler(mSfAnimationHandlerThreadLocal.get());
348 }
Tarandeep Singh54554e22019-11-01 14:43:05 -0700349 mAnimator.start();
350 }
351
352 @Override
Adrian Roos5d557ed2020-03-17 20:04:35 +0100353 public void onFinished(WindowInsetsAnimationController controller) {
Taran Singh85661e32020-05-07 14:45:34 -0700354 if (DEBUG) Log.d(TAG, "InternalAnimationControlListener onFinished types:"
355 + Type.toString(mRequestedTypes));
Adrian Roos5d557ed2020-03-17 20:04:35 +0100356 }
357
358 @Override
359 public void onCancelled(WindowInsetsAnimationController controller) {
Tarandeep Singh54554e22019-11-01 14:43:05 -0700360 // Animator can be null when it is cancelled before onReady() completes.
361 if (mAnimator != null) {
362 mAnimator.cancel();
363 }
Taran Singh85661e32020-05-07 14:45:34 -0700364 if (DEBUG) Log.d(TAG, "InternalAnimationControlListener onCancelled types:"
365 + mRequestedTypes);
Tarandeep Singh54554e22019-11-01 14:43:05 -0700366 }
367
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100368 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 }
Tarandeep Singh54554e22019-11-01 14:43:05 -0700380 }
381
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100382 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
398 protected void onAnimationFinish() {
399 mController.finish(mShow);
Taran Singh85661e32020-05-07 14:45:34 -0700400 if (DEBUG) Log.d(TAG, "onAnimationFinish showOnFinish: " + mShow);
Tarandeep Singh54554e22019-11-01 14:43:05 -0700401 }
402
Yunfan Chenb5d2db72019-12-06 15:43:43 +0900403 /**
404 * To get the animation duration in MS.
405 */
406 public long getDurationMs() {
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100407 return mDurationMs;
408 }
409
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;
Tarandeep Singh54554e22019-11-01 14:43:05 -0700419 }
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800420 }
421 }
422
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +0100423 /**
424 * Represents a running animation
425 */
426 private static class RunningAnimation {
427
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100428 RunningAnimation(InsetsAnimationControlRunner runner, int type) {
429 this.runner = runner;
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +0100430 this.type = type;
431 }
432
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100433 final InsetsAnimationControlRunner runner;
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +0100434 final @AnimationType int type;
Jorim Jaggi580aef52020-02-26 18:28:28 +0100435
436 /**
437 * Whether {@link WindowInsetsAnimation.Callback#onStart(WindowInsetsAnimation, Bounds)} has
438 * been dispatched already for this animation.
439 */
440 boolean startDispatched;
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +0100441 }
442
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100443 /**
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
449 PendingControlRequest(@InsetsType int types, WindowInsetsAnimationControlListener listener,
450 long durationMs, Interpolator interpolator, @AnimationType int animationType,
Adrian Roos3406fb92020-02-10 18:38:59 -0800451 @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100452 CancellationSignal cancellationSignal, boolean useInsetsAnimationThread) {
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100453 this.types = types;
454 this.listener = listener;
455 this.durationMs = durationMs;
456 this.interpolator = interpolator;
457 this.animationType = animationType;
458 this.layoutInsetsDuringAnimation = layoutInsetsDuringAnimation;
Adrian Roos3406fb92020-02-10 18:38:59 -0800459 this.cancellationSignal = cancellationSignal;
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100460 this.useInsetsAnimationThread = useInsetsAnimationThread;
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100461 }
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;
Adrian Roos3406fb92020-02-10 18:38:59 -0800469 final CancellationSignal cancellationSignal;
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100470 final boolean useInsetsAnimationThread;
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100471 }
Yunfan Chen4523d5d2020-01-16 16:54:00 +0900472
Tiger Huang173480f2020-04-29 01:05:42 +0800473 /** The local state */
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200474 private final InsetsState mState = new InsetsState();
Tiger Huang173480f2020-04-29 01:05:42 +0800475
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();
Jorim Jaggie35c0592018-11-06 16:21:08 +0100481
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200482 private final Rect mFrame = new Rect();
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100483 private final BiFunction<InsetsController, Integer, InsetsSourceConsumer> mConsumerCreator;
Jorim Jaggib6030952018-10-23 18:31:52 +0200484 private final SparseArray<InsetsSourceConsumer> mSourceConsumers = new SparseArray<>();
Jorim Jaggibf87c152020-04-22 17:18:25 +0200485 private final Host mHost;
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100486 private final Handler mHandler;
Jorim Jaggib6030952018-10-23 18:31:52 +0200487
488 private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>();
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +0100489 private final ArrayList<RunningAnimation> mRunningAnimations = new ArrayList<>();
Jorim Jaggi580aef52020-02-26 18:28:28 +0100490 private final ArrayList<WindowInsetsAnimation> mTmpRunningAnims = new ArrayList<>();
491 private final List<WindowInsetsAnimation> mUnmodifiableTmpRunningAnims =
492 Collections.unmodifiableList(mTmpRunningAnims);
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100493 private final ArrayList<InsetsAnimationControlImpl> mTmpFinishedControls = new ArrayList<>();
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800494 private WindowInsets mLastInsets;
495
496 private boolean mAnimCallbackScheduled;
497
498 private final Runnable mAnimCallback;
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200499
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100500 /** Pending control request that is waiting on IME to be ready to be shown */
501 private PendingControlRequest mPendingImeControlRequest;
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800502
Jorim Jaggi648e5882019-01-24 13:24:02 +0100503 private int mLastLegacySoftInputMode;
Jorim Jaggi7f761872020-01-10 18:24:27 +0100504 private int mLastLegacySystemUiFlags;
Jorim Jaggi580aef52020-02-26 18:28:28 +0100505 private DisplayCutout mLastDisplayCutout;
Jorim Jaggia51168a2019-12-27 15:17:44 +0100506 private boolean mStartingAnimation;
Yunfan Chenfae0aea2020-02-22 20:57:57 +0900507 private int mCaptionInsetsHeight = 0;
Tiger Huangf2321192020-05-21 22:16:14 +0800508 private boolean mAnimationsDisabled;
Jorim Jaggi648e5882019-01-24 13:24:02 +0100509
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100510 private Runnable mPendingControlTimeout = this::abortPendingImeControlRequest;
Jorim Jaggied35b172020-03-06 00:13:57 +0100511 private final ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners
512 = new ArrayList<>();
513
514 /** Set of inset types for which an animation was started since last resetting this field */
515 private @InsetsType int mLastStartedAnimTypes;
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100516
Tiger Huang618dbe022020-06-19 00:12:55 +0800517 /** Set of inset types which cannot be controlled by the user animation */
518 private @InsetsType int mDisabledUserAnimationInsetsTypes;
519
520 private Runnable mInvokeControllableInsetsChangedListeners =
521 this::invokeControllableInsetsChangedListeners;
522
Jorim Jaggibf87c152020-04-22 17:18:25 +0200523 public InsetsController(Host host) {
524 this(host, (controller, type) -> {
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100525 if (type == ITYPE_IME) {
526 return new ImeInsetsSourceConsumer(controller.mState, Transaction::new, controller);
527 } else {
528 return new InsetsSourceConsumer(type, controller.mState, Transaction::new,
529 controller);
530 }
Jorim Jaggibf87c152020-04-22 17:18:25 +0200531 }, host.getHandler());
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100532 }
533
534 @VisibleForTesting
Jorim Jaggibf87c152020-04-22 17:18:25 +0200535 public InsetsController(Host host,
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100536 BiFunction<InsetsController, Integer, InsetsSourceConsumer> consumerCreator,
537 Handler handler) {
Jorim Jaggibf87c152020-04-22 17:18:25 +0200538 mHost = host;
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100539 mConsumerCreator = consumerCreator;
540 mHandler = handler;
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800541 mAnimCallback = () -> {
542 mAnimCallbackScheduled = false;
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +0100543 if (mRunningAnimations.isEmpty()) {
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800544 return;
545 }
546
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100547 mTmpFinishedControls.clear();
Jorim Jaggi580aef52020-02-26 18:28:28 +0100548 mTmpRunningAnims.clear();
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800549 InsetsState state = new InsetsState(mState, true /* copySources */);
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +0100550 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
Jorim Jaggi580aef52020-02-26 18:28:28 +0100551 RunningAnimation runningAnimation = mRunningAnimations.get(i);
Taran Singh85661e32020-05-07 14:45:34 -0700552 if (DEBUG) Log.d(TAG, "Running animation type: " + runningAnimation.type);
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100553 InsetsAnimationControlRunner runner = runningAnimation.runner;
554 if (runner instanceof InsetsAnimationControlImpl) {
555 InsetsAnimationControlImpl control = (InsetsAnimationControlImpl) runner;
Jorim Jaggi580aef52020-02-26 18:28:28 +0100556
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100557 // Keep track of running animation to be dispatched. Aggregate it here such that
558 // if it gets finished within applyChangeInsets we still dispatch it to
559 // onProgress.
560 if (runningAnimation.startDispatched) {
561 mTmpRunningAnims.add(control.getAnimation());
562 }
563
564 if (control.applyChangeInsets(state)) {
565 mTmpFinishedControls.add(control);
566 }
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100567 }
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800568 }
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100569
Jorim Jaggi580aef52020-02-26 18:28:28 +0100570 WindowInsets insets = state.calculateInsets(mFrame, mState /* ignoringVisibilityState*/,
571 mLastInsets.isRound(), mLastInsets.shouldAlwaysConsumeSystemBars(),
Jorim Jaggi22488d32020-03-19 01:12:44 +0100572 mLastDisplayCutout, mLastLegacySoftInputMode, mLastLegacySystemUiFlags,
573 null /* typeSideMap */);
Jorim Jaggibf87c152020-04-22 17:18:25 +0200574 mHost.dispatchWindowInsetsAnimationProgress(insets, mUnmodifiableTmpRunningAnims);
Taran Singh85661e32020-05-07 14:45:34 -0700575 if (DEBUG) {
576 for (WindowInsetsAnimation anim : mUnmodifiableTmpRunningAnims) {
577 Log.d(TAG, String.format("Running animation type: %d, progress: %f",
578 anim.getTypeMask(), anim.getInterpolatedFraction()));
579 }
580 }
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100581
582 for (int i = mTmpFinishedControls.size() - 1; i >= 0; i--) {
Adrian Roosdb5b0c22020-02-12 15:05:27 -0800583 dispatchAnimationEnd(mTmpFinishedControls.get(i).getAnimation());
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100584 }
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800585 };
Jorim Jaggic8d60382018-10-31 17:06:06 +0100586 }
587
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100588 @VisibleForTesting
589 public void onFrameChanged(Rect frame) {
Tarandeep Singha6f35612019-01-11 19:50:46 -0800590 if (mFrame.equals(frame)) {
591 return;
592 }
Jorim Jaggibf87c152020-04-22 17:18:25 +0200593 mHost.notifyInsetsChanged();
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200594 mFrame.set(frame);
595 }
596
Tiger Huang4a7835f2019-11-06 00:07:56 +0800597 @Override
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200598 public InsetsState getState() {
599 return mState;
600 }
601
Jorim Jaggi49b9f6c2020-03-24 22:28:38 +0100602 @Override
603 public boolean isRequestedVisible(int type) {
604 return getSourceConsumer(type).isRequestedVisible();
605 }
606
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100607 public InsetsState getLastDispatchedState() {
Tiger Huang173480f2020-04-29 01:05:42 +0800608 return mLastDispatchedState;
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100609 }
610
611 @VisibleForTesting
612 public boolean onStateChanged(InsetsState state) {
Jorim Jaggied312592020-05-25 16:46:56 +0200613 boolean stateChanged = !mState.equals(state, true /* excludingCaptionInsets */,
614 false /* excludeInvisibleIme */)
Yunfan Chenfae0aea2020-02-22 20:57:57 +0900615 || !captionInsetsUnchanged();
Jorim Jaggied312592020-05-25 16:46:56 +0200616 if (!stateChanged && mLastDispatchedState.equals(state)) {
Jorim Jaggic8d60382018-10-31 17:06:06 +0100617 return false;
618 }
Taran Singh85661e32020-05-07 14:45:34 -0700619 if (DEBUG) Log.d(TAG, "onStateChanged: " + state);
Jorim Jaggi33a21832020-04-06 14:15:46 +0200620 updateState(state);
Jorim Jaggied312592020-05-25 16:46:56 +0200621
622 boolean localStateChanged = !mState.equals(mLastDispatchedState,
623 true /* excludingCaptionInsets */, true /* excludeInvisibleIme */);
Tiger Huang173480f2020-04-29 01:05:42 +0800624 mLastDispatchedState.set(state, true /* copySources */);
Jorim Jaggied312592020-05-25 16:46:56 +0200625
Jorim Jaggic8d60382018-10-31 17:06:06 +0100626 applyLocalVisibilityOverride();
Jorim Jaggi22488d32020-03-19 01:12:44 +0100627 if (localStateChanged) {
Jorim Jaggied312592020-05-25 16:46:56 +0200628 if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged, send state to WM: " + mState);
Jorim Jaggibf87c152020-04-22 17:18:25 +0200629 mHost.notifyInsetsChanged();
Tiger Huang173480f2020-04-29 01:05:42 +0800630 updateRequestedState();
Jorim Jaggie35c0592018-11-06 16:21:08 +0100631 }
Jorim Jaggi33a21832020-04-06 14:15:46 +0200632 return true;
633 }
634
635 private void updateState(InsetsState newState) {
636 mState.setDisplayFrame(newState.getDisplayFrame());
Tiger Huang618dbe022020-06-19 00:12:55 +0800637 @InsetsType int disabledUserAnimationTypes = 0;
638 @InsetsType int[] cancelledUserAnimationTypes = {0};
639 for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
640 InsetsSource source = newState.peekSource(type);
Jorim Jaggibfa95a72020-06-18 22:51:49 +0200641 if (source == null) continue;
Tiger Huang618dbe022020-06-19 00:12:55 +0800642 @AnimationType int animationType = getAnimationType(type);
643 if (!source.isUserControllable()) {
644 @InsetsType int insetsType = toPublicType(type);
645 // The user animation is not allowed when visible frame is empty.
646 disabledUserAnimationTypes |= insetsType;
647 if (animationType == ANIMATION_TYPE_USER) {
648 // Existing user animation needs to be cancelled.
649 animationType = ANIMATION_TYPE_NONE;
650 cancelledUserAnimationTypes[0] |= insetsType;
651 }
652 }
653 getSourceConsumer(type).updateSource(source, animationType);
654 }
655 for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
656 InsetsSource source = mState.peekSource(type);
657 if (source == null) continue;
658 if (newState.peekSource(type) == null) {
659 mState.removeSource(type);
Jorim Jaggi33a21832020-04-06 14:15:46 +0200660 }
661 }
Yunfan Chenfae0aea2020-02-22 20:57:57 +0900662 if (mCaptionInsetsHeight != 0) {
663 mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(mFrame.left, mFrame.top,
664 mFrame.right, mFrame.top + mCaptionInsetsHeight));
665 }
Tiger Huang618dbe022020-06-19 00:12:55 +0800666
667 updateDisabledUserAnimationTypes(disabledUserAnimationTypes);
668
669 if (cancelledUserAnimationTypes[0] != 0) {
670 mHandler.post(() -> show(cancelledUserAnimationTypes[0]));
671 }
672 }
673
674 private void updateDisabledUserAnimationTypes(@InsetsType int disabledUserAnimationTypes) {
675 @InsetsType int diff = mDisabledUserAnimationInsetsTypes ^ disabledUserAnimationTypes;
676 if (diff != 0) {
677 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
678 InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
679 if (consumer.getControl() != null
680 && (toPublicType(consumer.getType()) & diff) != 0) {
681 mHandler.removeCallbacks(mInvokeControllableInsetsChangedListeners);
682 mHandler.post(mInvokeControllableInsetsChangedListeners);
683 break;
684 }
685 }
686 mDisabledUserAnimationInsetsTypes = disabledUserAnimationTypes;
687 }
Yunfan Chenfae0aea2020-02-22 20:57:57 +0900688 }
689
690 private boolean captionInsetsUnchanged() {
691 if (mState.peekSource(ITYPE_CAPTION_BAR) == null
692 && mCaptionInsetsHeight == 0) {
693 return true;
694 }
695 if (mState.peekSource(ITYPE_CAPTION_BAR) != null
696 && mCaptionInsetsHeight
697 == mState.peekSource(ITYPE_CAPTION_BAR).getFrame().height()) {
698 return true;
699 }
700 return false;
Jorim Jaggi2751c292020-03-20 23:46:38 +0100701 }
702
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200703 /**
704 * @see InsetsState#calculateInsets
705 */
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100706 @VisibleForTesting
707 public WindowInsets calculateInsets(boolean isScreenRound,
Jorim Jaggi22488d32020-03-19 01:12:44 +0100708 boolean alwaysConsumeSystemBars, DisplayCutout cutout,
709 int legacySoftInputMode, int legacySystemUiFlags) {
Jorim Jaggi648e5882019-01-24 13:24:02 +0100710 mLastLegacySoftInputMode = legacySoftInputMode;
Jorim Jaggi7f761872020-01-10 18:24:27 +0100711 mLastLegacySystemUiFlags = legacySystemUiFlags;
Jorim Jaggi580aef52020-02-26 18:28:28 +0100712 mLastDisplayCutout = cutout;
713 mLastInsets = mState.calculateInsets(mFrame, null /* ignoringVisibilityState*/,
Jorim Jaggi22488d32020-03-19 01:12:44 +0100714 isScreenRound, alwaysConsumeSystemBars, cutout,
715 legacySoftInputMode, legacySystemUiFlags,
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100716 null /* typeSideMap */);
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800717 return mLastInsets;
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200718 }
719
Jorim Jaggib6030952018-10-23 18:31:52 +0200720 /**
Jorim Jaggi22488d32020-03-19 01:12:44 +0100721 * @see InsetsState#calculateVisibleInsets(Rect, int)
Jorim Jaggi4e04eb22020-01-09 16:42:14 +0100722 */
Jorim Jaggi22488d32020-03-19 01:12:44 +0100723 public Rect calculateVisibleInsets(@SoftInputModeFlags int softInputMode) {
724 return mState.calculateVisibleInsets(mFrame, softInputMode);
Jorim Jaggi4e04eb22020-01-09 16:42:14 +0100725 }
726
727 /**
Jorim Jaggib6030952018-10-23 18:31:52 +0200728 * Called when the server has dispatched us a new set of inset controls.
729 */
730 public void onControlsChanged(InsetsSourceControl[] activeControls) {
731 if (activeControls != null) {
732 for (InsetsSourceControl activeControl : activeControls) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800733 if (activeControl != null) {
734 // TODO(b/122982984): Figure out why it can be null.
735 mTmpControlArray.put(activeControl.getType(), activeControl);
736 }
Jorim Jaggib6030952018-10-23 18:31:52 +0200737 }
738 }
739
Tiger Huang173480f2020-04-29 01:05:42 +0800740 final boolean hasControl = mTmpControlArray.size() > 0;
741 final int[] showTypes = new int[1];
742 final int[] hideTypes = new int[1];
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100743
Jorim Jaggib6030952018-10-23 18:31:52 +0200744 // Ensure to update all existing source consumers
745 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
746 final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
747 final InsetsSourceControl control = mTmpControlArray.get(consumer.getType());
748
749 // control may be null, but we still need to update the control to null if it got
750 // revoked.
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100751 consumer.setControl(control, showTypes, hideTypes);
Jorim Jaggib6030952018-10-23 18:31:52 +0200752 }
753
754 // Ensure to create source consumers if not available yet.
755 for (int i = mTmpControlArray.size() - 1; i >= 0; i--) {
756 final InsetsSourceControl control = mTmpControlArray.valueAt(i);
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100757 InsetsSourceConsumer consumer = getSourceConsumer(control.getType());
758 consumer.setControl(control, showTypes, hideTypes);
759
Jorim Jaggib6030952018-10-23 18:31:52 +0200760 }
761 mTmpControlArray.clear();
Jorim Jaggied35b172020-03-06 00:13:57 +0100762
763 // Do not override any animations that the app started in the OnControllableInsetsChanged
764 // listeners.
765 int animatingTypes = invokeControllableInsetsChangedListeners();
766 showTypes[0] &= ~animatingTypes;
767 hideTypes[0] &= ~animatingTypes;
768
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100769 if (showTypes[0] != 0) {
770 applyAnimation(showTypes[0], true /* show */, false /* fromIme */);
771 }
772 if (hideTypes[0] != 0) {
773 applyAnimation(hideTypes[0], false /* show */, false /* fromIme */);
774 }
Jorim Jaggibfa95a72020-06-18 22:51:49 +0200775 if (hasControl && mRequestedState.hasSources()) {
Tiger Huang173480f2020-04-29 01:05:42 +0800776 // We might have changed our requested visibilities while we don't have the control,
777 // so we need to update our requested state once we have control. Otherwise, our
778 // requested state at the server side might be incorrect.
779 updateRequestedState();
780 }
Jorim Jaggib6030952018-10-23 18:31:52 +0200781 }
782
783 @Override
Tiger Huang332793b2019-10-29 23:21:27 +0800784 public void show(@InsetsType int types) {
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800785 show(types, false /* fromIme */);
786 }
787
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100788 @VisibleForTesting
789 public void show(@InsetsType int types, boolean fromIme) {
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100790 // Handle pending request ready in case there was one set.
791 if (fromIme && mPendingImeControlRequest != null) {
792 PendingControlRequest pendingRequest = mPendingImeControlRequest;
793 mPendingImeControlRequest = null;
794 mHandler.removeCallbacks(mPendingControlTimeout);
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000795 controlAnimationUnchecked(
796 pendingRequest.types, pendingRequest.cancellationSignal,
Adrian Roos3406fb92020-02-10 18:38:59 -0800797 pendingRequest.listener, mFrame,
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100798 true /* fromIme */, pendingRequest.durationMs, pendingRequest.interpolator,
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100799 pendingRequest.animationType,
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100800 pendingRequest.layoutInsetsDuringAnimation,
801 pendingRequest.useInsetsAnimationThread);
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100802 return;
803 }
804
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800805 // TODO: Support a ResultReceiver for IME.
806 // TODO(b/123718661): Make show() work for multi-session IME.
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800807 int typesReady = 0;
Jorim Jaggib6030952018-10-23 18:31:52 +0200808 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
809 for (int i = internalTypes.size() - 1; i >= 0; i--) {
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +0100810 @InternalInsetsType int internalType = internalTypes.valueAt(i);
811 @AnimationType int animationType = getAnimationType(internalType);
812 InsetsSourceConsumer consumer = getSourceConsumer(internalType);
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100813 if (consumer.isRequestedVisible() && animationType == ANIMATION_TYPE_NONE
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +0100814 || animationType == ANIMATION_TYPE_SHOW) {
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800815 // no-op: already shown or animating in (because window visibility is
816 // applied before starting animation).
Taran Singh85661e32020-05-07 14:45:34 -0700817 if (DEBUG) Log.d(TAG, String.format(
818 "show ignored for type: %d animType: %d requestedVisible: %s",
819 consumer.getType(), animationType, consumer.isRequestedVisible()));
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800820 continue;
821 }
Adrian Roosa2d2ba72020-05-25 17:44:55 +0200822 if (fromIme && animationType == ANIMATION_TYPE_USER) {
823 // App is already controlling the IME, don't cancel it.
824 continue;
825 }
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800826 typesReady |= InsetsState.toPublicType(consumer.getType());
Jorim Jaggib6030952018-10-23 18:31:52 +0200827 }
Taran Singh85661e32020-05-07 14:45:34 -0700828 if (DEBUG) Log.d(TAG, "show typesReady: " + typesReady);
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800829 applyAnimation(typesReady, true /* show */, fromIme);
Jorim Jaggib6030952018-10-23 18:31:52 +0200830 }
831
832 @Override
Tiger Huang332793b2019-10-29 23:21:27 +0800833 public void hide(@InsetsType int types) {
Taran Singhd7fc5862019-10-10 14:45:17 +0200834 hide(types, false /* fromIme */);
835 }
836
Tiger Huang332793b2019-10-29 23:21:27 +0800837 void hide(@InsetsType int types, boolean fromIme) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800838 int typesReady = 0;
Jorim Jaggib6030952018-10-23 18:31:52 +0200839 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
840 for (int i = internalTypes.size() - 1; i >= 0; i--) {
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +0100841 @InternalInsetsType int internalType = internalTypes.valueAt(i);
842 @AnimationType int animationType = getAnimationType(internalType);
843 InsetsSourceConsumer consumer = getSourceConsumer(internalType);
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100844 if (!consumer.isRequestedVisible() && animationType == ANIMATION_TYPE_NONE
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +0100845 || animationType == ANIMATION_TYPE_HIDE) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800846 // no-op: already hidden or animating out.
847 continue;
848 }
849 typesReady |= InsetsState.toPublicType(consumer.getType());
Jorim Jaggib6030952018-10-23 18:31:52 +0200850 }
Taran Singhd7fc5862019-10-10 14:45:17 +0200851 applyAnimation(typesReady, false /* show */, fromIme /* fromIme */);
Jorim Jaggib6030952018-10-23 18:31:52 +0200852 }
853
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100854 @Override
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000855 public void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
Jorim Jaggidd3304e2020-01-20 17:24:51 +0100856 @Nullable Interpolator interpolator,
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000857 @Nullable CancellationSignal cancellationSignal,
Jorim Jaggidd3304e2020-01-20 17:24:51 +0100858 @NonNull WindowInsetsAnimationControlListener listener) {
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000859 controlWindowInsetsAnimation(types, cancellationSignal, listener,
860 false /* fromIme */, durationMillis, interpolator, ANIMATION_TYPE_USER);
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800861 }
862
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000863 private void controlWindowInsetsAnimation(@InsetsType int types,
864 @Nullable CancellationSignal cancellationSignal,
865 WindowInsetsAnimationControlListener listener,
866 boolean fromIme, long durationMs, @Nullable Interpolator interpolator,
867 @AnimationType int animationType) {
Adrian Roos8d04bcb2020-05-29 18:01:04 +0200868 if ((mState.calculateUncontrollableInsetsFromFrame(mFrame) & types) != 0) {
Adrian Roos5d557ed2020-03-17 20:04:35 +0100869 listener.onCancelled(null);
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000870 return;
Tarandeep Singha6f35612019-01-11 19:50:46 -0800871 }
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100872
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000873 controlAnimationUnchecked(types, cancellationSignal, listener, mFrame, fromIme, durationMs,
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100874 interpolator, animationType, getLayoutInsetsDuringAnimationMode(types),
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100875 false /* useInsetsAnimationThread */);
Tarandeep Singha6f35612019-01-11 19:50:46 -0800876 }
877
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000878 private void controlAnimationUnchecked(@InsetsType int types,
879 @Nullable CancellationSignal cancellationSignal,
Tarandeep Singh54554e22019-11-01 14:43:05 -0700880 WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme,
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100881 long durationMs, Interpolator interpolator,
Jorim Jaggidd3304e2020-01-20 17:24:51 +0100882 @AnimationType int animationType,
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100883 @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
884 boolean useInsetsAnimationThread) {
Adrian Roosa79b8852020-05-26 21:25:15 +0200885 if ((types & mTypesBeingCancelled) != 0) {
886 throw new IllegalStateException("Cannot start a new insets animation of "
887 + Type.toString(types)
888 + " while an existing " + Type.toString(mTypesBeingCancelled)
889 + " is being cancelled.");
890 }
Tiger Huang618dbe022020-06-19 00:12:55 +0800891 if (animationType == ANIMATION_TYPE_USER) {
892 final @InsetsType int disabledTypes = types & mDisabledUserAnimationInsetsTypes;
893 if (DEBUG) Log.d(TAG, "user animation disabled types: " + disabledTypes);
894 types &= ~mDisabledUserAnimationInsetsTypes;
895
896 if (fromIme && (disabledTypes & ime()) != 0
897 && !mState.getSource(ITYPE_IME).isVisible()) {
898 // We've requested IMM to show IME, but the IME is not controllable. We need to
899 // cancel the request.
900 getSourceConsumer(ITYPE_IME).hide(true, animationType);
901 }
902 }
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800903 if (types == 0) {
904 // nothing to animate.
Adrian Roos5d557ed2020-03-17 20:04:35 +0100905 listener.onCancelled(null);
Taran Singh85661e32020-05-07 14:45:34 -0700906 if (DEBUG) Log.d(TAG, "no types to animate in controlAnimationUnchecked");
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000907 return;
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800908 }
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100909 cancelExistingControllers(types);
Taran Singh85661e32020-05-07 14:45:34 -0700910 if (DEBUG) Log.d(TAG, "controlAnimation types: " + types);
Jorim Jaggied35b172020-03-06 00:13:57 +0100911 mLastStartedAnimTypes |= types;
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100912
Tiger Huang969c6082019-12-24 20:08:57 +0800913 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
Yunfan Chen02abf552019-12-05 14:51:09 +0900914 final SparseArray<InsetsSourceControl> controls = new SparseArray<>();
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800915
Yunfan Chen02abf552019-12-05 14:51:09 +0900916 Pair<Integer, Boolean> typesReadyPair = collectSourceControls(
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100917 fromIme, internalTypes, controls, animationType);
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800918 int typesReady = typesReadyPair.first;
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100919 boolean imeReady = typesReadyPair.second;
Taran Singh85661e32020-05-07 14:45:34 -0700920 if (DEBUG) Log.d(TAG, String.format(
921 "controlAnimationUnchecked, typesReady: %s imeReady: %s", typesReady, imeReady));
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100922 if (!imeReady) {
923 // IME isn't ready, all requested types will be animated once IME is ready
924 abortPendingImeControlRequest();
Adrian Roos3406fb92020-02-10 18:38:59 -0800925 final PendingControlRequest request = new PendingControlRequest(types,
926 listener, durationMs,
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100927 interpolator, animationType, layoutInsetsDuringAnimation, cancellationSignal,
928 useInsetsAnimationThread);
Adrian Roos3406fb92020-02-10 18:38:59 -0800929 mPendingImeControlRequest = request;
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100930 mHandler.postDelayed(mPendingControlTimeout, PENDING_CONTROL_TIMEOUT_MS);
Taran Singh85661e32020-05-07 14:45:34 -0700931 if (DEBUG) Log.d(TAG, "Ime not ready. Create pending request");
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000932 if (cancellationSignal != null) {
933 cancellationSignal.setOnCancelListener(() -> {
934 if (mPendingImeControlRequest == request) {
Taran Singh85661e32020-05-07 14:45:34 -0700935 if (DEBUG) Log.d(TAG,
936 "Cancellation signal abortPendingImeControlRequest");
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000937 abortPendingImeControlRequest();
938 }
939 });
940 }
941 return;
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800942 }
943
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800944 if (typesReady == 0) {
Taran Singh85661e32020-05-07 14:45:34 -0700945 if (DEBUG) Log.d(TAG, "No types ready. onCancelled()");
Adrian Roos5d557ed2020-03-17 20:04:35 +0100946 listener.onCancelled(null);
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000947 return;
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800948 }
949
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100950
951 final InsetsAnimationControlRunner runner = useInsetsAnimationThread
952 ? new InsetsAnimationThreadControlRunner(controls,
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100953 frame, mState, listener, typesReady, this, durationMs, interpolator,
Jorim Jaggibf87c152020-04-22 17:18:25 +0200954 animationType, mHost.getHandler())
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100955 : new InsetsAnimationControlImpl(controls,
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100956 frame, mState, listener, typesReady, this, durationMs, interpolator,
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100957 animationType);
958 mRunningAnimations.add(new RunningAnimation(runner, animationType));
Taran Singh85661e32020-05-07 14:45:34 -0700959 if (DEBUG) Log.d(TAG, "Animation added to runner. useInsetsAnimationThread: "
960 + useInsetsAnimationThread);
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000961 if (cancellationSignal != null) {
Adrian Roosa79b8852020-05-26 21:25:15 +0200962 cancellationSignal.setOnCancelListener(() -> {
963 cancelAnimation(runner, true /* invokeCallback */);
964 });
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000965 }
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100966 if (layoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN) {
967 showDirectly(types);
968 } else {
969 hideDirectly(types, false /* animationFinished */, animationType);
970 }
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800971 }
972
973 /**
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100974 * @return Pair of (types ready to animate, IME ready to animate).
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800975 */
Yunfan Chen02abf552019-12-05 14:51:09 +0900976 private Pair<Integer, Boolean> collectSourceControls(boolean fromIme,
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100977 ArraySet<Integer> internalTypes, SparseArray<InsetsSourceControl> controls,
978 @AnimationType int animationType) {
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800979 int typesReady = 0;
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100980 boolean imeReady = true;
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100981 for (int i = internalTypes.size() - 1; i >= 0; i--) {
Tarandeep Singhb9538cd2020-02-20 17:51:18 -0800982 final InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100983 boolean show = animationType == ANIMATION_TYPE_SHOW
984 || animationType == ANIMATION_TYPE_USER;
985 boolean canRun = false;
986 if (show) {
Taran Singhd7fc5862019-10-10 14:45:17 +0200987 // Show request
988 switch(consumer.requestShow(fromIme)) {
989 case ShowResult.SHOW_IMMEDIATELY:
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100990 canRun = true;
Taran Singhd7fc5862019-10-10 14:45:17 +0200991 break;
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100992 case ShowResult.IME_SHOW_DELAYED:
993 imeReady = false;
Taran Singh85661e32020-05-07 14:45:34 -0700994 if (DEBUG) Log.d(TAG, "requestShow IME_SHOW_DELAYED");
Taran Singhd7fc5862019-10-10 14:45:17 +0200995 break;
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100996 case ShowResult.IME_SHOW_FAILED:
Taran Singh85661e32020-05-07 14:45:34 -0700997 if (WARN) Log.w(TAG, "requestShow IME_SHOW_FAILED. fromIme: "
998 + fromIme);
Taran Singhd7fc5862019-10-10 14:45:17 +0200999 // IME cannot be shown (since it didn't have focus), proceed
1000 // with animation of other types.
Taran Singhd7fc5862019-10-10 14:45:17 +02001001 break;
Tarandeep Singh46d59f02019-01-29 18:09:15 -08001002 }
Jorim Jaggi5bb571d2018-11-06 14:42:04 +01001003 } else {
Taran Singhd7fc5862019-10-10 14:45:17 +02001004 // Hide request
1005 // TODO: Move notifyHidden() to beginning of the hide animation
1006 // (when visibility actually changes using hideDirectly()).
1007 if (!fromIme) {
1008 consumer.notifyHidden();
1009 }
Jorim Jaggi3182ef12020-01-30 00:16:18 +01001010 canRun = true;
1011 }
1012 if (!canRun) {
Taran Singh85661e32020-05-07 14:45:34 -07001013 if (WARN) Log.w(TAG, String.format(
1014 "collectSourceControls can't continue show for type: %s fromIme: %b",
1015 InsetsState.typeToString(consumer.getType()), fromIme));
Jorim Jaggi3182ef12020-01-30 00:16:18 +01001016 continue;
Jorim Jaggi5bb571d2018-11-06 14:42:04 +01001017 }
Tiger Huang969c6082019-12-24 20:08:57 +08001018 final InsetsSourceControl control = consumer.getControl();
1019 if (control != null) {
Rob Carr3a367c42020-03-10 15:51:35 -07001020 controls.put(consumer.getType(), new InsetsSourceControl(control));
Jorim Jaggi3182ef12020-01-30 00:16:18 +01001021 typesReady |= toPublicType(consumer.getType());
1022 } else if (animationType == ANIMATION_TYPE_SHOW) {
Taran Singh85661e32020-05-07 14:45:34 -07001023 if (DEBUG) Log.d(TAG, "collectSourceControls no control for show(). fromIme: "
1024 + fromIme);
Jorim Jaggi3182ef12020-01-30 00:16:18 +01001025 // We don't have a control at the moment. However, we still want to update requested
1026 // visibility state such that in case we get control, we can apply show animation.
1027 consumer.show(fromIme);
1028 } else if (animationType == ANIMATION_TYPE_HIDE) {
1029 consumer.hide();
Tiger Huang969c6082019-12-24 20:08:57 +08001030 }
Jorim Jaggi5bb571d2018-11-06 14:42:04 +01001031 }
Jorim Jaggid7f10ed2020-01-08 21:41:55 +01001032 return new Pair<>(typesReady, imeReady);
Jorim Jaggi5bb571d2018-11-06 14:42:04 +01001033 }
1034
Jorim Jaggia51168a2019-12-27 15:17:44 +01001035 private @LayoutInsetsDuringAnimation int getLayoutInsetsDuringAnimationMode(
1036 @InsetsType int types) {
1037
1038 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
1039
1040 // Generally, we want to layout the opposite of the current state. This is to make animation
1041 // callbacks easy to use: The can capture the layout values and then treat that as end-state
1042 // during the animation.
1043 //
1044 // However, if controlling multiple sources, we want to treat it as shown if any of the
1045 // types is currently hidden.
1046 for (int i = internalTypes.size() - 1; i >= 0; i--) {
1047 InsetsSourceConsumer consumer = mSourceConsumers.get(internalTypes.valueAt(i));
1048 if (consumer == null) {
1049 continue;
1050 }
1051 if (!consumer.isRequestedVisible()) {
1052 return LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
1053 }
1054 }
1055 return LAYOUT_INSETS_DURING_ANIMATION_HIDDEN;
1056 }
1057
Tiger Huang332793b2019-10-29 23:21:27 +08001058 private void cancelExistingControllers(@InsetsType int types) {
Adrian Roosa79b8852020-05-26 21:25:15 +02001059 final int originalmTypesBeingCancelled = mTypesBeingCancelled;
1060 mTypesBeingCancelled |= types;
1061 try {
1062 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
1063 InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner;
1064 if ((control.getTypes() & types) != 0) {
1065 cancelAnimation(control, true /* invokeCallback */);
1066 }
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +01001067 }
Adrian Roosa79b8852020-05-26 21:25:15 +02001068 if ((types & ime()) != 0) {
1069 abortPendingImeControlRequest();
1070 }
1071 } finally {
1072 mTypesBeingCancelled = originalmTypesBeingCancelled;
Jorim Jaggid7f10ed2020-01-08 21:41:55 +01001073 }
1074 }
1075
1076 private void abortPendingImeControlRequest() {
1077 if (mPendingImeControlRequest != null) {
Adrian Roos5d557ed2020-03-17 20:04:35 +01001078 mPendingImeControlRequest.listener.onCancelled(null);
Jorim Jaggid7f10ed2020-01-08 21:41:55 +01001079 mPendingImeControlRequest = null;
1080 mHandler.removeCallbacks(mPendingControlTimeout);
Taran Singh85661e32020-05-07 14:45:34 -07001081 if (DEBUG) Log.d(TAG, "abortPendingImeControlRequest");
Jorim Jaggid7f10ed2020-01-08 21:41:55 +01001082 }
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +01001083 }
1084
1085 @VisibleForTesting
Yunfan Chen02abf552019-12-05 14:51:09 +09001086 @Override
Jorim Jaggi6d5c8012020-02-28 01:40:27 +01001087 public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) {
1088 cancelAnimation(runner, false /* invokeCallback */);
Taran Singh85661e32020-05-07 14:45:34 -07001089 if (DEBUG) Log.d(TAG, "notifyFinished. shown: " + shown);
Tarandeep Singh54554e22019-11-01 14:43:05 -07001090 if (shown) {
Jorim Jaggi6d5c8012020-02-28 01:40:27 +01001091 showDirectly(runner.getTypes());
Tarandeep Singh54554e22019-11-01 14:43:05 -07001092 } else {
Jorim Jaggi6d5c8012020-02-28 01:40:27 +01001093 hideDirectly(runner.getTypes(), true /* animationFinished */,
1094 runner.getAnimationType());
Tarandeep Singh54554e22019-11-01 14:43:05 -07001095 }
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +01001096 }
1097
Yunfan Chen02abf552019-12-05 14:51:09 +09001098 @Override
1099 public void applySurfaceParams(final SyncRtSurfaceTransactionApplier.SurfaceParams... params) {
Jorim Jaggibf87c152020-04-22 17:18:25 +02001100 mHost.applySurfaceParams(params);
Yunfan Chen02abf552019-12-05 14:51:09 +09001101 }
1102
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +01001103 void notifyControlRevoked(InsetsSourceConsumer consumer) {
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +01001104 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
Jorim Jaggi6d5c8012020-02-28 01:40:27 +01001105 InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner;
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +01001106 if ((control.getTypes() & toPublicType(consumer.getType())) != 0) {
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +01001107 cancelAnimation(control, true /* invokeCallback */);
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +01001108 }
1109 }
Jorim Jaggid7f10ed2020-01-08 21:41:55 +01001110 if (consumer.getType() == ITYPE_IME) {
1111 abortPendingImeControlRequest();
1112 }
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +01001113 }
1114
Jorim Jaggi6d5c8012020-02-28 01:40:27 +01001115 private void cancelAnimation(InsetsAnimationControlRunner control, boolean invokeCallback) {
Taran Singh85661e32020-05-07 14:45:34 -07001116 if (DEBUG) Log.d(TAG, String.format("cancelAnimation of types: %d, animType: %d",
1117 control.getTypes(), control.getAnimationType()));
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +01001118 if (invokeCallback) {
Jorim Jaggi6d5c8012020-02-28 01:40:27 +01001119 control.cancel();
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +01001120 }
1121 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
Jorim Jaggi33a21832020-04-06 14:15:46 +02001122 RunningAnimation runningAnimation = mRunningAnimations.get(i);
1123 if (runningAnimation.runner == control) {
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +01001124 mRunningAnimations.remove(i);
Jorim Jaggi33a21832020-04-06 14:15:46 +02001125 ArraySet<Integer> types = toInternalType(control.getTypes());
1126 for (int j = types.size() - 1; j >= 0; j--) {
1127 if (getSourceConsumer(types.valueAt(j)).notifyAnimationFinished()) {
Jorim Jaggibf87c152020-04-22 17:18:25 +02001128 mHost.notifyInsetsChanged();
Jorim Jaggi33a21832020-04-06 14:15:46 +02001129 }
1130 }
Adrian Roosa79b8852020-05-26 21:25:15 +02001131 if (invokeCallback && runningAnimation.startDispatched) {
1132 dispatchAnimationEnd(runningAnimation.runner.getAnimation());
1133 }
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +01001134 break;
1135 }
1136 }
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +01001137 }
1138
Jorim Jaggic8d60382018-10-31 17:06:06 +01001139 private void applyLocalVisibilityOverride() {
1140 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
1141 final InsetsSourceConsumer controller = mSourceConsumers.valueAt(i);
1142 controller.applyLocalVisibilityOverride();
1143 }
1144 }
1145
Jorim Jaggib6030952018-10-23 18:31:52 +02001146 @VisibleForTesting
Tiger Huang332793b2019-10-29 23:21:27 +08001147 public @NonNull InsetsSourceConsumer getSourceConsumer(@InternalInsetsType int type) {
Jorim Jaggib6030952018-10-23 18:31:52 +02001148 InsetsSourceConsumer controller = mSourceConsumers.get(type);
1149 if (controller != null) {
1150 return controller;
1151 }
Jorim Jaggid7f10ed2020-01-08 21:41:55 +01001152 controller = mConsumerCreator.apply(this, type);
Jorim Jaggib6030952018-10-23 18:31:52 +02001153 mSourceConsumers.put(type, controller);
1154 return controller;
1155 }
1156
Jorim Jaggi5bb571d2018-11-06 14:42:04 +01001157 @VisibleForTesting
1158 public void notifyVisibilityChanged() {
Jorim Jaggibf87c152020-04-22 17:18:25 +02001159 mHost.notifyInsetsChanged();
Tiger Huang173480f2020-04-29 01:05:42 +08001160 updateRequestedState();
Jorim Jaggie35c0592018-11-06 16:21:08 +01001161 }
1162
1163 /**
Tiger Huang2ab590a2020-05-19 21:55:13 +08001164 * @see ViewRootImpl#updateCompatSysUiVisibility(int, boolean, boolean)
1165 */
1166 public void updateCompatSysUiVisibility(@InternalInsetsType int type, boolean visible,
1167 boolean hasControl) {
1168 mHost.updateCompatSysUiVisibility(type, visible, hasControl);
1169 }
1170
1171 /**
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08001172 * Called when current window gains focus.
1173 */
1174 public void onWindowFocusGained() {
Tiger Huang332793b2019-10-29 23:21:27 +08001175 getSourceConsumer(ITYPE_IME).onWindowFocusGained();
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08001176 }
1177
1178 /**
1179 * Called when current window loses focus.
1180 */
1181 public void onWindowFocusLost() {
Tiger Huang332793b2019-10-29 23:21:27 +08001182 getSourceConsumer(ITYPE_IME).onWindowFocusLost();
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08001183 }
1184
Tarandeep Singh46d59f02019-01-29 18:09:15 -08001185 /**
1186 * Used by {@link ImeInsetsSourceConsumer} when IME decides to be shown/hidden.
1187 * @hide
1188 */
1189 @VisibleForTesting
1190 public void applyImeVisibility(boolean setVisible) {
1191 if (setVisible) {
1192 show(Type.IME, true /* fromIme */);
1193 } else {
1194 hide(Type.IME);
1195 }
1196 }
1197
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +01001198 @VisibleForTesting
1199 public @AnimationType int getAnimationType(@InternalInsetsType int type) {
1200 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
Jorim Jaggi6d5c8012020-02-28 01:40:27 +01001201 InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner;
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +01001202 if (control.controlsInternalType(type)) {
1203 return mRunningAnimations.get(i).type;
1204 }
1205 }
1206 return ANIMATION_TYPE_NONE;
Tarandeep Singh93ea15a2019-11-26 11:09:14 -08001207 }
1208
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08001209 /**
Tiger Huang173480f2020-04-29 01:05:42 +08001210 * Sends the local visibility state back to window manager if it is changed.
Jorim Jaggie35c0592018-11-06 16:21:08 +01001211 */
Tiger Huang173480f2020-04-29 01:05:42 +08001212 private void updateRequestedState() {
1213 boolean changed = false;
Jorim Jaggie35c0592018-11-06 16:21:08 +01001214 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
1215 final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
Tiger Huang173480f2020-04-29 01:05:42 +08001216 final @InternalInsetsType int type = consumer.getType();
1217 if (type == ITYPE_CAPTION_BAR) {
1218 continue;
1219 }
Jorim Jaggie35c0592018-11-06 16:21:08 +01001220 if (consumer.getControl() != null) {
Tiger Huang173480f2020-04-29 01:05:42 +08001221 final InsetsSource localSource = mState.getSource(type);
1222 if (!localSource.equals(mRequestedState.peekSource(type))) {
Tiger Huangfbfc3ab2020-05-06 01:02:31 +08001223 // Our requested state is stale. Update it here and send it to window manager.
Tiger Huang173480f2020-04-29 01:05:42 +08001224 mRequestedState.addSource(new InsetsSource(localSource));
1225 changed = true;
1226 }
Tiger Huangfbfc3ab2020-05-06 01:02:31 +08001227 if (!localSource.equals(mLastDispatchedState.peekSource(type))) {
1228 // The server state is not what we expected. This can happen while we don't have
1229 // the control. Since we have the control now, we need to send our request again
1230 // to modify the server state.
1231 changed = true;
1232 }
Jorim Jaggie35c0592018-11-06 16:21:08 +01001233 }
1234 }
Tiger Huang173480f2020-04-29 01:05:42 +08001235 if (!changed) {
1236 return;
1237 }
1238 mHost.onInsetsModified(mRequestedState);
Jorim Jaggic8d60382018-10-31 17:06:06 +01001239 }
1240
Taran Singh8321a2a42020-04-21 17:26:50 -07001241 @VisibleForTesting
1242 public void applyAnimation(@InsetsType final int types, boolean show, boolean fromIme) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -08001243 if (types == 0) {
1244 // nothing to animate.
Taran Singh85661e32020-05-07 14:45:34 -07001245 if (DEBUG) Log.d(TAG, "applyAnimation, nothing to animate");
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -08001246 return;
1247 }
Tarandeep Singha6f35612019-01-11 19:50:46 -08001248
Jorim Jaggibf87c152020-04-22 17:18:25 +02001249 boolean hasAnimationCallbacks = mHost.hasAnimationCallbacks();
Tiger Huangf2321192020-05-21 22:16:14 +08001250 final InternalAnimationControlListener listener = new InternalAnimationControlListener(
Taran Singh42914102020-06-10 13:42:49 -07001251 show, hasAnimationCallbacks, types, mAnimationsDisabled,
1252 mHost.dipToPx(InternalAnimationControlListener.FLOATING_IME_BOTTOM_INSET));
Jorim Jaggi5875cca2020-03-17 13:44:57 +01001253
Tarandeep Singha6f35612019-01-11 19:50:46 -08001254 // Show/hide animations always need to be relative to the display frame, in order that shown
1255 // and hidden state insets are correct.
Tarandeep Singh54554e22019-11-01 14:43:05 -07001256 controlAnimationUnchecked(
Adrian Roos5ad8cd22020-03-12 18:30:54 +00001257 types, null /* cancellationSignal */, listener, mState.getDisplayFrame(), fromIme,
Jorim Jaggi5875cca2020-03-17 13:44:57 +01001258 listener.getDurationMs(), listener.getInterpolator(),
1259 show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE,
1260 show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN,
1261 !hasAnimationCallbacks /* useInsetsAnimationThread */);
1262
Jorim Jaggi6d5c8012020-02-28 01:40:27 +01001263 }
1264
Tarandeep Singhb9538cd2020-02-20 17:51:18 -08001265 private void hideDirectly(
1266 @InsetsType int types, boolean animationFinished, @AnimationType int animationType) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -08001267 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
1268 for (int i = internalTypes.size() - 1; i >= 0; i--) {
Tarandeep Singhb9538cd2020-02-20 17:51:18 -08001269 getSourceConsumer(internalTypes.valueAt(i)).hide(animationFinished, animationType);
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -08001270 }
1271 }
1272
Tiger Huang332793b2019-10-29 23:21:27 +08001273 private void showDirectly(@InsetsType int types) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -08001274 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
1275 for (int i = internalTypes.size() - 1; i >= 0; i--) {
Jorim Jaggi3182ef12020-01-30 00:16:18 +01001276 getSourceConsumer(internalTypes.valueAt(i)).show(false /* fromIme */);
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -08001277 }
1278 }
1279
1280 /**
Tiger Huang332793b2019-10-29 23:21:27 +08001281 * Cancel on-going animation to show/hide {@link InsetsType}.
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -08001282 */
1283 @VisibleForTesting
Jorim Jaggi31e52542020-05-04 23:23:25 +02001284 public void cancelExistingAnimations() {
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +01001285 cancelExistingControllers(all());
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -08001286 }
1287
Jorim Jaggif96c90a2018-09-26 16:55:15 +02001288 void dump(String prefix, PrintWriter pw) {
1289 pw.println(prefix); pw.println("InsetsController:");
1290 mState.dump(prefix + " ", pw);
1291 }
Jorim Jaggi02a741f2018-12-12 17:40:19 -08001292
Jorim Jaggifae3e272019-01-14 14:05:05 +01001293 @VisibleForTesting
Yunfan Chen02abf552019-12-05 14:51:09 +09001294 @Override
Jorim Jaggia51168a2019-12-27 15:17:44 +01001295 public void startAnimation(InsetsAnimationControlImpl controller,
Adrian Roosdb5b0c22020-02-12 15:05:27 -08001296 WindowInsetsAnimationControlListener listener, int types,
Jorim Jaggi6d5c8012020-02-28 01:40:27 +01001297 WindowInsetsAnimation animation, Bounds bounds) {
Jorim Jaggibf87c152020-04-22 17:18:25 +02001298 mHost.dispatchWindowInsetsAnimationPrepare(animation);
1299 mHost.addOnPreDrawRunnable(() -> {
1300 if (controller.isCancelled()) {
Taran Singh85661e32020-05-07 14:45:34 -07001301 if (WARN) Log.w(TAG, "startAnimation canceled before preDraw");
Jorim Jaggibf87c152020-04-22 17:18:25 +02001302 return;
Jorim Jaggia51168a2019-12-27 15:17:44 +01001303 }
Jorim Jaggicb28ae62020-05-14 17:46:32 +02001304 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW,
1305 "InsetsAnimation: " + WindowInsets.Type.toString(types), types);
Jorim Jaggibf87c152020-04-22 17:18:25 +02001306 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
1307 RunningAnimation runningAnimation = mRunningAnimations.get(i);
1308 if (runningAnimation.runner == controller) {
1309 runningAnimation.startDispatched = true;
1310 }
1311 }
1312 mHost.dispatchWindowInsetsAnimationStart(animation, bounds);
1313 mStartingAnimation = true;
1314 controller.mReadyDispatched = true;
1315 listener.onReady(controller, types);
1316 mStartingAnimation = false;
Jorim Jaggia51168a2019-12-27 15:17:44 +01001317 });
Jorim Jaggi02a741f2018-12-12 17:40:19 -08001318 }
1319
Jorim Jaggifae3e272019-01-14 14:05:05 +01001320 @VisibleForTesting
Adrian Roosdb5b0c22020-02-12 15:05:27 -08001321 public void dispatchAnimationEnd(WindowInsetsAnimation animation) {
Jorim Jaggicb28ae62020-05-14 17:46:32 +02001322 Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW,
1323 "InsetsAnimation: " + WindowInsets.Type.toString(animation.getTypeMask()),
1324 animation.getTypeMask());
Jorim Jaggibf87c152020-04-22 17:18:25 +02001325 mHost.dispatchWindowInsetsAnimationEnd(animation);
Jorim Jaggi02a741f2018-12-12 17:40:19 -08001326 }
1327
Jorim Jaggifae3e272019-01-14 14:05:05 +01001328 @VisibleForTesting
Yunfan Chen02abf552019-12-05 14:51:09 +09001329 @Override
Adrian Roos6a4448f2020-04-01 15:01:08 +02001330 public void scheduleApplyChangeInsets(InsetsAnimationControlRunner runner) {
1331 if (mStartingAnimation || runner.getAnimationType() == ANIMATION_TYPE_USER) {
Jorim Jaggia51168a2019-12-27 15:17:44 +01001332 mAnimCallback.run();
1333 mAnimCallbackScheduled = false;
1334 return;
1335 }
Jorim Jaggi02a741f2018-12-12 17:40:19 -08001336 if (!mAnimCallbackScheduled) {
Jorim Jaggibf87c152020-04-22 17:18:25 +02001337 mHost.postInsetsAnimationCallback(mAnimCallback);
Jorim Jaggi02a741f2018-12-12 17:40:19 -08001338 mAnimCallbackScheduled = true;
1339 }
1340 }
Jorim Jaggib7848b72018-12-28 14:38:21 +01001341
1342 @Override
Tiger Huang7238a052020-01-10 20:37:01 +08001343 public void setSystemBarsAppearance(@Appearance int appearance, @Appearance int mask) {
Jorim Jaggibf87c152020-04-22 17:18:25 +02001344 mHost.setSystemBarsAppearance(appearance, mask);
Jorim Jaggib7848b72018-12-28 14:38:21 +01001345 }
1346
1347 @Override
Tiger Huang7238a052020-01-10 20:37:01 +08001348 public @Appearance int getSystemBarsAppearance() {
Jorim Jaggibf87c152020-04-22 17:18:25 +02001349 return mHost.getSystemBarsAppearance();
Tiger Huang7238a052020-01-10 20:37:01 +08001350 }
1351
1352 @Override
Yunfan Chenfae0aea2020-02-22 20:57:57 +09001353 public void setCaptionInsetsHeight(int height) {
1354 mCaptionInsetsHeight = height;
1355 }
1356
1357 @Override
Jorim Jaggib7848b72018-12-28 14:38:21 +01001358 public void setSystemBarsBehavior(@Behavior int behavior) {
Jorim Jaggibf87c152020-04-22 17:18:25 +02001359 mHost.setSystemBarsBehavior(behavior);
Jorim Jaggib7848b72018-12-28 14:38:21 +01001360 }
Tiger Huang7238a052020-01-10 20:37:01 +08001361
1362 @Override
1363 public @Appearance int getSystemBarsBehavior() {
Jorim Jaggibf87c152020-04-22 17:18:25 +02001364 return mHost.getSystemBarsBehavior();
Tiger Huang7238a052020-01-10 20:37:01 +08001365 }
Rob Carr521e3632020-03-09 11:22:30 -07001366
Tiger Huangf2321192020-05-21 22:16:14 +08001367 @Override
1368 public void setAnimationsDisabled(boolean disable) {
1369 mAnimationsDisabled = disable;
1370 }
1371
Jorim Jaggied35b172020-03-06 00:13:57 +01001372 private @InsetsType int calculateControllableTypes() {
Jorim Jaggied35b172020-03-06 00:13:57 +01001373 @InsetsType int result = 0;
1374 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
1375 InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
Tiger Huang618dbe022020-06-19 00:12:55 +08001376 InsetsSource source = mState.peekSource(consumer.mType);
1377 if (consumer.getControl() != null && source != null && source.isUserControllable()) {
Jorim Jaggied35b172020-03-06 00:13:57 +01001378 result |= toPublicType(consumer.mType);
1379 }
1380 }
Adrian Roos8d04bcb2020-05-29 18:01:04 +02001381 return result & ~mState.calculateUncontrollableInsetsFromFrame(mFrame);
Jorim Jaggied35b172020-03-06 00:13:57 +01001382 }
1383
1384 /**
1385 * @return The types that are now animating due to a listener invoking control/show/hide
1386 */
1387 private @InsetsType int invokeControllableInsetsChangedListeners() {
Tiger Huang618dbe022020-06-19 00:12:55 +08001388 mHandler.removeCallbacks(mInvokeControllableInsetsChangedListeners);
Jorim Jaggied35b172020-03-06 00:13:57 +01001389 mLastStartedAnimTypes = 0;
1390 @InsetsType int types = calculateControllableTypes();
1391 int size = mControllableInsetsChangedListeners.size();
1392 for (int i = 0; i < size; i++) {
1393 mControllableInsetsChangedListeners.get(i).onControllableInsetsChanged(this, types);
1394 }
1395 return mLastStartedAnimTypes;
1396 }
1397
1398 @Override
1399 public void addOnControllableInsetsChangedListener(
1400 OnControllableInsetsChangedListener listener) {
1401 Objects.requireNonNull(listener);
1402 mControllableInsetsChangedListeners.add(listener);
1403 listener.onControllableInsetsChanged(this, calculateControllableTypes());
1404 }
1405
1406 @Override
1407 public void removeOnControllableInsetsChangedListener(
1408 OnControllableInsetsChangedListener listener) {
1409 Objects.requireNonNull(listener);
1410 mControllableInsetsChangedListeners.remove(listener);
1411 }
1412
Jorim Jaggibf87c152020-04-22 17:18:25 +02001413 @Override
Rob Carr521e3632020-03-09 11:22:30 -07001414 public void releaseSurfaceControlFromRt(SurfaceControl sc) {
Jorim Jaggibf87c152020-04-22 17:18:25 +02001415 mHost.releaseSurfaceControlFromRt(sc);
1416 }
1417
Adrian Roosc22eec92020-06-12 18:48:10 +02001418 @Override
1419 public void reportPerceptible(int types, boolean perceptible) {
1420 final ArraySet<Integer> internalTypes = toInternalType(types);
1421 final int size = mSourceConsumers.size();
1422 for (int i = 0; i < size; i++) {
1423 final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
1424 if (internalTypes.contains(consumer.getType())) {
1425 consumer.onPerceptible(perceptible);
1426 }
1427 }
1428 }
1429
Jorim Jaggibf87c152020-04-22 17:18:25 +02001430 Host getHost() {
1431 return mHost;
Rob Carr521e3632020-03-09 11:22:30 -07001432 }
Jorim Jaggif96c90a2018-09-26 16:55:15 +02001433}