A brave new world for window insets (5/n)

Implement controlWindowInsetsAnimation

Based on the leashes we have on the client, and the insets the
client has requested, we are able to move the surfaces around
such that the resulting insets will match what the client
requested.

Bug: 118118435
Change-Id: I0616e53455a6544aaf374c1b0eb10e258aced21d
This commit is contained in:
Jorim Jaggi
2018-11-06 14:42:04 +01:00
parent 7fa78c545b
commit 5bb571dc40
17 changed files with 752 additions and 56 deletions

View File

@@ -54,6 +54,13 @@ public class SparseSetArray<T> {
return set.contains(value);
}
/**
* @return the set of items at index n
*/
public ArraySet<T> get(int n) {
return mData.get(n);
}
/**
* Remove a value from index n.
* @return TRUE when the value existed at the given index and removed, FALSE otherwise.

View File

@@ -0,0 +1,197 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package android.view;
import static android.view.InsetsState.INSET_SIDE_BOTTOM;
import static android.view.InsetsState.INSET_SIDE_LEFT;
import static android.view.InsetsState.INSET_SIDE_RIGHT;
import static android.view.InsetsState.INSET_SIDE_TOP;
import android.annotation.Nullable;
import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.UidProto.Sync;
import android.util.ArraySet;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.SparseSetArray;
import android.view.InsetsState.InsetSide;
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.WindowInsets.Type.InsetType;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* Implements {@link WindowInsetsAnimationController}
* @hide
*/
@VisibleForTesting
public class InsetsAnimationControlImpl implements WindowInsetsAnimationController {
private final WindowInsetsAnimationControlListener mListener;
private final SparseArray<InsetsSourceConsumer> mConsumers;
private final SparseIntArray mTypeSideMap = new SparseIntArray();
private final SparseSetArray<InsetsSourceConsumer> mSideSourceMap = new SparseSetArray<>();
/** @see WindowInsetsAnimationController#getHiddenStateInsets */
private final Insets mHiddenInsets;
/** @see WindowInsetsAnimationController#getShownStateInsets */
private final Insets mShownInsets;
private final Matrix mTmpMatrix = new Matrix();
private final InsetsState mInitialInsetsState;
private final @InsetType int mTypes;
private final Supplier<SyncRtSurfaceTransactionApplier> mTransactionApplierSupplier;
private Insets mCurrentInsets;
@VisibleForTesting
public InsetsAnimationControlImpl(SparseArray<InsetsSourceConsumer> consumers, Rect frame,
InsetsState state, WindowInsetsAnimationControlListener listener,
@InsetType int types,
Supplier<SyncRtSurfaceTransactionApplier> transactionApplierSupplier) {
mConsumers = consumers;
mListener = listener;
mTypes = types;
mTransactionApplierSupplier = transactionApplierSupplier;
mInitialInsetsState = new InsetsState(state);
mCurrentInsets = getInsetsFromState(mInitialInsetsState, frame, null /* typeSideMap */);
mHiddenInsets = calculateInsets(mInitialInsetsState, frame, consumers, false /* shown */,
null /* typeSideMap */);
mShownInsets = calculateInsets(mInitialInsetsState, frame, consumers, true /* shown */,
mTypeSideMap);
buildTypeSourcesMap(mTypeSideMap, mSideSourceMap, mConsumers);
// TODO: Check for controllability first and wait for IME if needed.
listener.onReady(this, types);
}
@Override
public Insets getHiddenStateInsets() {
return mHiddenInsets;
}
@Override
public Insets getShownStateInsets() {
return mShownInsets;
}
@Override
public Insets getCurrentInsets() {
return mCurrentInsets;
}
@Override
@InsetType
public int getTypes() {
return mTypes;
}
@Override
public void changeInsets(Insets insets) {
insets = sanitize(insets);
final Insets offset = Insets.subtract(mShownInsets, insets);
ArrayList<SurfaceParams> params = new ArrayList<>();
if (offset.left != 0) {
updateLeashesForSide(INSET_SIDE_LEFT, offset.left, params);
}
if (offset.top != 0) {
updateLeashesForSide(INSET_SIDE_TOP, offset.top, params);
}
if (offset.right != 0) {
updateLeashesForSide(INSET_SIDE_RIGHT, offset.right, params);
}
if (offset.bottom != 0) {
updateLeashesForSide(INSET_SIDE_BOTTOM, offset.bottom, params);
}
SyncRtSurfaceTransactionApplier applier = mTransactionApplierSupplier.get();
applier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
mCurrentInsets = insets;
}
@Override
public void finish(int shownTypes) {
// TODO
}
private Insets calculateInsets(InsetsState state, Rect frame,
SparseArray<InsetsSourceConsumer> consumers, boolean shown,
@Nullable @InsetSide SparseIntArray typeSideMap) {
for (int i = consumers.size() - 1; i >= 0; i--) {
state.getSource(consumers.valueAt(i).getType()).setVisible(shown);
}
return getInsetsFromState(state, frame, typeSideMap);
}
private Insets getInsetsFromState(InsetsState state, Rect frame,
@Nullable @InsetSide SparseIntArray typeSideMap) {
return state.calculateInsets(frame, false /* isScreenRound */,
false /* alwaysConsumerNavBar */, null /* displayCutout */, typeSideMap)
.getSystemWindowInsets();
}
private Insets sanitize(Insets insets) {
return Insets.max(Insets.min(insets, mShownInsets), mHiddenInsets);
}
private void updateLeashesForSide(@InsetSide int side, int inset,
ArrayList<SurfaceParams> surfaceParams) {
ArraySet<InsetsSourceConsumer> items = mSideSourceMap.get(side);
// TODO: Implement behavior when inset spans over multiple types
for (int i = items.size() - 1; i >= 0; i--) {
final InsetsSourceConsumer consumer = items.valueAt(i);
final InsetsSource source = mInitialInsetsState.getSource(consumer.getType());
final SurfaceControl leash = consumer.getControl().getLeash();
mTmpMatrix.setTranslate(source.getFrame().left, source.getFrame().top);
addTranslationToMatrix(side, inset, mTmpMatrix);
surfaceParams.add(new SurfaceParams(leash, 1f, mTmpMatrix, null, 0, 0f));
}
}
private void addTranslationToMatrix(@InsetSide int side, int inset, Matrix m) {
switch (side) {
case INSET_SIDE_LEFT:
m.postTranslate(-inset, 0);
break;
case INSET_SIDE_TOP:
m.postTranslate(0, -inset);
break;
case INSET_SIDE_RIGHT:
m.postTranslate(inset, 0);
break;
case INSET_SIDE_BOTTOM:
m.postTranslate(0, inset);
break;
}
}
private static void buildTypeSourcesMap(SparseIntArray typeSideMap,
SparseSetArray<InsetsSourceConsumer> sideSourcesMap,
SparseArray<InsetsSourceConsumer> consumers) {
for (int i = typeSideMap.size() - 1; i >= 0; i--) {
int type = typeSideMap.keyAt(i);
int side = typeSideMap.valueAt(i);
sideSourcesMap.add(side, consumers.get(type));
}
}
}

