blob: dd48d554f296bc6a12bdd6a0e4036991e2de4299 [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;
Jorim Jaggicb28ae62020-05-14 17:46:32 +020038import android.os.Trace;
Jorim Jaggib6030952018-10-23 18:31:52 +020039import android.util.ArraySet;
Taran Singh85661e32020-05-07 14:45:34 -070040import android.util.Log;
Tarandeep Singh46d59f02019-01-29 18:09:15 -080041import android.util.Pair;
Jorim Jaggib6030952018-10-23 18:31:52 +020042import android.util.SparseArray;
Tarandeep Singh46d59f02019-01-29 18:09:15 -080043import android.view.InsetsSourceConsumer.ShowResult;
Tiger Huang332793b2019-10-29 23:21:27 +080044import android.view.InsetsState.InternalInsetsType;
Jorim Jaggib6030952018-10-23 18:31:52 +020045import android.view.SurfaceControl.Transaction;
Tarandeep Singh46d59f02019-01-29 18:09:15 -080046import android.view.WindowInsets.Type;
Tiger Huang332793b2019-10-29 23:21:27 +080047import android.view.WindowInsets.Type.InsetsType;
Adrian Roosdb5b0c22020-02-12 15:05:27 -080048import android.view.WindowInsetsAnimation.Bounds;
Jorim Jaggi4e04eb22020-01-09 16:42:14 +010049import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
Jorim Jaggi79742592019-01-18 17:36:10 +010050import android.view.animation.Interpolator;
Jorim Jaggi5875cca2020-03-17 13:44:57 +010051import android.view.animation.LinearInterpolator;
Jorim Jaggi79742592019-01-18 17:36:10 +010052import android.view.animation.PathInterpolator;
Jorim Jaggibf87c152020-04-22 17:18:25 +020053import android.view.inputmethod.InputMethodManager;
Jorim Jaggib6030952018-10-23 18:31:52 +020054
55import com.android.internal.annotations.VisibleForTesting;
Jorim Jaggi6d5c8012020-02-28 01:40:27 +010056import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
Jorim Jaggif96c90a2018-09-26 16:55:15 +020057
58import java.io.PrintWriter;
Jorim Jaggia51168a2019-12-27 15:17:44 +010059import java.lang.annotation.Retention;
60import java.lang.annotation.RetentionPolicy;
Jorim Jaggi5bb571d2018-11-06 14:42:04 +010061import java.util.ArrayList;
Adrian Roosdb5b0c22020-02-12 15:05:27 -080062import java.util.Collections;
63import java.util.List;
Jorim Jaggied35b172020-03-06 00:13:57 +010064import java.util.Objects;
Jorim Jaggid7f10ed2020-01-08 21:41:55 +010065import java.util.function.BiFunction;
Jorim Jaggif96c90a2018-09-26 16:55:15 +020066
67/**
68 * Implements {@link WindowInsetsController} on the client.
Jorim Jaggib6030952018-10-23 18:31:52 +020069 * @hide
Jorim Jaggif96c90a2018-09-26 16:55:15 +020070 */
Yunfan Chen02abf552019-12-05 14:51:09 +090071public class InsetsController implements WindowInsetsController, InsetsAnimationControlCallbacks {
Jorim Jaggif96c90a2018-09-26 16:55:15 +020072
Adrian Roosa79b8852020-05-26 21:25:15 +020073 private int mTypesBeingCancelled;
74
Jorim Jaggibf87c152020-04-22 17:18:25 +020075 public interface Host {
76
77 Handler getHandler();
78
79 /**
80 * Notifies host that {@link InsetsController#getState()} has changed.
81 */
82 void notifyInsetsChanged();
83
84 void dispatchWindowInsetsAnimationPrepare(@NonNull WindowInsetsAnimation animation);
85 Bounds dispatchWindowInsetsAnimationStart(
86 @NonNull WindowInsetsAnimation animation, @NonNull Bounds bounds);
87 WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull WindowInsets insets,
88 @NonNull List<WindowInsetsAnimation> runningAnimations);
89 void dispatchWindowInsetsAnimationEnd(@NonNull WindowInsetsAnimation animation);
90
91 /**
92 * Requests host to apply surface params in synchronized manner.
93 */
94 void applySurfaceParams(final SyncRtSurfaceTransactionApplier.SurfaceParams... params);
95
96 /**
97 * @see ViewRootImpl#updateCompatSysUiVisibility(int, boolean, boolean)
98 */
99 void updateCompatSysUiVisibility(@InternalInsetsType int type, boolean visible,
100 boolean hasControl);
101
102 /**
103 * Called when insets have been modified by the client and should be reported back to WM.
104 */
105 void onInsetsModified(InsetsState insetsState);
106
107 /**
108 * @return Whether the host has any callbacks it wants to synchronize the animations with.
109 * If there are no callbacks, the animation will be off-loaded to another thread and
110 * slightly different animation curves are picked.
111 */
112 boolean hasAnimationCallbacks();
113
114 /**
115 * @see WindowInsetsController#setSystemBarsAppearance
116 */
117 void setSystemBarsAppearance(@Appearance int appearance, @Appearance int mask);
118
119 /**
120 * @see WindowInsetsController#getSystemBarsAppearance()
121 */
122 @Appearance int getSystemBarsAppearance();
123
124 /**
125 * @see WindowInsetsController#setSystemBarsBehavior
126 */
127 void setSystemBarsBehavior(@Behavior int behavior);
128
129 /**
130 * @see WindowInsetsController#getSystemBarsBehavior
131 */
132 @Behavior int getSystemBarsBehavior();
133
134 /**
135 * Releases a surface and ensure that this is done after {@link #applySurfaceParams} has
136 * finished applying params.
137 */
138 void releaseSurfaceControlFromRt(SurfaceControl surfaceControl);
139
140 /**
141 * If this host is a view hierarchy, adds a pre-draw runnable to ensure proper ordering as
142 * described in {@link WindowInsetsAnimation.Callback#onPrepare}.
143 *
144 * If this host isn't a view hierarchy, the runnable can be executed immediately.
145 */
146 void addOnPreDrawRunnable(Runnable r);
147
148 /**
149 * Adds a runnbale to be executed during {@link Choreographer#CALLBACK_INSETS_ANIMATION}
150 * phase.
151 */
152 void postInsetsAnimationCallback(Runnable r);
153
154 /**
155 * Obtains {@link InputMethodManager} instance from host.
156 */
157 InputMethodManager getInputMethodManager();
Taran Singh85661e32020-05-07 14:45:34 -0700158
159 /**
160 * @return title of the rootView, if it has one.
161 * Note: this method is for debugging purposes only.
162 */
163 @Nullable
164 String getRootViewTitle();
Jorim Jaggibf87c152020-04-22 17:18:25 +0200165 }
166
Taran Singh85661e32020-05-07 14:45:34 -0700167 private static final String TAG = "InsetsController";
Jorim Jaggi79742592019-01-18 17:36:10 +0100168 private static final int ANIMATION_DURATION_SHOW_MS = 275;
169 private static final int ANIMATION_DURATION_HIDE_MS = 340;
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100170
171 private static final int ANIMATION_DURATION_SYNC_IME_MS = 285;
172 private static final int ANIMATION_DURATION_UNSYNC_IME_MS = 200;
173
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100174 private static final int PENDING_CONTROL_TIMEOUT_MS = 2000;
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800175
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100176 public static final Interpolator SYSTEM_BARS_INTERPOLATOR =
177 new PathInterpolator(0.4f, 0f, 0.2f, 1f);
178 private static final Interpolator SYNC_IME_INTERPOLATOR =
179 new PathInterpolator(0.2f, 0f, 0f, 1f);
180 private static final Interpolator LINEAR_OUT_SLOW_IN_INTERPOLATOR =
181 new PathInterpolator(0, 0, 0.2f, 1f);
182 private static final Interpolator FAST_OUT_LINEAR_IN_INTERPOLATOR =
183 new PathInterpolator(0.4f, 0f, 1f, 1f);
Tarandeep Singh54554e22019-11-01 14:43:05 -0700184
Taran Singh85661e32020-05-07 14:45:34 -0700185 static final boolean DEBUG = false;
186 static final boolean WARN = false;
187
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800188 /**
Jorim Jaggia51168a2019-12-27 15:17:44 +0100189 * Layout mode during insets animation: The views should be laid out as if the changing inset
190 * types are fully shown. Before starting the animation, {@link View#onApplyWindowInsets} will
191 * be called as if the changing insets types are shown, which will result in the views being
192 * laid out as if the insets are fully shown.
193 */
Yunfan Chenb5d2db72019-12-06 15:43:43 +0900194 public static final int LAYOUT_INSETS_DURING_ANIMATION_SHOWN = 0;
Jorim Jaggia51168a2019-12-27 15:17:44 +0100195
196 /**
197 * Layout mode during insets animation: The views should be laid out as if the changing inset
198 * types are fully hidden. Before starting the animation, {@link View#onApplyWindowInsets} will
199 * be called as if the changing insets types are hidden, which will result in the views being
200 * laid out as if the insets are fully hidden.
201 */
Yunfan Chenb5d2db72019-12-06 15:43:43 +0900202 public static final int LAYOUT_INSETS_DURING_ANIMATION_HIDDEN = 1;
Jorim Jaggia51168a2019-12-27 15:17:44 +0100203
204 /**
205 * Determines the behavior of how the views should be laid out during an insets animation that
206 * is controlled by the application by calling {@link #controlWindowInsetsAnimation}.
207 * <p>
208 * When the animation is system-initiated, the layout mode is always chosen such that the
209 * pre-animation layout will represent the opposite of the starting state, i.e. when insets
210 * are appearing, {@link #LAYOUT_INSETS_DURING_ANIMATION_SHOWN} will be used. When insets
211 * are disappearing, {@link #LAYOUT_INSETS_DURING_ANIMATION_HIDDEN} will be used.
212 */
213 @Retention(RetentionPolicy.SOURCE)
214 @IntDef(value = {LAYOUT_INSETS_DURING_ANIMATION_SHOWN,
215 LAYOUT_INSETS_DURING_ANIMATION_HIDDEN})
216 @interface LayoutInsetsDuringAnimation {
217 }
218
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +0100219 /** Not running an animation. */
220 @VisibleForTesting
221 public static final int ANIMATION_TYPE_NONE = -1;
222
223 /** Running animation will show insets */
224 @VisibleForTesting
225 public static final int ANIMATION_TYPE_SHOW = 0;
226
227 /** Running animation will hide insets */
228 @VisibleForTesting
229 public static final int ANIMATION_TYPE_HIDE = 1;
230
231 /** Running animation is controlled by user via {@link #controlWindowInsetsAnimation} */
232 @VisibleForTesting
233 public static final int ANIMATION_TYPE_USER = 2;
234
235 @Retention(RetentionPolicy.SOURCE)
236 @IntDef(value = {ANIMATION_TYPE_NONE, ANIMATION_TYPE_SHOW, ANIMATION_TYPE_HIDE,
237 ANIMATION_TYPE_USER})
238 @interface AnimationType {
239 }
240
Jorim Jaggia51168a2019-12-27 15:17:44 +0100241 /**
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800242 * Translation animation evaluator.
243 */
244 private static TypeEvaluator<Insets> sEvaluator = (fraction, startValue, endValue) -> Insets.of(
Jorim Jaggi956ca412019-01-07 14:49:14 +0100245 (int) (startValue.left + fraction * (endValue.left - startValue.left)),
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800246 (int) (startValue.top + fraction * (endValue.top - startValue.top)),
Jorim Jaggi956ca412019-01-07 14:49:14 +0100247 (int) (startValue.right + fraction * (endValue.right - startValue.right)),
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800248 (int) (startValue.bottom + fraction * (endValue.bottom - startValue.bottom)));
249
250 /**
Yunfan Chen4523d5d2020-01-16 16:54:00 +0900251 * The default implementation of listener, to be used by InsetsController and InsetsPolicy to
252 * animate insets.
253 */
254 public static class InternalAnimationControlListener
255 implements WindowInsetsAnimationControlListener {
Tarandeep Singh54554e22019-11-01 14:43:05 -0700256
257 private WindowInsetsAnimationController mController;
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100258 private ValueAnimator mAnimator;
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100259 private final boolean mShow;
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100260 private final boolean mHasAnimationCallbacks;
261 private final @InsetsType int mRequestedTypes;
262 private final long mDurationMs;
Tiger Huangf2321192020-05-21 22:16:14 +0800263 private final boolean mDisable;
Tarandeep Singh54554e22019-11-01 14:43:05 -0700264
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100265 private ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal =
266 new ThreadLocal<AnimationHandler>() {
267 @Override
268 protected AnimationHandler initialValue() {
269 AnimationHandler handler = new AnimationHandler();
270 handler.setProvider(new SfVsyncFrameCallbackProvider());
271 return handler;
272 }
273 };
274
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100275 public InternalAnimationControlListener(boolean show, boolean hasAnimationCallbacks,
Tiger Huangf2321192020-05-21 22:16:14 +0800276 int requestedTypes, boolean disable) {
Tarandeep Singh54554e22019-11-01 14:43:05 -0700277 mShow = show;
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100278 mHasAnimationCallbacks = hasAnimationCallbacks;
279 mRequestedTypes = requestedTypes;
280 mDurationMs = calculateDurationMs();
Tiger Huangf2321192020-05-21 22:16:14 +0800281 mDisable = disable;
Tarandeep Singh54554e22019-11-01 14:43:05 -0700282 }
283
284 @Override
285 public void onReady(WindowInsetsAnimationController controller, int types) {
286 mController = controller;
Taran Singh85661e32020-05-07 14:45:34 -0700287 if (DEBUG) Log.d(TAG, "default animation onReady types: " + types);
Jorim Jaggia51168a2019-12-27 15:17:44 +0100288
Tiger Huangf2321192020-05-21 22:16:14 +0800289 if (mDisable) {
290 onAnimationFinish();
291 return;
292 }
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100293 mAnimator = ValueAnimator.ofFloat(0f, 1f);
294 mAnimator.setDuration(mDurationMs);
295 mAnimator.setInterpolator(new LinearInterpolator());
296 Insets start = mShow
297 ? controller.getHiddenStateInsets()
298 : controller.getShownStateInsets();
299 Insets end = mShow
300 ? controller.getShownStateInsets()
301 : controller.getHiddenStateInsets();
302 Interpolator insetsInterpolator = getInterpolator();
303 Interpolator alphaInterpolator = getAlphaInterpolator();
304 mAnimator.addUpdateListener(animation -> {
305 float rawFraction = animation.getAnimatedFraction();
306 float alphaFraction = mShow
307 ? rawFraction
308 : 1 - rawFraction;
309 float insetsFraction = insetsInterpolator.getInterpolation(rawFraction);
310 controller.setInsetsAndAlpha(
311 sEvaluator.evaluate(insetsFraction, start, end),
312 alphaInterpolator.getInterpolation(alphaFraction),
313 rawFraction);
Taran Singh85661e32020-05-07 14:45:34 -0700314 if (DEBUG) Log.d(TAG, "Default animation setInsetsAndAlpha fraction: "
315 + insetsFraction);
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100316 });
Tarandeep Singh54554e22019-11-01 14:43:05 -0700317 mAnimator.addListener(new AnimatorListenerAdapter() {
318
319 @Override
320 public void onAnimationEnd(Animator animation) {
321 onAnimationFinish();
322 }
323 });
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100324 if (!mHasAnimationCallbacks) {
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100325 mAnimator.setAnimationHandler(mSfAnimationHandlerThreadLocal.get());
326 }
Tarandeep Singh54554e22019-11-01 14:43:05 -0700327 mAnimator.start();
328 }
329
330 @Override
Adrian Roos5d557ed2020-03-17 20:04:35 +0100331 public void onFinished(WindowInsetsAnimationController controller) {
Taran Singh85661e32020-05-07 14:45:34 -0700332 if (DEBUG) Log.d(TAG, "InternalAnimationControlListener onFinished types:"
333 + Type.toString(mRequestedTypes));
Adrian Roos5d557ed2020-03-17 20:04:35 +0100334 }
335
336 @Override
337 public void onCancelled(WindowInsetsAnimationController controller) {
Tarandeep Singh54554e22019-11-01 14:43:05 -0700338 // Animator can be null when it is cancelled before onReady() completes.
339 if (mAnimator != null) {
340 mAnimator.cancel();
341 }
Taran Singh85661e32020-05-07 14:45:34 -0700342 if (DEBUG) Log.d(TAG, "InternalAnimationControlListener onCancelled types:"
343 + mRequestedTypes);
Tarandeep Singh54554e22019-11-01 14:43:05 -0700344 }
345
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100346 Interpolator getInterpolator() {
347 if ((mRequestedTypes & ime()) != 0) {
348 if (mHasAnimationCallbacks) {
349 return SYNC_IME_INTERPOLATOR;
350 } else if (mShow) {
351 return LINEAR_OUT_SLOW_IN_INTERPOLATOR;
352 } else {
353 return FAST_OUT_LINEAR_IN_INTERPOLATOR;
354 }
355 } else {
356 return SYSTEM_BARS_INTERPOLATOR;
357 }
Tarandeep Singh54554e22019-11-01 14:43:05 -0700358 }
359
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100360 Interpolator getAlphaInterpolator() {
361 if ((mRequestedTypes & ime()) != 0) {
362 if (mHasAnimationCallbacks) {
363 return input -> 1f;
364 } else if (mShow) {
365
366 // Alpha animation takes half the time with linear interpolation;
367 return input -> Math.min(1f, 2 * input);
368 } else {
369 return FAST_OUT_LINEAR_IN_INTERPOLATOR;
370 }
371 } else {
372 return input -> 1f;
373 }
374 }
375
376 protected void onAnimationFinish() {
377 mController.finish(mShow);
Taran Singh85661e32020-05-07 14:45:34 -0700378 if (DEBUG) Log.d(TAG, "onAnimationFinish showOnFinish: " + mShow);
Tarandeep Singh54554e22019-11-01 14:43:05 -0700379 }
380
Yunfan Chenb5d2db72019-12-06 15:43:43 +0900381 /**
382 * To get the animation duration in MS.
383 */
384 public long getDurationMs() {
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100385 return mDurationMs;
386 }
387
388 private long calculateDurationMs() {
389 if ((mRequestedTypes & ime()) != 0) {
390 if (mHasAnimationCallbacks) {
391 return ANIMATION_DURATION_SYNC_IME_MS;
392 } else {
393 return ANIMATION_DURATION_UNSYNC_IME_MS;
394 }
395 } else {
396 return mShow ? ANIMATION_DURATION_SHOW_MS : ANIMATION_DURATION_HIDE_MS;
Tarandeep Singh54554e22019-11-01 14:43:05 -0700397 }
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800398 }
399 }
400
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +0100401 /**
402 * Represents a running animation
403 */
404 private static class RunningAnimation {
405
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100406 RunningAnimation(InsetsAnimationControlRunner runner, int type) {
407 this.runner = runner;
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +0100408 this.type = type;
409 }
410
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100411 final InsetsAnimationControlRunner runner;
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +0100412 final @AnimationType int type;
Jorim Jaggi580aef52020-02-26 18:28:28 +0100413
414 /**
415 * Whether {@link WindowInsetsAnimation.Callback#onStart(WindowInsetsAnimation, Bounds)} has
416 * been dispatched already for this animation.
417 */
418 boolean startDispatched;
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +0100419 }
420
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100421 /**
422 * Represents a control request that we had to defer because we are waiting for the IME to
423 * process our show request.
424 */
425 private static class PendingControlRequest {
426
427 PendingControlRequest(@InsetsType int types, WindowInsetsAnimationControlListener listener,
428 long durationMs, Interpolator interpolator, @AnimationType int animationType,
Adrian Roos3406fb92020-02-10 18:38:59 -0800429 @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100430 CancellationSignal cancellationSignal, boolean useInsetsAnimationThread) {
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100431 this.types = types;
432 this.listener = listener;
433 this.durationMs = durationMs;
434 this.interpolator = interpolator;
435 this.animationType = animationType;
436 this.layoutInsetsDuringAnimation = layoutInsetsDuringAnimation;
Adrian Roos3406fb92020-02-10 18:38:59 -0800437 this.cancellationSignal = cancellationSignal;
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100438 this.useInsetsAnimationThread = useInsetsAnimationThread;
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100439 }
440
441 final @InsetsType int types;
442 final WindowInsetsAnimationControlListener listener;
443 final long durationMs;
444 final Interpolator interpolator;
445 final @AnimationType int animationType;
446 final @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation;
Adrian Roos3406fb92020-02-10 18:38:59 -0800447 final CancellationSignal cancellationSignal;
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100448 final boolean useInsetsAnimationThread;
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100449 }
Yunfan Chen4523d5d2020-01-16 16:54:00 +0900450
Tiger Huang173480f2020-04-29 01:05:42 +0800451 /** The local state */
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200452 private final InsetsState mState = new InsetsState();
Tiger Huang173480f2020-04-29 01:05:42 +0800453
454 /** The state dispatched from server */
455 private final InsetsState mLastDispatchedState = new InsetsState();
456
457 /** The state sent to server */
458 private final InsetsState mRequestedState = new InsetsState();
Jorim Jaggie35c0592018-11-06 16:21:08 +0100459
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200460 private final Rect mFrame = new Rect();
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100461 private final BiFunction<InsetsController, Integer, InsetsSourceConsumer> mConsumerCreator;
Jorim Jaggib6030952018-10-23 18:31:52 +0200462 private final SparseArray<InsetsSourceConsumer> mSourceConsumers = new SparseArray<>();
Jorim Jaggibf87c152020-04-22 17:18:25 +0200463 private final Host mHost;
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100464 private final Handler mHandler;
Jorim Jaggib6030952018-10-23 18:31:52 +0200465
466 private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>();
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +0100467 private final ArrayList<RunningAnimation> mRunningAnimations = new ArrayList<>();
Jorim Jaggi580aef52020-02-26 18:28:28 +0100468 private final ArrayList<WindowInsetsAnimation> mTmpRunningAnims = new ArrayList<>();
469 private final List<WindowInsetsAnimation> mUnmodifiableTmpRunningAnims =
470 Collections.unmodifiableList(mTmpRunningAnims);
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100471 private final ArrayList<InsetsAnimationControlImpl> mTmpFinishedControls = new ArrayList<>();
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800472 private WindowInsets mLastInsets;
473
474 private boolean mAnimCallbackScheduled;
475
476 private final Runnable mAnimCallback;
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200477
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100478 /** Pending control request that is waiting on IME to be ready to be shown */
479 private PendingControlRequest mPendingImeControlRequest;
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800480
Jorim Jaggi648e5882019-01-24 13:24:02 +0100481 private int mLastLegacySoftInputMode;
Jorim Jaggi7f761872020-01-10 18:24:27 +0100482 private int mLastLegacySystemUiFlags;
Jorim Jaggi580aef52020-02-26 18:28:28 +0100483 private DisplayCutout mLastDisplayCutout;
Jorim Jaggia51168a2019-12-27 15:17:44 +0100484 private boolean mStartingAnimation;
Yunfan Chenfae0aea2020-02-22 20:57:57 +0900485 private int mCaptionInsetsHeight = 0;
Tiger Huangf2321192020-05-21 22:16:14 +0800486 private boolean mAnimationsDisabled;
Jorim Jaggi648e5882019-01-24 13:24:02 +0100487
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100488 private Runnable mPendingControlTimeout = this::abortPendingImeControlRequest;
Jorim Jaggied35b172020-03-06 00:13:57 +0100489 private final ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners
490 = new ArrayList<>();
491
492 /** Set of inset types for which an animation was started since last resetting this field */
493 private @InsetsType int mLastStartedAnimTypes;
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100494
Jorim Jaggibf87c152020-04-22 17:18:25 +0200495 public InsetsController(Host host) {
496 this(host, (controller, type) -> {
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100497 if (type == ITYPE_IME) {
498 return new ImeInsetsSourceConsumer(controller.mState, Transaction::new, controller);
499 } else {
500 return new InsetsSourceConsumer(type, controller.mState, Transaction::new,
501 controller);
502 }
Jorim Jaggibf87c152020-04-22 17:18:25 +0200503 }, host.getHandler());
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100504 }
505
506 @VisibleForTesting
Jorim Jaggibf87c152020-04-22 17:18:25 +0200507 public InsetsController(Host host,
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100508 BiFunction<InsetsController, Integer, InsetsSourceConsumer> consumerCreator,
509 Handler handler) {
Jorim Jaggibf87c152020-04-22 17:18:25 +0200510 mHost = host;
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100511 mConsumerCreator = consumerCreator;
512 mHandler = handler;
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800513 mAnimCallback = () -> {
514 mAnimCallbackScheduled = false;
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +0100515 if (mRunningAnimations.isEmpty()) {
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800516 return;
517 }
518
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100519 mTmpFinishedControls.clear();
Jorim Jaggi580aef52020-02-26 18:28:28 +0100520 mTmpRunningAnims.clear();
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800521 InsetsState state = new InsetsState(mState, true /* copySources */);
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +0100522 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
Jorim Jaggi580aef52020-02-26 18:28:28 +0100523 RunningAnimation runningAnimation = mRunningAnimations.get(i);
Taran Singh85661e32020-05-07 14:45:34 -0700524 if (DEBUG) Log.d(TAG, "Running animation type: " + runningAnimation.type);
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100525 InsetsAnimationControlRunner runner = runningAnimation.runner;
526 if (runner instanceof InsetsAnimationControlImpl) {
527 InsetsAnimationControlImpl control = (InsetsAnimationControlImpl) runner;
Jorim Jaggi580aef52020-02-26 18:28:28 +0100528
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100529 // Keep track of running animation to be dispatched. Aggregate it here such that
530 // if it gets finished within applyChangeInsets we still dispatch it to
531 // onProgress.
532 if (runningAnimation.startDispatched) {
533 mTmpRunningAnims.add(control.getAnimation());
534 }
535
536 if (control.applyChangeInsets(state)) {
537 mTmpFinishedControls.add(control);
538 }
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100539 }
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800540 }
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100541
Jorim Jaggi580aef52020-02-26 18:28:28 +0100542 WindowInsets insets = state.calculateInsets(mFrame, mState /* ignoringVisibilityState*/,
543 mLastInsets.isRound(), mLastInsets.shouldAlwaysConsumeSystemBars(),
Jorim Jaggi22488d32020-03-19 01:12:44 +0100544 mLastDisplayCutout, mLastLegacySoftInputMode, mLastLegacySystemUiFlags,
545 null /* typeSideMap */);
Jorim Jaggibf87c152020-04-22 17:18:25 +0200546 mHost.dispatchWindowInsetsAnimationProgress(insets, mUnmodifiableTmpRunningAnims);
Taran Singh85661e32020-05-07 14:45:34 -0700547 if (DEBUG) {
548 for (WindowInsetsAnimation anim : mUnmodifiableTmpRunningAnims) {
549 Log.d(TAG, String.format("Running animation type: %d, progress: %f",
550 anim.getTypeMask(), anim.getInterpolatedFraction()));
551 }
552 }
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100553
554 for (int i = mTmpFinishedControls.size() - 1; i >= 0; i--) {
Adrian Roosdb5b0c22020-02-12 15:05:27 -0800555 dispatchAnimationEnd(mTmpFinishedControls.get(i).getAnimation());
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100556 }
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800557 };
Jorim Jaggic8d60382018-10-31 17:06:06 +0100558 }
559
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100560 @VisibleForTesting
561 public void onFrameChanged(Rect frame) {
Tarandeep Singha6f35612019-01-11 19:50:46 -0800562 if (mFrame.equals(frame)) {
563 return;
564 }
Jorim Jaggibf87c152020-04-22 17:18:25 +0200565 mHost.notifyInsetsChanged();
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200566 mFrame.set(frame);
567 }
568
Tiger Huang4a7835f2019-11-06 00:07:56 +0800569 @Override
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200570 public InsetsState getState() {
571 return mState;
572 }
573
Jorim Jaggi49b9f6c2020-03-24 22:28:38 +0100574 @Override
575 public boolean isRequestedVisible(int type) {
576 return getSourceConsumer(type).isRequestedVisible();
577 }
578
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100579 public InsetsState getLastDispatchedState() {
Tiger Huang173480f2020-04-29 01:05:42 +0800580 return mLastDispatchedState;
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100581 }
582
583 @VisibleForTesting
584 public boolean onStateChanged(InsetsState state) {
Jorim Jaggied312592020-05-25 16:46:56 +0200585 boolean stateChanged = !mState.equals(state, true /* excludingCaptionInsets */,
586 false /* excludeInvisibleIme */)
Yunfan Chenfae0aea2020-02-22 20:57:57 +0900587 || !captionInsetsUnchanged();
Jorim Jaggied312592020-05-25 16:46:56 +0200588 if (!stateChanged && mLastDispatchedState.equals(state)) {
Jorim Jaggic8d60382018-10-31 17:06:06 +0100589 return false;
590 }
Taran Singh85661e32020-05-07 14:45:34 -0700591 if (DEBUG) Log.d(TAG, "onStateChanged: " + state);
Jorim Jaggi33a21832020-04-06 14:15:46 +0200592 updateState(state);
Jorim Jaggied312592020-05-25 16:46:56 +0200593
594 boolean localStateChanged = !mState.equals(mLastDispatchedState,
595 true /* excludingCaptionInsets */, true /* excludeInvisibleIme */);
Tiger Huang173480f2020-04-29 01:05:42 +0800596 mLastDispatchedState.set(state, true /* copySources */);
Jorim Jaggied312592020-05-25 16:46:56 +0200597
Jorim Jaggic8d60382018-10-31 17:06:06 +0100598 applyLocalVisibilityOverride();
Jorim Jaggi22488d32020-03-19 01:12:44 +0100599 if (localStateChanged) {
Jorim Jaggied312592020-05-25 16:46:56 +0200600 if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged, send state to WM: " + mState);
Jorim Jaggibf87c152020-04-22 17:18:25 +0200601 mHost.notifyInsetsChanged();
Tiger Huang173480f2020-04-29 01:05:42 +0800602 updateRequestedState();
Jorim Jaggie35c0592018-11-06 16:21:08 +0100603 }
Jorim Jaggi33a21832020-04-06 14:15:46 +0200604 return true;
605 }
606
607 private void updateState(InsetsState newState) {
608 mState.setDisplayFrame(newState.getDisplayFrame());
Jorim Jaggibfa95a72020-06-18 22:51:49 +0200609 for (int i = 0; i < InsetsState.SIZE; i++) {
610 InsetsSource source = newState.peekSource(i);
611 if (source == null) continue;;
Yohei Yukawaf35971d2020-06-16 21:31:35 +0000612 getSourceConsumer(source.getType()).updateSource(source);
Jorim Jaggi33a21832020-04-06 14:15:46 +0200613 }
Jorim Jaggibfa95a72020-06-18 22:51:49 +0200614 for (int i = 0; i < InsetsState.SIZE; i++) {
615 InsetsSource source = mState.peekSource(i);
616 if (source == null) continue;
Jorim Jaggi33a21832020-04-06 14:15:46 +0200617 if (newState.peekSource(source.getType()) == null) {
618 mState.removeSource(source.getType());
619 }
620 }
Yunfan Chenfae0aea2020-02-22 20:57:57 +0900621 if (mCaptionInsetsHeight != 0) {
622 mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(mFrame.left, mFrame.top,
623 mFrame.right, mFrame.top + mCaptionInsetsHeight));
624 }
625 }
626
627 private boolean captionInsetsUnchanged() {
628 if (mState.peekSource(ITYPE_CAPTION_BAR) == null
629 && mCaptionInsetsHeight == 0) {
630 return true;
631 }
632 if (mState.peekSource(ITYPE_CAPTION_BAR) != null
633 && mCaptionInsetsHeight
634 == mState.peekSource(ITYPE_CAPTION_BAR).getFrame().height()) {
635 return true;
636 }
637 return false;
Jorim Jaggi2751c292020-03-20 23:46:38 +0100638 }
639
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200640 /**
641 * @see InsetsState#calculateInsets
642 */
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100643 @VisibleForTesting
644 public WindowInsets calculateInsets(boolean isScreenRound,
Jorim Jaggi22488d32020-03-19 01:12:44 +0100645 boolean alwaysConsumeSystemBars, DisplayCutout cutout,
646 int legacySoftInputMode, int legacySystemUiFlags) {
Jorim Jaggi648e5882019-01-24 13:24:02 +0100647 mLastLegacySoftInputMode = legacySoftInputMode;
Jorim Jaggi7f761872020-01-10 18:24:27 +0100648 mLastLegacySystemUiFlags = legacySystemUiFlags;
Jorim Jaggi580aef52020-02-26 18:28:28 +0100649 mLastDisplayCutout = cutout;
650 mLastInsets = mState.calculateInsets(mFrame, null /* ignoringVisibilityState*/,
Jorim Jaggi22488d32020-03-19 01:12:44 +0100651 isScreenRound, alwaysConsumeSystemBars, cutout,
652 legacySoftInputMode, legacySystemUiFlags,
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100653 null /* typeSideMap */);
Jorim Jaggi02a741f2018-12-12 17:40:19 -0800654 return mLastInsets;
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200655 }
656
Jorim Jaggib6030952018-10-23 18:31:52 +0200657 /**
Jorim Jaggi22488d32020-03-19 01:12:44 +0100658 * @see InsetsState#calculateVisibleInsets(Rect, int)
Jorim Jaggi4e04eb22020-01-09 16:42:14 +0100659 */
Jorim Jaggi22488d32020-03-19 01:12:44 +0100660 public Rect calculateVisibleInsets(@SoftInputModeFlags int softInputMode) {
661 return mState.calculateVisibleInsets(mFrame, softInputMode);
Jorim Jaggi4e04eb22020-01-09 16:42:14 +0100662 }
663
664 /**
Jorim Jaggib6030952018-10-23 18:31:52 +0200665 * Called when the server has dispatched us a new set of inset controls.
666 */
667 public void onControlsChanged(InsetsSourceControl[] activeControls) {
668 if (activeControls != null) {
669 for (InsetsSourceControl activeControl : activeControls) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800670 if (activeControl != null) {
671 // TODO(b/122982984): Figure out why it can be null.
672 mTmpControlArray.put(activeControl.getType(), activeControl);
673 }
Jorim Jaggib6030952018-10-23 18:31:52 +0200674 }
675 }
676
Tiger Huang173480f2020-04-29 01:05:42 +0800677 final boolean hasControl = mTmpControlArray.size() > 0;
678 final int[] showTypes = new int[1];
679 final int[] hideTypes = new int[1];
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100680
Jorim Jaggib6030952018-10-23 18:31:52 +0200681 // Ensure to update all existing source consumers
682 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
683 final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
684 final InsetsSourceControl control = mTmpControlArray.get(consumer.getType());
685
686 // control may be null, but we still need to update the control to null if it got
687 // revoked.
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100688 consumer.setControl(control, showTypes, hideTypes);
Jorim Jaggib6030952018-10-23 18:31:52 +0200689 }
690
691 // Ensure to create source consumers if not available yet.
692 for (int i = mTmpControlArray.size() - 1; i >= 0; i--) {
693 final InsetsSourceControl control = mTmpControlArray.valueAt(i);
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100694 InsetsSourceConsumer consumer = getSourceConsumer(control.getType());
695 consumer.setControl(control, showTypes, hideTypes);
696
Jorim Jaggib6030952018-10-23 18:31:52 +0200697 }
698 mTmpControlArray.clear();
Jorim Jaggied35b172020-03-06 00:13:57 +0100699
700 // Do not override any animations that the app started in the OnControllableInsetsChanged
701 // listeners.
702 int animatingTypes = invokeControllableInsetsChangedListeners();
703 showTypes[0] &= ~animatingTypes;
704 hideTypes[0] &= ~animatingTypes;
705
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100706 if (showTypes[0] != 0) {
707 applyAnimation(showTypes[0], true /* show */, false /* fromIme */);
708 }
709 if (hideTypes[0] != 0) {
710 applyAnimation(hideTypes[0], false /* show */, false /* fromIme */);
711 }
Jorim Jaggibfa95a72020-06-18 22:51:49 +0200712 if (hasControl && mRequestedState.hasSources()) {
Tiger Huang173480f2020-04-29 01:05:42 +0800713 // We might have changed our requested visibilities while we don't have the control,
714 // so we need to update our requested state once we have control. Otherwise, our
715 // requested state at the server side might be incorrect.
716 updateRequestedState();
717 }
Jorim Jaggib6030952018-10-23 18:31:52 +0200718 }
719
720 @Override
Tiger Huang332793b2019-10-29 23:21:27 +0800721 public void show(@InsetsType int types) {
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800722 show(types, false /* fromIme */);
723 }
724
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100725 @VisibleForTesting
726 public void show(@InsetsType int types, boolean fromIme) {
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100727 // Handle pending request ready in case there was one set.
728 if (fromIme && mPendingImeControlRequest != null) {
729 PendingControlRequest pendingRequest = mPendingImeControlRequest;
730 mPendingImeControlRequest = null;
731 mHandler.removeCallbacks(mPendingControlTimeout);
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000732 controlAnimationUnchecked(
733 pendingRequest.types, pendingRequest.cancellationSignal,
Adrian Roos3406fb92020-02-10 18:38:59 -0800734 pendingRequest.listener, mFrame,
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100735 true /* fromIme */, pendingRequest.durationMs, pendingRequest.interpolator,
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100736 pendingRequest.animationType,
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100737 pendingRequest.layoutInsetsDuringAnimation,
738 pendingRequest.useInsetsAnimationThread);
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100739 return;
740 }
741
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800742 // TODO: Support a ResultReceiver for IME.
743 // TODO(b/123718661): Make show() work for multi-session IME.
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800744 int typesReady = 0;
Jorim Jaggib6030952018-10-23 18:31:52 +0200745 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
746 for (int i = internalTypes.size() - 1; i >= 0; i--) {
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +0100747 @InternalInsetsType int internalType = internalTypes.valueAt(i);
748 @AnimationType int animationType = getAnimationType(internalType);
749 InsetsSourceConsumer consumer = getSourceConsumer(internalType);
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100750 if (consumer.isRequestedVisible() && animationType == ANIMATION_TYPE_NONE
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +0100751 || animationType == ANIMATION_TYPE_SHOW) {
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800752 // no-op: already shown or animating in (because window visibility is
753 // applied before starting animation).
Taran Singh85661e32020-05-07 14:45:34 -0700754 if (DEBUG) Log.d(TAG, String.format(
755 "show ignored for type: %d animType: %d requestedVisible: %s",
756 consumer.getType(), animationType, consumer.isRequestedVisible()));
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800757 continue;
758 }
Adrian Roosa2d2ba72020-05-25 17:44:55 +0200759 if (fromIme && animationType == ANIMATION_TYPE_USER) {
760 // App is already controlling the IME, don't cancel it.
761 continue;
762 }
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800763 typesReady |= InsetsState.toPublicType(consumer.getType());
Jorim Jaggib6030952018-10-23 18:31:52 +0200764 }
Taran Singh85661e32020-05-07 14:45:34 -0700765 if (DEBUG) Log.d(TAG, "show typesReady: " + typesReady);
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800766 applyAnimation(typesReady, true /* show */, fromIme);
Jorim Jaggib6030952018-10-23 18:31:52 +0200767 }
768
769 @Override
Tiger Huang332793b2019-10-29 23:21:27 +0800770 public void hide(@InsetsType int types) {
Taran Singhd7fc5862019-10-10 14:45:17 +0200771 hide(types, false /* fromIme */);
772 }
773
Tiger Huang332793b2019-10-29 23:21:27 +0800774 void hide(@InsetsType int types, boolean fromIme) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800775 int typesReady = 0;
Jorim Jaggib6030952018-10-23 18:31:52 +0200776 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
777 for (int i = internalTypes.size() - 1; i >= 0; i--) {
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +0100778 @InternalInsetsType int internalType = internalTypes.valueAt(i);
779 @AnimationType int animationType = getAnimationType(internalType);
780 InsetsSourceConsumer consumer = getSourceConsumer(internalType);
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100781 if (!consumer.isRequestedVisible() && animationType == ANIMATION_TYPE_NONE
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +0100782 || animationType == ANIMATION_TYPE_HIDE) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -0800783 // no-op: already hidden or animating out.
784 continue;
785 }
786 typesReady |= InsetsState.toPublicType(consumer.getType());
Jorim Jaggib6030952018-10-23 18:31:52 +0200787 }
Taran Singhd7fc5862019-10-10 14:45:17 +0200788 applyAnimation(typesReady, false /* show */, fromIme /* fromIme */);
Jorim Jaggib6030952018-10-23 18:31:52 +0200789 }
790
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100791 @Override
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000792 public void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
Jorim Jaggidd3304e2020-01-20 17:24:51 +0100793 @Nullable Interpolator interpolator,
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000794 @Nullable CancellationSignal cancellationSignal,
Jorim Jaggidd3304e2020-01-20 17:24:51 +0100795 @NonNull WindowInsetsAnimationControlListener listener) {
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000796 controlWindowInsetsAnimation(types, cancellationSignal, listener,
797 false /* fromIme */, durationMillis, interpolator, ANIMATION_TYPE_USER);
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800798 }
799
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000800 private void controlWindowInsetsAnimation(@InsetsType int types,
801 @Nullable CancellationSignal cancellationSignal,
802 WindowInsetsAnimationControlListener listener,
803 boolean fromIme, long durationMs, @Nullable Interpolator interpolator,
804 @AnimationType int animationType) {
Adrian Roos8d04bcb2020-05-29 18:01:04 +0200805 if ((mState.calculateUncontrollableInsetsFromFrame(mFrame) & types) != 0) {
Adrian Roos5d557ed2020-03-17 20:04:35 +0100806 listener.onCancelled(null);
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000807 return;
Tarandeep Singha6f35612019-01-11 19:50:46 -0800808 }
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100809
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000810 controlAnimationUnchecked(types, cancellationSignal, listener, mFrame, fromIme, durationMs,
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100811 interpolator, animationType, getLayoutInsetsDuringAnimationMode(types),
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100812 false /* useInsetsAnimationThread */);
Tarandeep Singha6f35612019-01-11 19:50:46 -0800813 }
814
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000815 private void controlAnimationUnchecked(@InsetsType int types,
816 @Nullable CancellationSignal cancellationSignal,
Tarandeep Singh54554e22019-11-01 14:43:05 -0700817 WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme,
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100818 long durationMs, Interpolator interpolator,
Jorim Jaggidd3304e2020-01-20 17:24:51 +0100819 @AnimationType int animationType,
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100820 @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
821 boolean useInsetsAnimationThread) {
Adrian Roosa79b8852020-05-26 21:25:15 +0200822 if ((types & mTypesBeingCancelled) != 0) {
823 throw new IllegalStateException("Cannot start a new insets animation of "
824 + Type.toString(types)
825 + " while an existing " + Type.toString(mTypesBeingCancelled)
826 + " is being cancelled.");
827 }
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800828 if (types == 0) {
829 // nothing to animate.
Adrian Roos5d557ed2020-03-17 20:04:35 +0100830 listener.onCancelled(null);
Taran Singh85661e32020-05-07 14:45:34 -0700831 if (DEBUG) Log.d(TAG, "no types to animate in controlAnimationUnchecked");
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000832 return;
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800833 }
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100834 cancelExistingControllers(types);
Taran Singh85661e32020-05-07 14:45:34 -0700835 if (DEBUG) Log.d(TAG, "controlAnimation types: " + types);
Jorim Jaggied35b172020-03-06 00:13:57 +0100836 mLastStartedAnimTypes |= types;
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100837
Tiger Huang969c6082019-12-24 20:08:57 +0800838 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
Yunfan Chen02abf552019-12-05 14:51:09 +0900839 final SparseArray<InsetsSourceControl> controls = new SparseArray<>();
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800840
Yunfan Chen02abf552019-12-05 14:51:09 +0900841 Pair<Integer, Boolean> typesReadyPair = collectSourceControls(
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100842 fromIme, internalTypes, controls, animationType);
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800843 int typesReady = typesReadyPair.first;
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100844 boolean imeReady = typesReadyPair.second;
Taran Singh85661e32020-05-07 14:45:34 -0700845 if (DEBUG) Log.d(TAG, String.format(
846 "controlAnimationUnchecked, typesReady: %s imeReady: %s", typesReady, imeReady));
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100847 if (!imeReady) {
848 // IME isn't ready, all requested types will be animated once IME is ready
849 abortPendingImeControlRequest();
Adrian Roos3406fb92020-02-10 18:38:59 -0800850 final PendingControlRequest request = new PendingControlRequest(types,
851 listener, durationMs,
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100852 interpolator, animationType, layoutInsetsDuringAnimation, cancellationSignal,
853 useInsetsAnimationThread);
Adrian Roos3406fb92020-02-10 18:38:59 -0800854 mPendingImeControlRequest = request;
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100855 mHandler.postDelayed(mPendingControlTimeout, PENDING_CONTROL_TIMEOUT_MS);
Taran Singh85661e32020-05-07 14:45:34 -0700856 if (DEBUG) Log.d(TAG, "Ime not ready. Create pending request");
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000857 if (cancellationSignal != null) {
858 cancellationSignal.setOnCancelListener(() -> {
859 if (mPendingImeControlRequest == request) {
Taran Singh85661e32020-05-07 14:45:34 -0700860 if (DEBUG) Log.d(TAG,
861 "Cancellation signal abortPendingImeControlRequest");
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000862 abortPendingImeControlRequest();
863 }
864 });
865 }
866 return;
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800867 }
868
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800869 if (typesReady == 0) {
Taran Singh85661e32020-05-07 14:45:34 -0700870 if (DEBUG) Log.d(TAG, "No types ready. onCancelled()");
Adrian Roos5d557ed2020-03-17 20:04:35 +0100871 listener.onCancelled(null);
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000872 return;
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800873 }
874
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100875
876 final InsetsAnimationControlRunner runner = useInsetsAnimationThread
877 ? new InsetsAnimationThreadControlRunner(controls,
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100878 frame, mState, listener, typesReady, this, durationMs, interpolator,
Jorim Jaggibf87c152020-04-22 17:18:25 +0200879 animationType, mHost.getHandler())
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100880 : new InsetsAnimationControlImpl(controls,
Jorim Jaggi5875cca2020-03-17 13:44:57 +0100881 frame, mState, listener, typesReady, this, durationMs, interpolator,
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100882 animationType);
883 mRunningAnimations.add(new RunningAnimation(runner, animationType));
Taran Singh85661e32020-05-07 14:45:34 -0700884 if (DEBUG) Log.d(TAG, "Animation added to runner. useInsetsAnimationThread: "
885 + useInsetsAnimationThread);
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000886 if (cancellationSignal != null) {
Adrian Roosa79b8852020-05-26 21:25:15 +0200887 cancellationSignal.setOnCancelListener(() -> {
888 cancelAnimation(runner, true /* invokeCallback */);
889 });
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000890 }
Jorim Jaggi6d5c8012020-02-28 01:40:27 +0100891 if (layoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN) {
892 showDirectly(types);
893 } else {
894 hideDirectly(types, false /* animationFinished */, animationType);
895 }
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800896 }
897
898 /**
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100899 * @return Pair of (types ready to animate, IME ready to animate).
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800900 */
Yunfan Chen02abf552019-12-05 14:51:09 +0900901 private Pair<Integer, Boolean> collectSourceControls(boolean fromIme,
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100902 ArraySet<Integer> internalTypes, SparseArray<InsetsSourceControl> controls,
903 @AnimationType int animationType) {
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800904 int typesReady = 0;
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100905 boolean imeReady = true;
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100906 for (int i = internalTypes.size() - 1; i >= 0; i--) {
Tarandeep Singhb9538cd2020-02-20 17:51:18 -0800907 final InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100908 boolean show = animationType == ANIMATION_TYPE_SHOW
909 || animationType == ANIMATION_TYPE_USER;
910 boolean canRun = false;
911 if (show) {
Taran Singhd7fc5862019-10-10 14:45:17 +0200912 // Show request
913 switch(consumer.requestShow(fromIme)) {
914 case ShowResult.SHOW_IMMEDIATELY:
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100915 canRun = true;
Taran Singhd7fc5862019-10-10 14:45:17 +0200916 break;
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100917 case ShowResult.IME_SHOW_DELAYED:
918 imeReady = false;
Taran Singh85661e32020-05-07 14:45:34 -0700919 if (DEBUG) Log.d(TAG, "requestShow IME_SHOW_DELAYED");
Taran Singhd7fc5862019-10-10 14:45:17 +0200920 break;
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100921 case ShowResult.IME_SHOW_FAILED:
Taran Singh85661e32020-05-07 14:45:34 -0700922 if (WARN) Log.w(TAG, "requestShow IME_SHOW_FAILED. fromIme: "
923 + fromIme);
Taran Singhd7fc5862019-10-10 14:45:17 +0200924 // IME cannot be shown (since it didn't have focus), proceed
925 // with animation of other types.
Taran Singhd7fc5862019-10-10 14:45:17 +0200926 break;
Tarandeep Singh46d59f02019-01-29 18:09:15 -0800927 }
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100928 } else {
Taran Singhd7fc5862019-10-10 14:45:17 +0200929 // Hide request
930 // TODO: Move notifyHidden() to beginning of the hide animation
931 // (when visibility actually changes using hideDirectly()).
932 if (!fromIme) {
933 consumer.notifyHidden();
934 }
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100935 canRun = true;
936 }
937 if (!canRun) {
Taran Singh85661e32020-05-07 14:45:34 -0700938 if (WARN) Log.w(TAG, String.format(
939 "collectSourceControls can't continue show for type: %s fromIme: %b",
940 InsetsState.typeToString(consumer.getType()), fromIme));
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100941 continue;
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100942 }
Tiger Huang969c6082019-12-24 20:08:57 +0800943 final InsetsSourceControl control = consumer.getControl();
944 if (control != null) {
Rob Carr3a367c42020-03-10 15:51:35 -0700945 controls.put(consumer.getType(), new InsetsSourceControl(control));
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100946 typesReady |= toPublicType(consumer.getType());
947 } else if (animationType == ANIMATION_TYPE_SHOW) {
Taran Singh85661e32020-05-07 14:45:34 -0700948 if (DEBUG) Log.d(TAG, "collectSourceControls no control for show(). fromIme: "
949 + fromIme);
Jorim Jaggi3182ef12020-01-30 00:16:18 +0100950 // We don't have a control at the moment. However, we still want to update requested
951 // visibility state such that in case we get control, we can apply show animation.
952 consumer.show(fromIme);
953 } else if (animationType == ANIMATION_TYPE_HIDE) {
954 consumer.hide();
Tiger Huang969c6082019-12-24 20:08:57 +0800955 }
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100956 }
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100957 return new Pair<>(typesReady, imeReady);
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100958 }
959
Jorim Jaggia51168a2019-12-27 15:17:44 +0100960 private @LayoutInsetsDuringAnimation int getLayoutInsetsDuringAnimationMode(
961 @InsetsType int types) {
962
963 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
964
965 // Generally, we want to layout the opposite of the current state. This is to make animation
966 // callbacks easy to use: The can capture the layout values and then treat that as end-state
967 // during the animation.
968 //
969 // However, if controlling multiple sources, we want to treat it as shown if any of the
970 // types is currently hidden.
971 for (int i = internalTypes.size() - 1; i >= 0; i--) {
972 InsetsSourceConsumer consumer = mSourceConsumers.get(internalTypes.valueAt(i));
973 if (consumer == null) {
974 continue;
975 }
976 if (!consumer.isRequestedVisible()) {
977 return LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
978 }
979 }
980 return LAYOUT_INSETS_DURING_ANIMATION_HIDDEN;
981 }
982
Tiger Huang332793b2019-10-29 23:21:27 +0800983 private void cancelExistingControllers(@InsetsType int types) {
Adrian Roosa79b8852020-05-26 21:25:15 +0200984 final int originalmTypesBeingCancelled = mTypesBeingCancelled;
985 mTypesBeingCancelled |= types;
986 try {
987 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
988 InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner;
989 if ((control.getTypes() & types) != 0) {
990 cancelAnimation(control, true /* invokeCallback */);
991 }
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +0100992 }
Adrian Roosa79b8852020-05-26 21:25:15 +0200993 if ((types & ime()) != 0) {
994 abortPendingImeControlRequest();
995 }
996 } finally {
997 mTypesBeingCancelled = originalmTypesBeingCancelled;
Jorim Jaggid7f10ed2020-01-08 21:41:55 +0100998 }
999 }
1000
1001 private void abortPendingImeControlRequest() {
1002 if (mPendingImeControlRequest != null) {
Adrian Roos5d557ed2020-03-17 20:04:35 +01001003 mPendingImeControlRequest.listener.onCancelled(null);
Jorim Jaggid7f10ed2020-01-08 21:41:55 +01001004 mPendingImeControlRequest = null;
1005 mHandler.removeCallbacks(mPendingControlTimeout);
Taran Singh85661e32020-05-07 14:45:34 -07001006 if (DEBUG) Log.d(TAG, "abortPendingImeControlRequest");
Jorim Jaggid7f10ed2020-01-08 21:41:55 +01001007 }
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +01001008 }
1009
1010 @VisibleForTesting
Yunfan Chen02abf552019-12-05 14:51:09 +09001011 @Override
Jorim Jaggi6d5c8012020-02-28 01:40:27 +01001012 public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) {
1013 cancelAnimation(runner, false /* invokeCallback */);
Taran Singh85661e32020-05-07 14:45:34 -07001014 if (DEBUG) Log.d(TAG, "notifyFinished. shown: " + shown);
Tarandeep Singh54554e22019-11-01 14:43:05 -07001015 if (shown) {
Jorim Jaggi6d5c8012020-02-28 01:40:27 +01001016 showDirectly(runner.getTypes());
Tarandeep Singh54554e22019-11-01 14:43:05 -07001017 } else {
Jorim Jaggi6d5c8012020-02-28 01:40:27 +01001018 hideDirectly(runner.getTypes(), true /* animationFinished */,
1019 runner.getAnimationType());
Tarandeep Singh54554e22019-11-01 14:43:05 -07001020 }
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +01001021 }
1022
Yunfan Chen02abf552019-12-05 14:51:09 +09001023 @Override
1024 public void applySurfaceParams(final SyncRtSurfaceTransactionApplier.SurfaceParams... params) {
Jorim Jaggibf87c152020-04-22 17:18:25 +02001025 mHost.applySurfaceParams(params);
Yunfan Chen02abf552019-12-05 14:51:09 +09001026 }
1027
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +01001028 void notifyControlRevoked(InsetsSourceConsumer consumer) {
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +01001029 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
Jorim Jaggi6d5c8012020-02-28 01:40:27 +01001030 InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner;
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +01001031 if ((control.getTypes() & toPublicType(consumer.getType())) != 0) {
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +01001032 cancelAnimation(control, true /* invokeCallback */);
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +01001033 }
1034 }
Jorim Jaggid7f10ed2020-01-08 21:41:55 +01001035 if (consumer.getType() == ITYPE_IME) {
1036 abortPendingImeControlRequest();
1037 }
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +01001038 }
1039
Jorim Jaggi6d5c8012020-02-28 01:40:27 +01001040 private void cancelAnimation(InsetsAnimationControlRunner control, boolean invokeCallback) {
Taran Singh85661e32020-05-07 14:45:34 -07001041 if (DEBUG) Log.d(TAG, String.format("cancelAnimation of types: %d, animType: %d",
1042 control.getTypes(), control.getAnimationType()));
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +01001043 if (invokeCallback) {
Jorim Jaggi6d5c8012020-02-28 01:40:27 +01001044 control.cancel();
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +01001045 }
1046 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
Jorim Jaggi33a21832020-04-06 14:15:46 +02001047 RunningAnimation runningAnimation = mRunningAnimations.get(i);
1048 if (runningAnimation.runner == control) {
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +01001049 mRunningAnimations.remove(i);
Jorim Jaggi33a21832020-04-06 14:15:46 +02001050 ArraySet<Integer> types = toInternalType(control.getTypes());
1051 for (int j = types.size() - 1; j >= 0; j--) {
1052 if (getSourceConsumer(types.valueAt(j)).notifyAnimationFinished()) {
Jorim Jaggibf87c152020-04-22 17:18:25 +02001053 mHost.notifyInsetsChanged();
Jorim Jaggi33a21832020-04-06 14:15:46 +02001054 }
1055 }
Adrian Roosa79b8852020-05-26 21:25:15 +02001056 if (invokeCallback && runningAnimation.startDispatched) {
1057 dispatchAnimationEnd(runningAnimation.runner.getAnimation());
1058 }
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +01001059 break;
1060 }
1061 }
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +01001062 }
1063
Jorim Jaggic8d60382018-10-31 17:06:06 +01001064 private void applyLocalVisibilityOverride() {
1065 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
1066 final InsetsSourceConsumer controller = mSourceConsumers.valueAt(i);
1067 controller.applyLocalVisibilityOverride();
1068 }
1069 }
1070
Jorim Jaggib6030952018-10-23 18:31:52 +02001071 @VisibleForTesting
Tiger Huang332793b2019-10-29 23:21:27 +08001072 public @NonNull InsetsSourceConsumer getSourceConsumer(@InternalInsetsType int type) {
Jorim Jaggib6030952018-10-23 18:31:52 +02001073 InsetsSourceConsumer controller = mSourceConsumers.get(type);
1074 if (controller != null) {
1075 return controller;
1076 }
Jorim Jaggid7f10ed2020-01-08 21:41:55 +01001077 controller = mConsumerCreator.apply(this, type);
Jorim Jaggib6030952018-10-23 18:31:52 +02001078 mSourceConsumers.put(type, controller);
1079 return controller;
1080 }
1081
Jorim Jaggi5bb571d2018-11-06 14:42:04 +01001082 @VisibleForTesting
1083 public void notifyVisibilityChanged() {
Jorim Jaggibf87c152020-04-22 17:18:25 +02001084 mHost.notifyInsetsChanged();
Tiger Huang173480f2020-04-29 01:05:42 +08001085 updateRequestedState();
Jorim Jaggie35c0592018-11-06 16:21:08 +01001086 }
1087
1088 /**
Tiger Huang2ab590a2020-05-19 21:55:13 +08001089 * @see ViewRootImpl#updateCompatSysUiVisibility(int, boolean, boolean)
1090 */
1091 public void updateCompatSysUiVisibility(@InternalInsetsType int type, boolean visible,
1092 boolean hasControl) {
1093 mHost.updateCompatSysUiVisibility(type, visible, hasControl);
1094 }
1095
1096 /**
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08001097 * Called when current window gains focus.
1098 */
1099 public void onWindowFocusGained() {
Tiger Huang332793b2019-10-29 23:21:27 +08001100 getSourceConsumer(ITYPE_IME).onWindowFocusGained();
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08001101 }
1102
1103 /**
1104 * Called when current window loses focus.
1105 */
1106 public void onWindowFocusLost() {
Tiger Huang332793b2019-10-29 23:21:27 +08001107 getSourceConsumer(ITYPE_IME).onWindowFocusLost();
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08001108 }
1109
Tarandeep Singh46d59f02019-01-29 18:09:15 -08001110 /**
1111 * Used by {@link ImeInsetsSourceConsumer} when IME decides to be shown/hidden.
1112 * @hide
1113 */
1114 @VisibleForTesting
1115 public void applyImeVisibility(boolean setVisible) {
1116 if (setVisible) {
1117 show(Type.IME, true /* fromIme */);
1118 } else {
1119 hide(Type.IME);
1120 }
1121 }
1122
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +01001123 @VisibleForTesting
1124 public @AnimationType int getAnimationType(@InternalInsetsType int type) {
1125 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
Jorim Jaggi6d5c8012020-02-28 01:40:27 +01001126 InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner;
Jorim Jaggi1f2c7eb2020-01-08 00:07:13 +01001127 if (control.controlsInternalType(type)) {
1128 return mRunningAnimations.get(i).type;
1129 }
1130 }
1131 return ANIMATION_TYPE_NONE;
Tarandeep Singh93ea15a2019-11-26 11:09:14 -08001132 }
1133
Tarandeep Singh2cbcd7f2019-01-25 11:47:57 -08001134 /**
Tiger Huang173480f2020-04-29 01:05:42 +08001135 * Sends the local visibility state back to window manager if it is changed.
Jorim Jaggie35c0592018-11-06 16:21:08 +01001136 */
Tiger Huang173480f2020-04-29 01:05:42 +08001137 private void updateRequestedState() {
1138 boolean changed = false;
Jorim Jaggie35c0592018-11-06 16:21:08 +01001139 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
1140 final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
Tiger Huang173480f2020-04-29 01:05:42 +08001141 final @InternalInsetsType int type = consumer.getType();
1142 if (type == ITYPE_CAPTION_BAR) {
1143 continue;
1144 }
Jorim Jaggie35c0592018-11-06 16:21:08 +01001145 if (consumer.getControl() != null) {
Tiger Huang173480f2020-04-29 01:05:42 +08001146 final InsetsSource localSource = mState.getSource(type);
1147 if (!localSource.equals(mRequestedState.peekSource(type))) {
Tiger Huangfbfc3ab2020-05-06 01:02:31 +08001148 // Our requested state is stale. Update it here and send it to window manager.
Tiger Huang173480f2020-04-29 01:05:42 +08001149 mRequestedState.addSource(new InsetsSource(localSource));
1150 changed = true;
1151 }
Tiger Huangfbfc3ab2020-05-06 01:02:31 +08001152 if (!localSource.equals(mLastDispatchedState.peekSource(type))) {
1153 // The server state is not what we expected. This can happen while we don't have
1154 // the control. Since we have the control now, we need to send our request again
1155 // to modify the server state.
1156 changed = true;
1157 }
Jorim Jaggie35c0592018-11-06 16:21:08 +01001158 }
1159 }
Tiger Huang173480f2020-04-29 01:05:42 +08001160 if (!changed) {
1161 return;
1162 }
1163 mHost.onInsetsModified(mRequestedState);
Jorim Jaggic8d60382018-10-31 17:06:06 +01001164 }
1165
Taran Singh8321a2a42020-04-21 17:26:50 -07001166 @VisibleForTesting
1167 public void applyAnimation(@InsetsType final int types, boolean show, boolean fromIme) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -08001168 if (types == 0) {
1169 // nothing to animate.
Taran Singh85661e32020-05-07 14:45:34 -07001170 if (DEBUG) Log.d(TAG, "applyAnimation, nothing to animate");
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -08001171 return;
1172 }
Tarandeep Singha6f35612019-01-11 19:50:46 -08001173
Jorim Jaggibf87c152020-04-22 17:18:25 +02001174 boolean hasAnimationCallbacks = mHost.hasAnimationCallbacks();
Tiger Huangf2321192020-05-21 22:16:14 +08001175 final InternalAnimationControlListener listener = new InternalAnimationControlListener(
1176 show, hasAnimationCallbacks, types, mAnimationsDisabled);
Jorim Jaggi5875cca2020-03-17 13:44:57 +01001177
Tarandeep Singha6f35612019-01-11 19:50:46 -08001178 // Show/hide animations always need to be relative to the display frame, in order that shown
1179 // and hidden state insets are correct.
Tarandeep Singh54554e22019-11-01 14:43:05 -07001180 controlAnimationUnchecked(
Adrian Roos5ad8cd22020-03-12 18:30:54 +00001181 types, null /* cancellationSignal */, listener, mState.getDisplayFrame(), fromIme,
Jorim Jaggi5875cca2020-03-17 13:44:57 +01001182 listener.getDurationMs(), listener.getInterpolator(),
1183 show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE,
1184 show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN,
1185 !hasAnimationCallbacks /* useInsetsAnimationThread */);
1186
Jorim Jaggi6d5c8012020-02-28 01:40:27 +01001187 }
1188
Tarandeep Singhb9538cd2020-02-20 17:51:18 -08001189 private void hideDirectly(
1190 @InsetsType int types, boolean animationFinished, @AnimationType int animationType) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -08001191 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
1192 for (int i = internalTypes.size() - 1; i >= 0; i--) {
Tarandeep Singhb9538cd2020-02-20 17:51:18 -08001193 getSourceConsumer(internalTypes.valueAt(i)).hide(animationFinished, animationType);
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -08001194 }
1195 }
1196
Tiger Huang332793b2019-10-29 23:21:27 +08001197 private void showDirectly(@InsetsType int types) {
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -08001198 final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
1199 for (int i = internalTypes.size() - 1; i >= 0; i--) {
Jorim Jaggi3182ef12020-01-30 00:16:18 +01001200 getSourceConsumer(internalTypes.valueAt(i)).show(false /* fromIme */);
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -08001201 }
1202 }
1203
1204 /**
Tiger Huang332793b2019-10-29 23:21:27 +08001205 * Cancel on-going animation to show/hide {@link InsetsType}.
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -08001206 */
1207 @VisibleForTesting
Jorim Jaggi31e52542020-05-04 23:23:25 +02001208 public void cancelExistingAnimations() {
Jorim Jaggi5ed50cc2019-01-23 16:59:42 +01001209 cancelExistingControllers(all());
Tarandeep Singh22f2b4c2019-01-10 19:41:30 -08001210 }
1211
Jorim Jaggif96c90a2018-09-26 16:55:15 +02001212 void dump(String prefix, PrintWriter pw) {
1213 pw.println(prefix); pw.println("InsetsController:");
1214 mState.dump(prefix + " ", pw);
1215 }
Jorim Jaggi02a741f2018-12-12 17:40:19 -08001216
Jorim Jaggifae3e272019-01-14 14:05:05 +01001217 @VisibleForTesting
Yunfan Chen02abf552019-12-05 14:51:09 +09001218 @Override
Jorim Jaggia51168a2019-12-27 15:17:44 +01001219 public void startAnimation(InsetsAnimationControlImpl controller,
Adrian Roosdb5b0c22020-02-12 15:05:27 -08001220 WindowInsetsAnimationControlListener listener, int types,
Jorim Jaggi6d5c8012020-02-28 01:40:27 +01001221 WindowInsetsAnimation animation, Bounds bounds) {
Jorim Jaggibf87c152020-04-22 17:18:25 +02001222 mHost.dispatchWindowInsetsAnimationPrepare(animation);
1223 mHost.addOnPreDrawRunnable(() -> {
1224 if (controller.isCancelled()) {
Taran Singh85661e32020-05-07 14:45:34 -07001225 if (WARN) Log.w(TAG, "startAnimation canceled before preDraw");
Jorim Jaggibf87c152020-04-22 17:18:25 +02001226 return;
Jorim Jaggia51168a2019-12-27 15:17:44 +01001227 }
Jorim Jaggicb28ae62020-05-14 17:46:32 +02001228 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW,
1229 "InsetsAnimation: " + WindowInsets.Type.toString(types), types);
Jorim Jaggibf87c152020-04-22 17:18:25 +02001230 for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
1231 RunningAnimation runningAnimation = mRunningAnimations.get(i);
1232 if (runningAnimation.runner == controller) {
1233 runningAnimation.startDispatched = true;
1234 }
1235 }
1236 mHost.dispatchWindowInsetsAnimationStart(animation, bounds);
1237 mStartingAnimation = true;
1238 controller.mReadyDispatched = true;
1239 listener.onReady(controller, types);
1240 mStartingAnimation = false;
Jorim Jaggia51168a2019-12-27 15:17:44 +01001241 });
Jorim Jaggi02a741f2018-12-12 17:40:19 -08001242 }
1243
Jorim Jaggifae3e272019-01-14 14:05:05 +01001244 @VisibleForTesting
Adrian Roosdb5b0c22020-02-12 15:05:27 -08001245 public void dispatchAnimationEnd(WindowInsetsAnimation animation) {
Jorim Jaggicb28ae62020-05-14 17:46:32 +02001246 Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW,
1247 "InsetsAnimation: " + WindowInsets.Type.toString(animation.getTypeMask()),
1248 animation.getTypeMask());
Jorim Jaggibf87c152020-04-22 17:18:25 +02001249 mHost.dispatchWindowInsetsAnimationEnd(animation);
Jorim Jaggi02a741f2018-12-12 17:40:19 -08001250 }
1251
Jorim Jaggifae3e272019-01-14 14:05:05 +01001252 @VisibleForTesting
Yunfan Chen02abf552019-12-05 14:51:09 +09001253 @Override
Adrian Roos6a4448f2020-04-01 15:01:08 +02001254 public void scheduleApplyChangeInsets(InsetsAnimationControlRunner runner) {
1255 if (mStartingAnimation || runner.getAnimationType() == ANIMATION_TYPE_USER) {
Jorim Jaggia51168a2019-12-27 15:17:44 +01001256 mAnimCallback.run();
1257 mAnimCallbackScheduled = false;
1258 return;
1259 }
Jorim Jaggi02a741f2018-12-12 17:40:19 -08001260 if (!mAnimCallbackScheduled) {
Jorim Jaggibf87c152020-04-22 17:18:25 +02001261 mHost.postInsetsAnimationCallback(mAnimCallback);
Jorim Jaggi02a741f2018-12-12 17:40:19 -08001262 mAnimCallbackScheduled = true;
1263 }
1264 }
Jorim Jaggib7848b72018-12-28 14:38:21 +01001265
1266 @Override
Tiger Huang7238a052020-01-10 20:37:01 +08001267 public void setSystemBarsAppearance(@Appearance int appearance, @Appearance int mask) {
Jorim Jaggibf87c152020-04-22 17:18:25 +02001268 mHost.setSystemBarsAppearance(appearance, mask);
Jorim Jaggib7848b72018-12-28 14:38:21 +01001269 }
1270
1271 @Override
Tiger Huang7238a052020-01-10 20:37:01 +08001272 public @Appearance int getSystemBarsAppearance() {
Jorim Jaggibf87c152020-04-22 17:18:25 +02001273 return mHost.getSystemBarsAppearance();
Tiger Huang7238a052020-01-10 20:37:01 +08001274 }
1275
1276 @Override
Yunfan Chenfae0aea2020-02-22 20:57:57 +09001277 public void setCaptionInsetsHeight(int height) {
1278 mCaptionInsetsHeight = height;
1279 }
1280
1281 @Override
Jorim Jaggib7848b72018-12-28 14:38:21 +01001282 public void setSystemBarsBehavior(@Behavior int behavior) {
Jorim Jaggibf87c152020-04-22 17:18:25 +02001283 mHost.setSystemBarsBehavior(behavior);
Jorim Jaggib7848b72018-12-28 14:38:21 +01001284 }
Tiger Huang7238a052020-01-10 20:37:01 +08001285
1286 @Override
1287 public @Appearance int getSystemBarsBehavior() {
Jorim Jaggibf87c152020-04-22 17:18:25 +02001288 return mHost.getSystemBarsBehavior();
Tiger Huang7238a052020-01-10 20:37:01 +08001289 }
Rob Carr521e3632020-03-09 11:22:30 -07001290
Tiger Huangf2321192020-05-21 22:16:14 +08001291 @Override
1292 public void setAnimationsDisabled(boolean disable) {
1293 mAnimationsDisabled = disable;
1294 }
1295
Jorim Jaggied35b172020-03-06 00:13:57 +01001296 private @InsetsType int calculateControllableTypes() {
Jorim Jaggied35b172020-03-06 00:13:57 +01001297 @InsetsType int result = 0;
1298 for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
1299 InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
Yohei Yukawaf35971d2020-06-16 21:31:35 +00001300 if (consumer.getControl() != null) {
Jorim Jaggied35b172020-03-06 00:13:57 +01001301 result |= toPublicType(consumer.mType);
1302 }
1303 }
Adrian Roos8d04bcb2020-05-29 18:01:04 +02001304 return result & ~mState.calculateUncontrollableInsetsFromFrame(mFrame);
Jorim Jaggied35b172020-03-06 00:13:57 +01001305 }
1306
1307 /**
1308 * @return The types that are now animating due to a listener invoking control/show/hide
1309 */
1310 private @InsetsType int invokeControllableInsetsChangedListeners() {
1311 mLastStartedAnimTypes = 0;
1312 @InsetsType int types = calculateControllableTypes();
1313 int size = mControllableInsetsChangedListeners.size();
1314 for (int i = 0; i < size; i++) {
1315 mControllableInsetsChangedListeners.get(i).onControllableInsetsChanged(this, types);
1316 }
1317 return mLastStartedAnimTypes;
1318 }
1319
1320 @Override
1321 public void addOnControllableInsetsChangedListener(
1322 OnControllableInsetsChangedListener listener) {
1323 Objects.requireNonNull(listener);
1324 mControllableInsetsChangedListeners.add(listener);
1325 listener.onControllableInsetsChanged(this, calculateControllableTypes());
1326 }
1327
1328 @Override
1329 public void removeOnControllableInsetsChangedListener(
1330 OnControllableInsetsChangedListener listener) {
1331 Objects.requireNonNull(listener);
1332 mControllableInsetsChangedListeners.remove(listener);
1333 }
1334
Jorim Jaggibf87c152020-04-22 17:18:25 +02001335 @Override
Rob Carr521e3632020-03-09 11:22:30 -07001336 public void releaseSurfaceControlFromRt(SurfaceControl sc) {
Jorim Jaggibf87c152020-04-22 17:18:25 +02001337 mHost.releaseSurfaceControlFromRt(sc);
1338 }
1339
1340 Host getHost() {
1341 return mHost;
Rob Carr521e3632020-03-09 11:22:30 -07001342 }
Jorim Jaggif96c90a2018-09-26 16:55:15 +02001343}