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:
@@ -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.
|
||||||
|
if (mController.getAnimationType(control.getType()) == ANIMATION_TYPE_NONE) {
|
||||||
applyRequestedVisibilityToControl();
|
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) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user