View File

@@ -28,6 +28,7 @@ import android.view.InsetsState.InternalInsetType;
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
import java.util.ArrayList;
/**
* Implements {@link WindowInsetsController} on the client.
@@ -41,6 +42,7 @@ public class InsetsController implements WindowInsetsController {
private final ViewRootImpl mViewRoot;
private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>();
private final ArrayList<InsetsAnimationControlImpl> mAnimationControls = new ArrayList<>();
public InsetsController(ViewRootImpl viewRoot) {
mViewRoot = viewRoot;
@@ -67,9 +69,11 @@ public class InsetsController implements WindowInsetsController {
/**
* @see InsetsState#calculateInsets
*/
WindowInsets calculateInsets(boolean isScreenRound,
@VisibleForTesting
public WindowInsets calculateInsets(boolean isScreenRound,
boolean alwaysConsumeNavBar, DisplayCutout cutout) {
return mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout);
return mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout,
null /* typeSideMap */);
}
/**
@@ -116,6 +120,28 @@ public class InsetsController implements WindowInsetsController {
}
}
@Override
public void controlWindowInsetsAnimation(@InsetType int types,
WindowInsetsAnimationControlListener listener) {
// TODO: Check whether we already have a controller.
final ArraySet<Integer> internalTypes = mState.toInternalType(types);
final SparseArray<InsetsSourceConsumer> consumers = new SparseArray<>();
for (int i = internalTypes.size() - 1; i >= 0; i--) {
InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
if (consumer.getControl() != null) {
consumers.put(consumer.getType(), consumer);
} else {
// TODO: Let calling app know it's not possible, or wait
// TODO: Remove it from types
}
}
final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(consumers,
mFrame, mState, listener, types,
() -> new SyncRtSurfaceTransactionApplier(mViewRoot.mView));
mAnimationControls.add(controller);
}
private void applyLocalVisibilityOverride() {
for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
final InsetsSourceConsumer controller = mSourceConsumers.valueAt(i);
@@ -134,7 +160,8 @@ public class InsetsController implements WindowInsetsController {
return controller;
}
void notifyVisibilityChanged() {
@VisibleForTesting
public void notifyVisibilityChanged() {
mViewRoot.notifyInsetsChanged();
}

View File

@@ -70,7 +70,8 @@ public class InsetsSource implements Parcelable {
*
* @param relativeFrame The frame to calculate the insets relative to.
* @param ignoreVisibility If true, always reports back insets even if source isn't visible.
* @return The resulting insets.
* @return The resulting insets. The contract is that only one side will be occupied by a
* source.
*/
public Insets calculateInsets(Rect relativeFrame, boolean ignoreVisibility) {
if (!ignoreVisibility && !mVisible) {

View File

@@ -17,12 +17,15 @@
package android.view;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetType;
@@ -77,11 +80,30 @@ public class InsetsState implements Parcelable {
/** A shelf is the same as the navigation bar. */
public static final int TYPE_SHELF = TYPE_NAVIGATION_BAR;
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = "INSET_SIDE", value = {
INSET_SIDE_LEFT,
INSET_SIDE_TOP,
INSET_SIDE_RIGHT,
INSET_SIDE_BOTTOM,
INSET_SIDE_UNKNWON
})
public @interface InsetSide {}
static final int INSET_SIDE_LEFT = 0;
static final int INSET_SIDE_TOP = 1;
static final int INSET_SIDE_RIGHT = 2;
static final int INSET_SIDE_BOTTOM = 3;
static final int INSET_SIDE_UNKNWON = 4;
private final ArrayMap<Integer, InsetsSource> mSources = new ArrayMap<>();
public InsetsState() {
}
public InsetsState(InsetsState copy) {
set(copy);
}
/**
* Calculates {@link WindowInsets} based on the current source configuration.
*
@@ -89,7 +111,8 @@ public class InsetsState implements Parcelable {
* @return The calculated insets.
*/
public WindowInsets calculateInsets(Rect frame, boolean isScreenRound,
boolean alwaysConsumeNavBar, DisplayCutout cutout) {
boolean alwaysConsumeNavBar, DisplayCutout cutout,
@Nullable @InsetSide SparseIntArray typeSideMap) {
Insets systemInsets = Insets.NONE;
Insets maxInsets = Insets.NONE;
final Rect relativeFrame = new Rect(frame);
@@ -100,13 +123,13 @@ public class InsetsState implements Parcelable {
continue;
}
systemInsets = processSource(source, systemInsets, relativeFrame,
false /* ignoreVisibility */);
false /* ignoreVisibility */, typeSideMap);
// IME won't be reported in max insets as the size depends on the EditorInfo of the IME
// target.
if (source.getType() != TYPE_IME) {
maxInsets = processSource(source, maxInsets, relativeFrameMax,
true /* ignoreVisibility */);
true /* ignoreVisibility */, null /* typeSideMap */);
}
}
return new WindowInsets(new Rect(systemInsets), null, new Rect(maxInsets), isScreenRound,
@@ -114,13 +137,39 @@ public class InsetsState implements Parcelable {
}
private Insets processSource(InsetsSource source, Insets insets, Rect relativeFrame,
boolean ignoreVisibility) {
boolean ignoreVisibility, @Nullable @InsetSide SparseIntArray typeSideMap) {
Insets currentInsets = source.calculateInsets(relativeFrame, ignoreVisibility);
insets = Insets.add(currentInsets, insets);
relativeFrame.inset(insets);
if (typeSideMap != null && !Insets.NONE.equals(currentInsets)) {
@InsetSide int insetSide = getInsetSide(currentInsets);
if (insetSide != INSET_SIDE_UNKNWON) {
typeSideMap.put(source.getType(), getInsetSide(currentInsets));
}
}
return insets;
}
/**
* Retrieves the side for a certain {@code insets}. It is required that only one field l/t/r/b
* is set in order that this method returns a meaningful result.
*/
private @InsetSide int getInsetSide(Insets insets) {
if (insets.left != 0) {
return INSET_SIDE_LEFT;
}
if (insets.top != 0) {
return INSET_SIDE_TOP;
}
if (insets.right != 0) {
return INSET_SIDE_RIGHT;
}
if (insets.bottom != 0) {
return INSET_SIDE_BOTTOM;
}
return INSET_SIDE_UNKNWON;
}
public InsetsSource getSource(@InternalInsetType int type) {
return mSources.computeIfAbsent(type, InsetsSource::new);
}

View File

@@ -0,0 +1,151 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.view;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.view.SurfaceControl.Transaction;
import com.android.internal.annotations.VisibleForTesting;
import java.util.function.Consumer;
/**
* Helper class to apply surface transactions in sync with RenderThread.
* @hide
*/
public class SyncRtSurfaceTransactionApplier {
private final Surface mTargetSurface;
private final ViewRootImpl mTargetViewRootImpl;
private final float[] mTmpFloat9 = new float[9];
/**
* @param targetView The view in the surface that acts as synchronization anchor.
*/
public SyncRtSurfaceTransactionApplier(View targetView) {
mTargetViewRootImpl = targetView != null ? targetView.getViewRootImpl() : null;
mTargetSurface = mTargetViewRootImpl != null ? mTargetViewRootImpl.mSurface : null;
}
/**
* Schedules applying surface parameters on the next frame.
*
* @param params The surface parameters to apply. DO NOT MODIFY the list after passing into
* this method to avoid synchronization issues.
*/
public void scheduleApply(final SurfaceParams... params) {
if (mTargetViewRootImpl == null) {
return;
}
mTargetViewRootImpl.registerRtFrameCallback(frame -> {
if (mTargetSurface == null || !mTargetSurface.isValid()) {
return;
}
Transaction t = new Transaction();
for (int i = params.length - 1; i >= 0; i--) {
SurfaceParams surfaceParams = params[i];
SurfaceControl surface = surfaceParams.surface;
t.deferTransactionUntilSurface(surface, mTargetSurface, frame);
applyParams(t, surfaceParams, mTmpFloat9);
}
t.setEarlyWakeup();
t.apply();
});
// Make sure a frame gets scheduled.
mTargetViewRootImpl.getView().invalidate();
}
public static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) {
t.setMatrix(params.surface, params.matrix, tmpFloat9);
t.setWindowCrop(params.surface, params.windowCrop);
t.setAlpha(params.surface, params.alpha);
t.setLayer(params.surface, params.layer);
t.setCornerRadius(params.surface, params.cornerRadius);
t.show(params.surface);
}
/**
* Creates an instance of SyncRtSurfaceTransactionApplier, deferring until the target view is
* attached if necessary.
*/
public static void create(final View targetView,
final Consumer<SyncRtSurfaceTransactionApplier> callback) {
if (targetView == null) {
// No target view, no applier
callback.accept(null);
} else if (targetView.getViewRootImpl() != null) {
// Already attached, we're good to go
callback.accept(new SyncRtSurfaceTransactionApplier(targetView));
} else {
// Haven't been attached before we can get the view root
targetView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
targetView.removeOnAttachStateChangeListener(this);
callback.accept(new SyncRtSurfaceTransactionApplier(targetView));
}
@Override
public void onViewDetachedFromWindow(View v) {
// Do nothing
}
});
}
}
public static class SurfaceParams {
/**
* Constructs surface parameters to be applied when the current view state gets pushed to
* RenderThread.
*
* @param surface The surface to modify.
* @param alpha Alpha to apply.
* @param matrix Matrix to apply.
* @param windowCrop Crop to apply.
*/
public SurfaceParams(SurfaceControl surface, float alpha, Matrix matrix,
Rect windowCrop, int layer, float cornerRadius) {
this.surface = surface;
this.alpha = alpha;
this.matrix = new Matrix(matrix);
this.windowCrop = new Rect(windowCrop);
this.layer = layer;
this.cornerRadius = cornerRadius;
}
@VisibleForTesting
public final SurfaceControl surface;
@VisibleForTesting
public final float alpha;
@VisibleForTesting
final float cornerRadius;
@VisibleForTesting
public final Matrix matrix;
@VisibleForTesting
public final Rect windowCrop;
@VisibleForTesting
public final int layer;
}
}

