summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt6
-rw-r--r--core/java/android/inputmethodservice/SoftInputWindow.java8
-rw-r--r--core/java/android/view/InsetsController.java68
-rw-r--r--core/java/android/view/PendingInsetsController.java29
-rw-r--r--core/java/android/view/WindowInsetsController.java53
-rw-r--r--core/tests/coretests/src/android/view/InsetsControllerTest.java17
-rw-r--r--core/tests/coretests/src/android/view/PendingInsetsControllerTest.java34
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java4
-rw-r--r--tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java59
9 files changed, 267 insertions, 11 deletions
diff --git a/api/current.txt b/api/current.txt
index 15f10faa4c4a..56846beef518 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -55587,10 +55587,12 @@ package android.view {
}
public interface WindowInsetsController {
+ method public void addOnControllableInsetsChangedListener(@NonNull android.view.WindowInsetsController.OnControllableInsetsChangedListener);
method @NonNull public android.os.CancellationSignal controlWindowInsetsAnimation(int, long, @Nullable android.view.animation.Interpolator, @NonNull android.view.WindowInsetsAnimationControlListener);
method public int getSystemBarsAppearance();
method public int getSystemBarsBehavior();
method public void hide(int);
+ method public void removeOnControllableInsetsChangedListener(@NonNull android.view.WindowInsetsController.OnControllableInsetsChangedListener);
method public void setSystemBarsAppearance(int, int);
method public void setSystemBarsBehavior(int);
method public void show(int);
@@ -55601,6 +55603,10 @@ package android.view {
field public static final int BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE = 2; // 0x2
}
+ public static interface WindowInsetsController.OnControllableInsetsChangedListener {
+ method public void onControllableInsetsChanged(@NonNull android.view.WindowInsetsController, int);
+ }
+
public interface WindowManager extends android.view.ViewManager {
method @NonNull public default android.view.WindowMetrics getCurrentWindowMetrics();
method @Deprecated public android.view.Display getDefaultDisplay();
diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java
index 0513feef801f..6efd03c44b9f 100644
--- a/core/java/android/inputmethodservice/SoftInputWindow.java
+++ b/core/java/android/inputmethodservice/SoftInputWindow.java
@@ -28,6 +28,7 @@ import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.view.View;
import android.view.WindowManager;
import java.lang.annotation.Retention;
@@ -94,6 +95,13 @@ public class SoftInputWindow extends Dialog {
lp.token = token;
getWindow().setAttributes(lp);
updateWindowState(SoftInputWindowState.TOKEN_SET);
+
+ // As soon as we have a token, make sure the window is added (but not shown) by
+ // setting visibility to INVISIBLE and calling show() on Dialog. Note that
+ // WindowInsetsController.OnControllableInsetsChangedListener relies on the window
+ // being added to function.
+ getWindow().getDecorView().setVisibility(View.INVISIBLE);
+ show();
return;
case SoftInputWindowState.TOKEN_SET:
case SoftInputWindowState.SHOWN_AT_LEAST_ONCE:
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 302d4f3f79c8..607886f3b13b 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -52,6 +52,7 @@ import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -59,6 +60,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.function.BiFunction;
/**
@@ -306,6 +308,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private SyncRtSurfaceTransactionApplier mApplier;
private Runnable mPendingControlTimeout = this::abortPendingImeControlRequest;
+ private final ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners
+ = new ArrayList<>();
+
+ /** Set of inset types for which an animation was started since last resetting this field */
+ private @InsetsType int mLastStartedAnimTypes;
public InsetsController(ViewRootImpl viewRoot) {
this(viewRoot, (controller, type) -> {
@@ -459,6 +466,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
mTmpControlArray.clear();
+
+ // Do not override any animations that the app started in the OnControllableInsetsChanged
+ // listeners.
+ int animatingTypes = invokeControllableInsetsChangedListeners();
+ showTypes[0] &= ~animatingTypes;
+ hideTypes[0] &= ~animatingTypes;
+
if (showTypes[0] != 0) {
applyAnimation(showTypes[0], true /* show */, false /* fromIme */);
}
@@ -542,9 +556,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private CancellationSignal controlWindowInsetsAnimation(@InsetsType int types,
WindowInsetsAnimationControlListener listener, boolean fromIme, long durationMs,
@Nullable Interpolator interpolator, @AnimationType int animationType) {
- // If the frame of our window doesn't span the entire display, the control API makes very
- // little sense, as we don't deal with negative insets. So just cancel immediately.
- if (!mState.getDisplayFrame().equals(mFrame)) {
+ if (!checkDisplayFramesForControlling()) {
listener.onCancelled();
CancellationSignal cancellationSignal = new CancellationSignal();
cancellationSignal.cancel();
@@ -554,6 +566,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
false /* fade */, animationType, getLayoutInsetsDuringAnimationMode(types));
}
+ private boolean checkDisplayFramesForControlling() {
+
+ // If the frame of our window doesn't span the entire display, the control API makes very
+ // little sense, as we don't deal with negative insets. So just cancel immediately.
+ return mState.getDisplayFrame().equals(mFrame);
+ }
+
private CancellationSignal controlAnimationUnchecked(@InsetsType int types,
WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme,
long durationMs, Interpolator interpolator, boolean fade,
@@ -567,6 +586,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
return cancellationSignal;
}
cancelExistingControllers(types);
+ mLastStartedAnimTypes |= types;
final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
final SparseArray<InsetsSourceControl> controls = new SparseArray<>();
@@ -994,6 +1014,48 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
return mViewRoot.mWindowAttributes.insetsFlags.behavior;
}
+ private @InsetsType int calculateControllableTypes() {
+ if (!checkDisplayFramesForControlling()) {
+ return 0;
+ }
+ @InsetsType int result = 0;
+ for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
+ InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
+ if (consumer.getControl() != null) {
+ result |= toPublicType(consumer.mType);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @return The types that are now animating due to a listener invoking control/show/hide
+ */
+ private @InsetsType int invokeControllableInsetsChangedListeners() {
+ mLastStartedAnimTypes = 0;
+ @InsetsType int types = calculateControllableTypes();
+ int size = mControllableInsetsChangedListeners.size();
+ for (int i = 0; i < size; i++) {
+ mControllableInsetsChangedListeners.get(i).onControllableInsetsChanged(this, types);
+ }
+ return mLastStartedAnimTypes;
+ }
+
+ @Override
+ public void addOnControllableInsetsChangedListener(
+ OnControllableInsetsChangedListener listener) {
+ Objects.requireNonNull(listener);
+ mControllableInsetsChangedListeners.add(listener);
+ listener.onControllableInsetsChanged(this, calculateControllableTypes());
+ }
+
+ @Override
+ public void removeOnControllableInsetsChangedListener(
+ OnControllableInsetsChangedListener listener) {
+ Objects.requireNonNull(listener);
+ mControllableInsetsChangedListeners.remove(listener);
+ }
+
/**
* At the time we receive new leashes (e.g. InsetsSourceConsumer is processing
* setControl) we need to release the old leash. But we may have already scheduled
diff --git a/core/java/android/view/PendingInsetsController.java b/core/java/android/view/PendingInsetsController.java
index c0ed9359c613..7f3641803770 100644
--- a/core/java/android/view/PendingInsetsController.java
+++ b/core/java/android/view/PendingInsetsController.java
@@ -38,6 +38,8 @@ public class PendingInsetsController implements WindowInsetsController {
private @Behavior int mBehavior = KEEP_BEHAVIOR;
private final InsetsState mDummyState = new InsetsState();
private InsetsController mReplayedInsetsController;
+ private ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners
+ = new ArrayList<>();
@Override
public void show(int types) {
@@ -112,6 +114,27 @@ public class PendingInsetsController implements WindowInsetsController {
return mDummyState;
}
+ @Override
+ public void addOnControllableInsetsChangedListener(
+ OnControllableInsetsChangedListener listener) {
+ if (mReplayedInsetsController != null) {
+ mReplayedInsetsController.addOnControllableInsetsChangedListener(listener);
+ } else {
+ mControllableInsetsChangedListeners.add(listener);
+ listener.onControllableInsetsChanged(this, 0);
+ }
+ }
+
+ @Override
+ public void removeOnControllableInsetsChangedListener(
+ OnControllableInsetsChangedListener listener) {
+ if (mReplayedInsetsController != null) {
+ mReplayedInsetsController.removeOnControllableInsetsChangedListener(listener);
+ } else {
+ mControllableInsetsChangedListeners.remove(listener);
+ }
+ }
+
/**
* Replays the commands on {@code controller} and attaches it to this instance such that any
* calls will be forwarded to the real instance in the future.
@@ -128,9 +151,15 @@ public class PendingInsetsController implements WindowInsetsController {
for (int i = 0; i < size; i++) {
mRequests.get(i).replay(controller);
}
+ size = mControllableInsetsChangedListeners.size();
+ for (int i = 0; i < size; i++) {
+ controller.addOnControllableInsetsChangedListener(
+ mControllableInsetsChangedListeners.get(i));
+ }
// Reset all state so it doesn't get applied twice just in case
mRequests.clear();
+ mControllableInsetsChangedListeners.clear();
mBehavior = KEEP_BEHAVIOR;
mAppearance = 0;
mAppearanceMask = 0;
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index b7ca03798bbe..2ad557e6d9f6 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -20,7 +20,9 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Insets;
+import android.inputmethodservice.InputMethodService;
import android.os.CancellationSignal;
+import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
import android.view.animation.Interpolator;
@@ -212,4 +214,55 @@ public interface WindowInsetsController {
* @hide
*/
InsetsState getState();
+
+ /**
+ * Adds a {@link OnControllableInsetsChangedListener} to the window insets controller.
+ *
+ * @param listener The listener to add.
+ *
+ * @see OnControllableInsetsChangedListener
+ * @see #removeOnControllableInsetsChangedListener(OnControllableInsetsChangedListener)
+ */
+ void addOnControllableInsetsChangedListener(
+ @NonNull OnControllableInsetsChangedListener listener);
+
+ /**
+ * Removes a {@link OnControllableInsetsChangedListener} from the window insets controller.
+ *
+ * @param listener The listener to remove.
+ *
+ * @see OnControllableInsetsChangedListener
+ * @see #addOnControllableInsetsChangedListener(OnControllableInsetsChangedListener)
+ */
+ void removeOnControllableInsetsChangedListener(
+ @NonNull OnControllableInsetsChangedListener listener);
+
+ /**
+ * Listener to be notified when the set of controllable {@link InsetsType} controlled by a
+ * {@link WindowInsetsController} changes.
+ * <p>
+ * Once a {@link InsetsType} becomes controllable, the app will be able to control the window
+ * that is causing this type of insets by calling {@link #controlWindowInsetsAnimation}.
+ * <p>
+ * Note: When listening to controllability of the {@link Type#ime},
+ * {@link #controlWindowInsetsAnimation} may still fail in case the {@link InputMethodService}
+ * decides to cancel the show request. This could happen when there is a hardware keyboard
+ * attached.
+ *
+ * @see #addOnControllableInsetsChangedListener(OnControllableInsetsChangedListener)
+ * @see #removeOnControllableInsetsChangedListener(OnControllableInsetsChangedListener)
+ */
+ interface OnControllableInsetsChangedListener {
+
+ /**
+ * Called when the set of controllable {@link InsetsType} changes.
+ *
+ * @param controller The controller for which the set of controllable {@link InsetsType}s
+ * are changing.
+ * @param typeMask Bitwise type-mask of the {@link InsetsType}s the controller is currently
+ * able to control.
+ */
+ void onControllableInsetsChanged(@NonNull WindowInsetsController controller,
+ @InsetsType int typeMask);
+ }
}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 7737b1a2a776..023fc1736aca 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -49,6 +49,7 @@ import android.os.CancellationSignal;
import android.platform.test.annotations.Presubmit;
import android.view.SurfaceControl.Transaction;
import android.view.WindowInsets.Type;
+import android.view.WindowInsetsController.OnControllableInsetsChangedListener;
import android.view.WindowManager.BadTokenException;
import android.view.WindowManager.LayoutParams;
import android.view.animation.LinearInterpolator;
@@ -67,6 +68,8 @@ import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.Mockito;
import java.util.concurrent.CountDownLatch;
import java.util.function.Supplier;
@@ -171,15 +174,24 @@ public class InsetsControllerTest {
mController.onControlsChanged(new InsetsSourceControl[] { control });
assertEquals(mLeash,
mController.getSourceConsumer(ITYPE_STATUS_BAR).getControl().getLeash());
+ mController.addOnControllableInsetsChangedListener(
+ ((controller, typeMask) -> assertEquals(statusBars(), typeMask)));
}
@Test
public void testControlsRevoked() {
+ OnControllableInsetsChangedListener listener
+ = mock(OnControllableInsetsChangedListener.class);
+ mController.addOnControllableInsetsChangedListener(listener);
InsetsSourceControl control =
new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
mController.onControlsChanged(new InsetsSourceControl[] { control });
mController.onControlsChanged(new InsetsSourceControl[0]);
assertNull(mController.getSourceConsumer(ITYPE_STATUS_BAR).getControl());
+ InOrder inOrder = Mockito.inOrder(listener);
+ inOrder.verify(listener).onControllableInsetsChanged(eq(mController), eq(0));
+ inOrder.verify(listener).onControllableInsetsChanged(eq(mController), eq(statusBars()));
+ inOrder.verify(listener).onControllableInsetsChanged(eq(mController), eq(0));
}
@Test
@@ -206,10 +218,15 @@ public class InsetsControllerTest {
public void testFrameDoesntMatchDisplay() {
mController.onFrameChanged(new Rect(0, 0, 100, 100));
mController.getState().setDisplayFrame(new Rect(0, 0, 200, 200));
+ InsetsSourceControl control =
+ new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
+ mController.onControlsChanged(new InsetsSourceControl[] { control });
WindowInsetsAnimationControlListener controlListener =
mock(WindowInsetsAnimationControlListener.class);
mController.controlWindowInsetsAnimation(0, 0 /* durationMs */, new LinearInterpolator(),
controlListener);
+ mController.addOnControllableInsetsChangedListener(
+ (controller, typeMask) -> assertEquals(0, typeMask));
verify(controlListener).onCancelled();
verify(controlListener, never()).onReady(any(), anyInt());
}
diff --git a/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java b/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
index 9787b7780702..9797178fca6e 100644
--- a/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
@@ -25,12 +25,14 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.os.CancellationSignal;
import android.platform.test.annotations.Presubmit;
+import android.view.WindowInsetsController.OnControllableInsetsChangedListener;
import android.view.animation.LinearInterpolator;
import org.junit.Before;
@@ -163,11 +165,43 @@ public class PendingInsetsControllerTest {
}
@Test
+ public void testAddOnControllableInsetsChangedListener() {
+ OnControllableInsetsChangedListener listener =
+ mock(OnControllableInsetsChangedListener.class);
+ mPendingInsetsController.addOnControllableInsetsChangedListener(listener);
+ mPendingInsetsController.replayAndAttach(mReplayedController);
+ verify(mReplayedController).addOnControllableInsetsChangedListener(eq(listener));
+ verify(listener).onControllableInsetsChanged(eq(mPendingInsetsController), eq(0));
+ }
+
+ @Test
+ public void testAddRemoveControllableInsetsChangedListener() {
+ OnControllableInsetsChangedListener listener =
+ mock(OnControllableInsetsChangedListener.class);
+ mPendingInsetsController.addOnControllableInsetsChangedListener(listener);
+ mPendingInsetsController.removeOnControllableInsetsChangedListener(listener);
+ mPendingInsetsController.replayAndAttach(mReplayedController);
+ verify(mReplayedController, never()).addOnControllableInsetsChangedListener(any());
+ verify(listener).onControllableInsetsChanged(eq(mPendingInsetsController), eq(0));
+ }
+
+ @Test
+ public void testAddOnControllableInsetsChangedListener_direct() {
+ mPendingInsetsController.replayAndAttach(mReplayedController);
+ OnControllableInsetsChangedListener listener =
+ mock(OnControllableInsetsChangedListener.class);
+ mPendingInsetsController.addOnControllableInsetsChangedListener(listener);
+ verify(mReplayedController).addOnControllableInsetsChangedListener(eq(listener));
+ }
+
+ @Test
public void testReplayTwice() {
mPendingInsetsController.show(systemBars());
mPendingInsetsController.setSystemBarsBehavior(BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
mPendingInsetsController.setSystemBarsAppearance(APPEARANCE_LIGHT_STATUS_BARS,
APPEARANCE_LIGHT_STATUS_BARS);
+ mPendingInsetsController.addOnControllableInsetsChangedListener(
+ (controller, typeMask) -> {});
mPendingInsetsController.replayAndAttach(mReplayedController);
InsetsController secondController = mock(InsetsController.class);
mPendingInsetsController.replayAndAttach(secondController);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 42a66adff5e5..ecbbb03a7c94 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2447,10 +2447,6 @@ public class WindowManagerService extends IWindowManager.Stub
// of a transaction to avoid artifacts.
win.mAnimatingExit = true;
} else {
- final DisplayContent displayContent = win.getDisplayContent();
- if (displayContent.mInputMethodWindow == win) {
- displayContent.setInputMethodWindowLocked(null);
- }
boolean stopped = win.mActivityRecord != null ? win.mActivityRecord.mAppStopped : true;
// We set mDestroying=true so ActivityRecord#notifyAppStopped in-to destroy surfaces
// will later actually destroy the surface if we do not do so here. Normally we leave
diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
index 8e6f1985a7d9..e41517085c36 100644
--- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
@@ -16,11 +16,15 @@
package com.google.android.test.windowinsetstests;
+import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
import static java.lang.Math.max;
import static java.lang.Math.min;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -37,6 +41,8 @@ import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimation.Callback;
import android.view.WindowInsetsAnimationControlListener;
import android.view.WindowInsetsAnimationController;
+import android.view.WindowInsetsController;
+import android.view.WindowInsetsController.OnControllableInsetsChangedListener;
import android.view.animation.LinearInterpolator;
import android.widget.LinearLayout;
@@ -82,8 +88,8 @@ public class WindowInsetsActivity extends AppCompatActivity {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mDown = event.getY();
- mDownInsets = v.getRootWindowInsets().getInsets(Type.ime());
- mShownAtDown = v.getRootWindowInsets().isVisible(Type.ime());
+ mDownInsets = v.getRootWindowInsets().getInsets(ime());
+ mShownAtDown = v.getRootWindowInsets().isVisible(ime());
mRequestedController = false;
mCurrentRequest = null;
break;
@@ -94,7 +100,7 @@ public class WindowInsetsActivity extends AppCompatActivity {
> mViewConfiguration.getScaledTouchSlop()
&& !mRequestedController) {
mRequestedController = true;
- v.getWindowInsetsController().controlWindowInsetsAnimation(Type.ime(),
+ v.getWindowInsetsController().controlWindowInsetsAnimation(ime(),
1000, new LinearInterpolator(),
mCurrentRequest = new WindowInsetsAnimationControlListener() {
@Override
@@ -189,6 +195,51 @@ public class WindowInsetsActivity extends AppCompatActivity {
getWindow().getDecorView().post(() -> getWindow().setDecorFitsSystemWindows(false));
}
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ getWindow().getInsetsController().addOnControllableInsetsChangedListener(
+ new OnControllableInsetsChangedListener() {
+
+ boolean hasControl = false;
+ @Override
+ public void onControllableInsetsChanged(WindowInsetsController controller,
+ int types) {
+ if ((types & ime()) != 0 && !hasControl) {
+ hasControl = true;
+ controller.controlWindowInsetsAnimation(ime(), -1,
+ new LinearInterpolator(),
+ new WindowInsetsAnimationControlListener() {
+ @Override
+ public void onReady(
+ WindowInsetsAnimationController controller,
+ int types) {
+ ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
+ anim.setDuration(1500);
+ anim.addUpdateListener(animation
+ -> controller.setInsetsAndAlpha(
+ controller.getShownStateInsets(),
+ (float) animation.getAnimatedValue(),
+ anim.getAnimatedFraction()));
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ controller.finish(true);
+ }
+ });
+ anim.start();
+ }
+
+ @Override
+ public void onCancelled() {
+ }
+ });
+ }
+ }
+ });
+ }
+
static class Transition {
private int mEndBottom;
private int mStartBottom;
@@ -200,7 +251,7 @@ public class WindowInsetsActivity extends AppCompatActivity {
}
void onPrepare(WindowInsetsAnimation animation) {
- if ((animation.getTypeMask() & Type.ime()) != 0) {
+ if ((animation.getTypeMask() & ime()) != 0) {
mInsetsAnimation = animation;
}
mStartBottom = mView.getBottom();