Merge "Fix IME jumpcut when playing user IME animation after IME restarted" into tm-qpr-dev am: 469cbe9037

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

Change-Id: I6a80cf882660267d6a131ebb016a09e09c34297a
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Ming-Shin Lu
2022-08-25 16:32:23 +00:00
committed by Automerger Merge Worker
4 changed files with 66 additions and 10 deletions

View File

@@ -176,7 +176,9 @@ public class InsetsSourceConsumer {
// If we have a new leash, make sure visibility is up-to-date, even though we // If we have a new leash, make sure visibility is up-to-date, even though we
// didn't want to run an animation above. // didn't want to run an animation above.
applyRequestedVisibilityToControl(); if (mController.getAnimationType(control.getType()) == ANIMATION_TYPE_NONE) {
applyRequestedVisibilityToControl();
}
// Remove the surface that owned by last control when it lost. // Remove the surface that owned by last control when it lost.
if (!requestedVisible && lastControl == null) { if (!requestedVisible && lastControl == null) {

View File

@@ -18,8 +18,10 @@ package android.view;
import static android.view.InsetsController.ANIMATION_TYPE_NONE; import static android.view.InsetsController.ANIMATION_TYPE_NONE;
import static android.view.InsetsController.ANIMATION_TYPE_USER; import static android.view.InsetsController.ANIMATION_TYPE_USER;
import static android.view.InsetsSourceConsumer.ShowResult.SHOW_IMMEDIATELY;
import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.statusBars; import static android.view.WindowInsets.Type.statusBars;
import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertEquals;
@@ -28,6 +30,7 @@ import static junit.framework.TestCase.assertTrue;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset; import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.verifyZeroInteractions;
@@ -75,6 +78,7 @@ public class InsetsSourceConsumerTest {
private boolean mRemoveSurfaceCalled = false; private boolean mRemoveSurfaceCalled = false;
private InsetsController mController; private InsetsController mController;
private InsetsState mState; private InsetsState mState;
private ViewRootImpl mViewRoot;
@Before @Before
public void setup() { public void setup() {
@@ -86,10 +90,9 @@ public class InsetsSourceConsumerTest {
instrumentation.runOnMainSync(() -> { instrumentation.runOnMainSync(() -> {
final Context context = instrumentation.getTargetContext(); final Context context = instrumentation.getTargetContext();
// cannot mock ViewRootImpl since it's final. // cannot mock ViewRootImpl since it's final.
final ViewRootImpl viewRootImpl = new ViewRootImpl(context, mViewRoot = new ViewRootImpl(context, context.getDisplayNoVerify());
context.getDisplayNoVerify());
try { try {
viewRootImpl.setView(new TextView(context), new LayoutParams(), null); mViewRoot.setView(new TextView(context), new LayoutParams(), null);
} catch (BadTokenException e) { } catch (BadTokenException e) {
// activity isn't running, lets ignore BadTokenException. // activity isn't running, lets ignore BadTokenException.
} }
@@ -97,7 +100,7 @@ public class InsetsSourceConsumerTest {
mSpyInsetsSource = Mockito.spy(new InsetsSource(ITYPE_STATUS_BAR)); mSpyInsetsSource = Mockito.spy(new InsetsSource(ITYPE_STATUS_BAR));
mState.addSource(mSpyInsetsSource); mState.addSource(mSpyInsetsSource);
mController = new InsetsController(new ViewRootInsetsControllerHost(viewRootImpl)); mController = new InsetsController(new ViewRootInsetsControllerHost(mViewRoot));
mConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR, mState, mConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR, mState,
() -> mMockTransaction, mController) { () -> mMockTransaction, mController) {
@Override @Override
@@ -207,4 +210,40 @@ public class InsetsSourceConsumerTest {
}); });
} }
@Test
public void testWontUpdateImeLeashVisibility_whenAnimation() {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
InsetsState state = new InsetsState();
ViewRootInsetsControllerHost host = new ViewRootInsetsControllerHost(mViewRoot);
InsetsController insetsController = new InsetsController(host, (controller, type) -> {
if (type == ITYPE_IME) {
return new InsetsSourceConsumer(ITYPE_IME, state,
() -> mMockTransaction, controller) {
@Override
public int requestShow(boolean fromController) {
return SHOW_IMMEDIATELY;
}
};
}
return new InsetsSourceConsumer(type, controller.getState(), Transaction::new,
controller);
}, host.getHandler());
InsetsSourceConsumer imeConsumer = insetsController.getSourceConsumer(ITYPE_IME);
// Initial IME insets source control with its leash.
imeConsumer.setControl(new InsetsSourceControl(ITYPE_IME, mLeash,
false /* initialVisible */, new Point(), Insets.NONE), new int[1], new int[1]);
reset(mMockTransaction);
// Verify when the app requests controlling show IME animation, the IME leash
// visibility won't be updated when the consumer received the same leash in setControl.
insetsController.controlWindowInsetsAnimation(ime(), 0L,
null /* interpolator */, null /* cancellationSignal */, null /* listener */);
assertTrue(insetsController.getAnimationType(ITYPE_IME) == ANIMATION_TYPE_USER);
imeConsumer.setControl(new InsetsSourceControl(ITYPE_IME, mLeash,
true /* initialVisible */, new Point(), Insets.NONE), new int[1], new int[1]);
verify(mMockTransaction, never()).show(mLeash);
});
}
} }

View File

@@ -4388,13 +4388,20 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/ */
@VisibleForTesting @VisibleForTesting
InsetsControlTarget computeImeControlTarget() { InsetsControlTarget computeImeControlTarget() {
if (mImeInputTarget == null) {
// A special case that if there is no IME input target while the IME is being killed,
// in case seeing unexpected IME surface visibility change when delivering the IME leash
// to the remote insets target during the IME restarting, but the focus window is not in
// multi-windowing mode, return null target until the next input target updated.
return null;
}
final WindowState imeInputTarget = mImeInputTarget.getWindowState();
if (!isImeControlledByApp() && mRemoteInsetsControlTarget != null if (!isImeControlledByApp() && mRemoteInsetsControlTarget != null
|| (mImeInputTarget != null || getImeHostOrFallback(imeInputTarget) == mRemoteInsetsControlTarget) {
&& getImeHostOrFallback(mImeInputTarget.getWindowState())
== mRemoteInsetsControlTarget)) {
return mRemoteInsetsControlTarget; return mRemoteInsetsControlTarget;
} else { } else {
return mImeInputTarget != null ? mImeInputTarget.getWindowState() : null; return imeInputTarget;
} }
} }

View File

@@ -1251,7 +1251,15 @@ public class DisplayContentTests extends WindowTestsBase {
public void testComputeImeControlTarget() throws Exception { public void testComputeImeControlTarget() throws Exception {
final DisplayContent dc = createNewDisplay(); final DisplayContent dc = createNewDisplay();
dc.setRemoteInsetsController(createDisplayWindowInsetsController()); dc.setRemoteInsetsController(createDisplayWindowInsetsController());
dc.setImeInputTarget(createWindow(null, TYPE_BASE_APPLICATION, "app")); dc.mCurrentFocus = createWindow(null, TYPE_BASE_APPLICATION, "app");
// Expect returning null IME control target when the focus window has not yet been the
// IME input target (e.g. IME is restarting) in fullscreen windowing mode.
dc.setImeInputTarget(null);
assertFalse(dc.mCurrentFocus.inMultiWindowMode());
assertNull(dc.computeImeControlTarget());
dc.setImeInputTarget(dc.mCurrentFocus);
dc.setImeLayeringTarget(dc.getImeInputTarget().getWindowState()); dc.setImeLayeringTarget(dc.getImeInputTarget().getWindowState());
assertEquals(dc.getImeInputTarget().getWindowState(), dc.computeImeControlTarget()); assertEquals(dc.getImeInputTarget().getWindowState(), dc.computeImeControlTarget());
} }