Merge "Add OnControllableInsetsChangedListener" into rvc-dev
This commit is contained in:
@@ -55582,10 +55582,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);
|
||||
@@ -55596,6 +55598,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();
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -162,12 +164,44 @@ public class PendingInsetsControllerTest {
|
||||
APPEARANCE_LIGHT_STATUS_BARS);
|
||||
}
|
||||
|
||||
@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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user