Merge "Force showing navigation bar while IME is visible" into sc-dev am: a254d7fd43

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14486201

Change-Id: I7f460b9435bd20bb34d1e0c578e5d0d725d371db
This commit is contained in:
Tiger Huang
2021-05-19 15:33:56 +00:00
committed by Automerger Merge Worker
6 changed files with 86 additions and 7 deletions

View File

@@ -86,6 +86,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
private final InsetsState mInitialInsetsState; private final InsetsState mInitialInsetsState;
private final @AnimationType int mAnimationType; private final @AnimationType int mAnimationType;
private final @InsetsType int mTypes; private final @InsetsType int mTypes;
private @InsetsType int mControllingTypes;
private final InsetsAnimationControlCallbacks mController; private final InsetsAnimationControlCallbacks mController;
private final WindowInsetsAnimation mAnimation; private final WindowInsetsAnimation mAnimation;
/** @see WindowInsetsAnimationController#hasZeroInsetsIme */ /** @see WindowInsetsAnimationController#hasZeroInsetsIme */
@@ -112,6 +113,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
mControls = controls; mControls = controls;
mListener = listener; mListener = listener;
mTypes = types; mTypes = types;
mControllingTypes = types;
mController = controller; mController = controller;
mInitialInsetsState = new InsetsState(state, true /* copySources */); mInitialInsetsState = new InsetsState(state, true /* copySources */);
if (frame != null) { if (frame != null) {
@@ -186,6 +188,16 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
return mTypes; return mTypes;
} }
@Override
public int getControllingTypes() {
return mControllingTypes;
}
@Override
public void notifyControlRevoked(@InsetsType int types) {
mControllingTypes &= ~types;
}
@Override @Override
public @AnimationType int getAnimationType() { public @AnimationType int getAnimationType() {
return mAnimationType; return mAnimationType;

View File

@@ -29,10 +29,21 @@ import android.view.WindowInsets.Type.InsetsType;
public interface InsetsAnimationControlRunner { public interface InsetsAnimationControlRunner {
/** /**
* @return The {@link InsetsType} the animation of this runner is controlling. * @return The {@link InsetsType} the animation of this runner controls.
*/ */
@InsetsType int getTypes(); @InsetsType int getTypes();
/**
* @return The {@link InsetsType} the animation of this runner is controlling. This can be
* changed if a control is revoked.
*/
@InsetsType int getControllingTypes();
/**
* Notifies {@link InsetsType types} of control are getting revoked.
*/
void notifyControlRevoked(@InsetsType int types);
/** /**
* Cancels the animation. * Cancels the animation.
*/ */

View File

@@ -139,6 +139,17 @@ public class InsetsAnimationThreadControlRunner implements InsetsAnimationContro
return mControl.getTypes(); return mControl.getTypes();
} }
@Override
@UiThread
public int getControllingTypes() {
return mControl.getControllingTypes();
}
@Override
public void notifyControlRevoked(@InsetsType int types) {
mControl.notifyControlRevoked(types);
}
@Override @Override
@UiThread @UiThread
public void cancel() { public void cancel() {

View File

@@ -1224,9 +1224,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
} }
void notifyControlRevoked(InsetsSourceConsumer consumer) { void notifyControlRevoked(InsetsSourceConsumer consumer) {
final @InsetsType int types = toPublicType(consumer.getType());
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner; InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner;
if ((control.getTypes() & toPublicType(consumer.getType())) != 0) { control.notifyControlRevoked(types);
if (control.getControllingTypes() == 0) {
cancelAnimation(control, true /* invokeCallback */); cancelAnimation(control, true /* invokeCallback */);
} }
} }

View File

@@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.view.InsetsController.ANIMATION_TYPE_HIDE; import static android.view.InsetsController.ANIMATION_TYPE_HIDE;
import static android.view.InsetsController.ANIMATION_TYPE_SHOW; import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.SyncRtSurfaceTransactionApplier.applyParams; import static android.view.SyncRtSurfaceTransactionApplier.applyParams;
@@ -207,7 +208,8 @@ class InsetsPolicy {
*/ */
InsetsState getInsetsForWindow(WindowState target) { InsetsState getInsetsForWindow(WindowState target) {
final InsetsState originalState = mStateController.getInsetsForWindow(target); final InsetsState originalState = mStateController.getInsetsForWindow(target);
return adjustVisibilityForTransientTypes(originalState); final InsetsState state = adjustVisibilityForTransientTypes(originalState);
return target.mIsImWindow ? adjustVisibilityForIme(state, state == originalState) : state;
} }
/** /**
@@ -237,6 +239,20 @@ class InsetsPolicy {
return state; return state;
} }
// Navigation bar insets is always visible to IME.
private static InsetsState adjustVisibilityForIme(InsetsState originalState,
boolean copyState) {
final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR);
if (originalNavSource != null && !originalNavSource.isVisible()) {
final InsetsState state = copyState ? new InsetsState(originalState) : originalState;
final InsetsSource navSource = new InsetsSource(originalNavSource);
navSource.setVisible(true);
state.addSource(navSource);
return state;
}
return originalState;
}
void onInsetsModified(InsetsControlTarget caller) { void onInsetsModified(InsetsControlTarget caller) {
mStateController.onInsetsModified(caller); mStateController.onInsetsModified(caller);
checkAbortTransient(caller); checkAbortTransient(caller);
@@ -245,17 +261,21 @@ class InsetsPolicy {
/** /**
* Called when a control target modified the insets state. If the target set a insets source to * Called when a control target modified the insets state. If the target set a insets source to
* visible while it is shown transiently, we need to abort the transient state. * visible while it is shown transiently, we need to abort the transient state. While IME is
* requested visible, we also need to abort the transient state of navigation bar if it is shown
* transiently.
* *
* @param caller who changed the insets state. * @param caller who changed the insets state.
*/ */
private void checkAbortTransient(InsetsControlTarget caller) { private void checkAbortTransient(InsetsControlTarget caller) {
if (mShowingTransientTypes.size() != 0) { if (mShowingTransientTypes.size() != 0) {
IntArray abortTypes = new IntArray(); final IntArray abortTypes = new IntArray();
final boolean imeRequestedVisible = caller.getRequestedVisibility(ITYPE_IME);
for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) { for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) {
final @InternalInsetsType int type = mShowingTransientTypes.get(i); final @InternalInsetsType int type = mShowingTransientTypes.get(i);
if (mStateController.isFakeTarget(type, caller) if ((mStateController.isFakeTarget(type, caller)
&& caller.getRequestedVisibility(type)) { && caller.getRequestedVisibility(type))
|| (type == ITYPE_NAVIGATION_BAR && imeRequestedVisible)) {
mShowingTransientTypes.remove(i); mShowingTransientTypes.remove(i);
abortTypes.add(type); abortTypes.add(type);
} }
@@ -330,6 +350,11 @@ class InsetsPolicy {
private @Nullable InsetsControlTarget getNavControlTarget(@Nullable WindowState focusedWin, private @Nullable InsetsControlTarget getNavControlTarget(@Nullable WindowState focusedWin,
boolean forceShowsSystemBarsForWindowingMode) { boolean forceShowsSystemBarsForWindowingMode) {
final WindowState imeWin = mDisplayContent.mInputMethodWindow;
if (imeWin != null && imeWin.isVisible()) {
// Force showing navigation bar while IME is visible.
return null;
}
if (mShowingTransientTypes.indexOf(ITYPE_NAVIGATION_BAR) != -1) { if (mShowingTransientTypes.indexOf(ITYPE_NAVIGATION_BAR) != -1) {
return mDummyControlTarget; return mDummyControlTarget;
} }

View File

@@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_270;
@@ -78,6 +79,7 @@ import android.os.RemoteException;
import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.Presubmit;
import android.view.Gravity; import android.view.Gravity;
import android.view.InputWindowHandle; import android.view.InputWindowHandle;
import android.view.InsetsSource;
import android.view.InsetsState; import android.view.InsetsState;
import android.view.SurfaceControl; import android.view.SurfaceControl;
import android.view.WindowManager; import android.view.WindowManager;
@@ -880,6 +882,22 @@ public class WindowStateTests extends WindowTestsBase {
verify(app).notifyInsetsChanged(); verify(app).notifyInsetsChanged();
} }
@UseTestDisplay(addWindows = { W_INPUT_METHOD, W_ACTIVITY })
@Test
public void testImeAlwaysReceivesVisibleNavigationBarInsets() {
final InsetsSource navSource = new InsetsSource(ITYPE_NAVIGATION_BAR);
mImeWindow.mAboveInsetsState.addSource(navSource);
mAppWindow.mAboveInsetsState.addSource(navSource);
navSource.setVisible(false);
assertTrue(mImeWindow.getInsetsState().getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR));
assertFalse(mAppWindow.getInsetsState().getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR));
navSource.setVisible(true);
assertTrue(mImeWindow.getInsetsState().getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR));
assertTrue(mAppWindow.getInsetsState().getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR));
}
@UseTestDisplay(addWindows = { W_ACTIVITY }) @UseTestDisplay(addWindows = { W_ACTIVITY })
@Test @Test
public void testUpdateImeControlTargetWhenLeavingMultiWindow() { public void testUpdateImeControlTargetWhenLeavingMultiWindow() {