View File

@@ -10489,6 +10489,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @return The {@link WindowInsetsController} or {@code null} if the view isn't attached to a
* a window.
* @see Window#getInsetsController()
* @hide pending unhide
*/
public @Nullable WindowInsetsController getWindowInsetsController() {

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.view;
import android.annotation.NonNull;
import android.view.WindowInsets.Type.InsetType;
import android.view.inputmethod.EditorInfo;
/**
* Interface that informs the client about {@link WindowInsetsAnimationController} state changes.
* @hide pending unhide
*/
public interface WindowInsetsAnimationControlListener {
/**
* Gets called as soon as the animation is ready to be controlled. This may be
* delayed when the IME needs to redraw because of an {@link EditorInfo} change, or when the
* window is starting up.
*
* @param controller The controller to control the inset animation.
* @param types The {@link InsetType}s it was able to gain control over. Note that this may be
* different than the types passed into
* {@link WindowInsetsController#controlWindowInsetsAnimation} in case the window
* wasn't able to gain the controls because it wasn't the IME target or not
* currently the window that's controlling the system bars.
*/
void onReady(@NonNull WindowInsetsAnimationController controller, @InsetType int types);
/**
* Called when the window no longer has control over the requested types. If it loses control
* over one type, the whole control will be cancelled. If none of the requested types were
* available when requesting the control, the animation control will be cancelled immediately
* without {@link #onReady} being called.
*/
void onCancelled();
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.view;
import android.annotation.NonNull;
import android.graphics.Insets;
import android.view.WindowInsets.Type.InsetType;
/**
* Interface to control a window inset animation frame-by-frame.
* @hide pending unhide
*/
public interface WindowInsetsAnimationController {
/**
* Retrieves the {@link Insets} when the windows this animation is controlling are fully hidden.
*
* @return Insets when the windows this animation is controlling are fully hidden.
*/
@NonNull Insets getHiddenStateInsets();
/**
* Retrieves the {@link Insets} when the windows this animation is controlling are fully shown.
* <p>
* In case the size of a window causing insets is changing in the middle of the animation, we
* execute that height change after this animation has finished.
*
* @return Insets when the windows this animation is controlling are fully shown.
*/
@NonNull Insets getShownStateInsets();
/**
* @return The current insets on the window. These will follow any animation changes.
*/
@NonNull Insets getCurrentInsets();
/**
* @return The {@link InsetType}s this object is currently controlling.
*/
@InsetType int getTypes();
/**
* Modifies the insets by indirectly moving the windows around in the system that are causing
* window insets.
* <p>
* Note that this will <b>not</b> inform the view system of a full inset change via
* {@link View#dispatchApplyWindowInsets} in order to avoid a full layout pass during the
* animation. If you'd like to animate views during a window inset animation, use
* TODO add link to animation listeners.
* <p>
* {@link View#dispatchApplyWindowInsets} will instead be called once the animation has
* finished, i.e. once {@link #finish} has been called.
*
* @param insets The new insets to apply. Based on the requested insets, the system will
* calculate the positions of the windows in the system causing insets such that
* the resulting insets of that configuration will match the passed in parameter.
* Note that these insets are being clamped to the range from
* {@link #getHiddenStateInsets} to {@link #getShownStateInsets}
*/
void changeInsets(@NonNull Insets insets);
/**
* @param shownTypes The list of windows causing insets that should remain shown after finishing
* the animation.
*/
void finish(@InsetType int shownTypes);
}

View File

@@ -16,6 +16,7 @@
package android.view;
import android.annotation.NonNull;
import android.view.WindowInsets.Type.InsetType;
/**
@@ -51,4 +52,15 @@ public interface WindowInsetsController {
* would like to make disappear.
*/
void hide(@InsetType int types);
/**
* Lets the application control window inset animations in a frame-by-frame manner by modifying
* the position of the windows in the system causing insets directly.
*
* @param types The {@link InsetType}s the application has requested to control.
* @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the
* windows are ready to be controlled, among other callbacks.
*/
void controlWindowInsetsAnimation(@InsetType int types,
@NonNull WindowInsetsAnimationControlListener listener);
}