Loading core/java/android/view/ImeInsetsSourceConsumer.java +2 −2 Original line number Original line Diff line number Diff line Loading @@ -51,8 +51,8 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { } } @Override @Override public void onWindowFocusGained() { public void onWindowFocusGained(boolean hasViewFocus) { super.onWindowFocusGained(); super.onWindowFocusGained(hasViewFocus); getImm().registerImeConsumer(this); getImm().registerImeConsumer(this); } } Loading core/java/android/view/InsetsController.java +5 −4 Original line number Original line Diff line number Diff line Loading @@ -1302,8 +1302,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation /** /** * Called when current window gains focus. * Called when current window gains focus. */ */ public void onWindowFocusGained() { public void onWindowFocusGained(boolean hasViewFocused) { getSourceConsumer(ITYPE_IME).onWindowFocusGained(); getSourceConsumer(ITYPE_IME).onWindowFocusGained(hasViewFocused); } } /** /** Loading Loading @@ -1366,8 +1366,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation final InsetsSourceControl imeControl = consumer != null ? consumer.getControl() : null; final InsetsSourceControl imeControl = consumer != null ? consumer.getControl() : null; // Skip showing animation once that made by system for some reason. // Skip showing animation once that made by system for some reason. // (e.g. starting window with IME snapshot) // (e.g. starting window with IME snapshot) if (imeControl != null && show) { if (imeControl != null) { skipAnim = imeControl.getAndClearSkipAnimationOnce(); skipAnim = imeControl.getAndClearSkipAnimationOnce() && show && consumer.hasViewFocusWhenWindowFocusGain(); } } } } applyAnimation(types, show, fromIme, skipAnim); applyAnimation(types, show, fromIme, skipAnim); Loading core/java/android/view/InsetsSourceConsumer.java +11 −1 Original line number Original line Diff line number Diff line Loading @@ -81,6 +81,11 @@ public class InsetsSourceConsumer { private final Supplier<Transaction> mTransactionSupplier; private final Supplier<Transaction> mTransactionSupplier; private @Nullable InsetsSourceControl mSourceControl; private @Nullable InsetsSourceControl mSourceControl; private boolean mHasWindowFocus; private boolean mHasWindowFocus; /** * Whether the view has focus returned by {@link #onWindowFocusGained(boolean)}. */ private boolean mHasViewFocusWhenWindowFocusGain; private Rect mPendingFrame; private Rect mPendingFrame; private Rect mPendingVisibleFrame; private Rect mPendingVisibleFrame; Loading Loading @@ -223,8 +228,9 @@ public class InsetsSourceConsumer { /** /** * Called when current window gains focus * Called when current window gains focus */ */ public void onWindowFocusGained() { public void onWindowFocusGained(boolean hasViewFocus) { mHasWindowFocus = true; mHasWindowFocus = true; mHasViewFocusWhenWindowFocusGain = hasViewFocus; } } /** /** Loading @@ -238,6 +244,10 @@ public class InsetsSourceConsumer { return mHasWindowFocus; return mHasWindowFocus; } } boolean hasViewFocusWhenWindowFocusGain() { return mHasViewFocusWhenWindowFocusGain; } boolean applyLocalVisibilityOverride() { boolean applyLocalVisibilityOverride() { final InsetsSource source = mState.peekSource(mType); final InsetsSource source = mState.peekSource(mType); final boolean isVisible = source != null ? source.isVisible() : getDefaultVisibility(mType); final boolean isVisible = source != null ? source.isVisible() : getDefaultVisibility(mType); Loading core/java/android/view/ViewRootImpl.java +3 −3 Original line number Original line Diff line number Diff line Loading @@ -3344,8 +3344,9 @@ public final class ViewRootImpl implements ViewParent, } } // TODO (b/131181940): Make sure this doesn't leak Activity with mActivityConfigCallback // TODO (b/131181940): Make sure this doesn't leak Activity with mActivityConfigCallback // config changes. // config changes. final View focusedView = mView != null ? mView.findFocus() : null; if (hasWindowFocus) { if (hasWindowFocus) { mInsetsController.onWindowFocusGained(); mInsetsController.onWindowFocusGained(focusedView != null /* hasViewFocused */); } else { } else { mInsetsController.onWindowFocusLost(); mInsetsController.onWindowFocusLost(); } } Loading Loading @@ -3394,8 +3395,7 @@ public final class ViewRootImpl implements ViewParent, // Note: must be done after the focus change callbacks, // Note: must be done after the focus change callbacks, // so all of the view state is set up correctly. // so all of the view state is set up correctly. mImeFocusController.onPostWindowFocus(mView != null ? mView.findFocus() : null, mImeFocusController.onPostWindowFocus(focusedView, hasWindowFocus, mWindowAttributes); hasWindowFocus, mWindowAttributes); if (hasWindowFocus) { if (hasWindowFocus) { // Clear the forward bit. We can just do this directly, since // Clear the forward bit. We can just do this directly, since Loading core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java +46 −12 Original line number Original line Diff line number Diff line Loading @@ -97,7 +97,7 @@ public class ImeInsetsSourceConsumerTest { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { // test if setVisibility can show IME // test if setVisibility can show IME mImeConsumer.onWindowFocusGained(); mImeConsumer.onWindowFocusGained(true); mController.show(WindowInsets.Type.ime(), true /* fromIme */); mController.show(WindowInsets.Type.ime(), true /* fromIme */); mController.cancelExistingAnimations(); mController.cancelExistingAnimations(); assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible()); assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible()); Loading @@ -116,7 +116,7 @@ public class ImeInsetsSourceConsumerTest { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { // Request IME visible before control is available. // Request IME visible before control is available. mImeConsumer.onWindowFocusGained(); mImeConsumer.onWindowFocusGained(true); mController.show(WindowInsets.Type.ime(), true /* fromIme */); mController.show(WindowInsets.Type.ime(), true /* fromIme */); // set control and verify visibility is applied. // set control and verify visibility is applied. Loading @@ -132,24 +132,58 @@ public class ImeInsetsSourceConsumerTest { } } @Test @Test public void testImeGetAndClearSkipAnimationOnce() { public void testImeGetAndClearSkipAnimationOnce_expectSkip() { // Expect IME animation will skipped when the IME is visible at first place. verifyImeGetAndClearSkipAnimationOnce(true /* hasWindowFocus */, true /* hasViewFocus */, true /* expectSkipAnim */); } @Test public void testImeGetAndClearSkipAnimationOnce_expectNoSkip() { // Expect IME animation will not skipped if previously no view focused when gained the // window focus and requesting the IME visible next time. verifyImeGetAndClearSkipAnimationOnce(true /* hasWindowFocus */, false /* hasViewFocus */, false /* expectSkipAnim */); } private void verifyImeGetAndClearSkipAnimationOnce(boolean hasWindowFocus, boolean hasViewFocus, boolean expectSkipAnim) { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { // Request IME visible before control is available. // Request IME visible before control is available. mImeConsumer.onWindowFocusGained(); mImeConsumer.onWindowFocusGained(hasWindowFocus); final boolean imeVisible = hasWindowFocus && hasViewFocus; if (imeVisible) { mController.show(WindowInsets.Type.ime(), true /* fromIme */); mController.show(WindowInsets.Type.ime(), true /* fromIme */); } // set control and verify visibility is applied. // set control and verify visibility is applied. InsetsSourceControl control = Mockito.spy( InsetsSourceControl control = Mockito.spy( new InsetsSourceControl(ITYPE_IME, mLeash, new Point(), Insets.NONE)); new InsetsSourceControl(ITYPE_IME, mLeash, new Point(), Insets.NONE)); // Simulate IME source control set this flag when the target has starting window. // Simulate IME source control set this flag when the target has starting window. control.setSkipAnimationOnce(true); control.setSkipAnimationOnce(true); if (imeVisible) { // Verify IME applyAnimation should be triggered when control becomes available, // and expect skip animation state after getAndClearSkipAnimationOnce invoked. mController.onControlsChanged(new InsetsSourceControl[]{ control }); verify(control).getAndClearSkipAnimationOnce(); verify(mController).applyAnimation(eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(false) /* fromIme */, eq(expectSkipAnim) /* skipAnim */); } // If previously hasViewFocus is false, verify when requesting the IME visible next // time will not skip animation. if (!hasViewFocus) { mController.show(WindowInsets.Type.ime(), true); mController.onControlsChanged(new InsetsSourceControl[]{ control }); mController.onControlsChanged(new InsetsSourceControl[]{ control }); // Verify IME show animation should be triggered when control becomes available and // Verify IME show animation should be triggered when control becomes available and // the animation will be skipped by getAndClearSkipAnimationOnce invoked. // the animation will be skipped by getAndClearSkipAnimationOnce invoked. verify(control).getAndClearSkipAnimationOnce(); verify(control).getAndClearSkipAnimationOnce(); verify(mController).applyAnimation( verify(mController).applyAnimation(eq(WindowInsets.Type.ime()), eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(false) /* fromIme */, eq(true) /* show */, eq(true) /* fromIme */, eq(true) /* skipAnim */); eq(false) /* skipAnim */); } }); }); } } } } Loading
core/java/android/view/ImeInsetsSourceConsumer.java +2 −2 Original line number Original line Diff line number Diff line Loading @@ -51,8 +51,8 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { } } @Override @Override public void onWindowFocusGained() { public void onWindowFocusGained(boolean hasViewFocus) { super.onWindowFocusGained(); super.onWindowFocusGained(hasViewFocus); getImm().registerImeConsumer(this); getImm().registerImeConsumer(this); } } Loading
core/java/android/view/InsetsController.java +5 −4 Original line number Original line Diff line number Diff line Loading @@ -1302,8 +1302,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation /** /** * Called when current window gains focus. * Called when current window gains focus. */ */ public void onWindowFocusGained() { public void onWindowFocusGained(boolean hasViewFocused) { getSourceConsumer(ITYPE_IME).onWindowFocusGained(); getSourceConsumer(ITYPE_IME).onWindowFocusGained(hasViewFocused); } } /** /** Loading Loading @@ -1366,8 +1366,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation final InsetsSourceControl imeControl = consumer != null ? consumer.getControl() : null; final InsetsSourceControl imeControl = consumer != null ? consumer.getControl() : null; // Skip showing animation once that made by system for some reason. // Skip showing animation once that made by system for some reason. // (e.g. starting window with IME snapshot) // (e.g. starting window with IME snapshot) if (imeControl != null && show) { if (imeControl != null) { skipAnim = imeControl.getAndClearSkipAnimationOnce(); skipAnim = imeControl.getAndClearSkipAnimationOnce() && show && consumer.hasViewFocusWhenWindowFocusGain(); } } } } applyAnimation(types, show, fromIme, skipAnim); applyAnimation(types, show, fromIme, skipAnim); Loading
core/java/android/view/InsetsSourceConsumer.java +11 −1 Original line number Original line Diff line number Diff line Loading @@ -81,6 +81,11 @@ public class InsetsSourceConsumer { private final Supplier<Transaction> mTransactionSupplier; private final Supplier<Transaction> mTransactionSupplier; private @Nullable InsetsSourceControl mSourceControl; private @Nullable InsetsSourceControl mSourceControl; private boolean mHasWindowFocus; private boolean mHasWindowFocus; /** * Whether the view has focus returned by {@link #onWindowFocusGained(boolean)}. */ private boolean mHasViewFocusWhenWindowFocusGain; private Rect mPendingFrame; private Rect mPendingFrame; private Rect mPendingVisibleFrame; private Rect mPendingVisibleFrame; Loading Loading @@ -223,8 +228,9 @@ public class InsetsSourceConsumer { /** /** * Called when current window gains focus * Called when current window gains focus */ */ public void onWindowFocusGained() { public void onWindowFocusGained(boolean hasViewFocus) { mHasWindowFocus = true; mHasWindowFocus = true; mHasViewFocusWhenWindowFocusGain = hasViewFocus; } } /** /** Loading @@ -238,6 +244,10 @@ public class InsetsSourceConsumer { return mHasWindowFocus; return mHasWindowFocus; } } boolean hasViewFocusWhenWindowFocusGain() { return mHasViewFocusWhenWindowFocusGain; } boolean applyLocalVisibilityOverride() { boolean applyLocalVisibilityOverride() { final InsetsSource source = mState.peekSource(mType); final InsetsSource source = mState.peekSource(mType); final boolean isVisible = source != null ? source.isVisible() : getDefaultVisibility(mType); final boolean isVisible = source != null ? source.isVisible() : getDefaultVisibility(mType); Loading
core/java/android/view/ViewRootImpl.java +3 −3 Original line number Original line Diff line number Diff line Loading @@ -3344,8 +3344,9 @@ public final class ViewRootImpl implements ViewParent, } } // TODO (b/131181940): Make sure this doesn't leak Activity with mActivityConfigCallback // TODO (b/131181940): Make sure this doesn't leak Activity with mActivityConfigCallback // config changes. // config changes. final View focusedView = mView != null ? mView.findFocus() : null; if (hasWindowFocus) { if (hasWindowFocus) { mInsetsController.onWindowFocusGained(); mInsetsController.onWindowFocusGained(focusedView != null /* hasViewFocused */); } else { } else { mInsetsController.onWindowFocusLost(); mInsetsController.onWindowFocusLost(); } } Loading Loading @@ -3394,8 +3395,7 @@ public final class ViewRootImpl implements ViewParent, // Note: must be done after the focus change callbacks, // Note: must be done after the focus change callbacks, // so all of the view state is set up correctly. // so all of the view state is set up correctly. mImeFocusController.onPostWindowFocus(mView != null ? mView.findFocus() : null, mImeFocusController.onPostWindowFocus(focusedView, hasWindowFocus, mWindowAttributes); hasWindowFocus, mWindowAttributes); if (hasWindowFocus) { if (hasWindowFocus) { // Clear the forward bit. We can just do this directly, since // Clear the forward bit. We can just do this directly, since Loading
core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java +46 −12 Original line number Original line Diff line number Diff line Loading @@ -97,7 +97,7 @@ public class ImeInsetsSourceConsumerTest { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { // test if setVisibility can show IME // test if setVisibility can show IME mImeConsumer.onWindowFocusGained(); mImeConsumer.onWindowFocusGained(true); mController.show(WindowInsets.Type.ime(), true /* fromIme */); mController.show(WindowInsets.Type.ime(), true /* fromIme */); mController.cancelExistingAnimations(); mController.cancelExistingAnimations(); assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible()); assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible()); Loading @@ -116,7 +116,7 @@ public class ImeInsetsSourceConsumerTest { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { // Request IME visible before control is available. // Request IME visible before control is available. mImeConsumer.onWindowFocusGained(); mImeConsumer.onWindowFocusGained(true); mController.show(WindowInsets.Type.ime(), true /* fromIme */); mController.show(WindowInsets.Type.ime(), true /* fromIme */); // set control and verify visibility is applied. // set control and verify visibility is applied. Loading @@ -132,24 +132,58 @@ public class ImeInsetsSourceConsumerTest { } } @Test @Test public void testImeGetAndClearSkipAnimationOnce() { public void testImeGetAndClearSkipAnimationOnce_expectSkip() { // Expect IME animation will skipped when the IME is visible at first place. verifyImeGetAndClearSkipAnimationOnce(true /* hasWindowFocus */, true /* hasViewFocus */, true /* expectSkipAnim */); } @Test public void testImeGetAndClearSkipAnimationOnce_expectNoSkip() { // Expect IME animation will not skipped if previously no view focused when gained the // window focus and requesting the IME visible next time. verifyImeGetAndClearSkipAnimationOnce(true /* hasWindowFocus */, false /* hasViewFocus */, false /* expectSkipAnim */); } private void verifyImeGetAndClearSkipAnimationOnce(boolean hasWindowFocus, boolean hasViewFocus, boolean expectSkipAnim) { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { // Request IME visible before control is available. // Request IME visible before control is available. mImeConsumer.onWindowFocusGained(); mImeConsumer.onWindowFocusGained(hasWindowFocus); final boolean imeVisible = hasWindowFocus && hasViewFocus; if (imeVisible) { mController.show(WindowInsets.Type.ime(), true /* fromIme */); mController.show(WindowInsets.Type.ime(), true /* fromIme */); } // set control and verify visibility is applied. // set control and verify visibility is applied. InsetsSourceControl control = Mockito.spy( InsetsSourceControl control = Mockito.spy( new InsetsSourceControl(ITYPE_IME, mLeash, new Point(), Insets.NONE)); new InsetsSourceControl(ITYPE_IME, mLeash, new Point(), Insets.NONE)); // Simulate IME source control set this flag when the target has starting window. // Simulate IME source control set this flag when the target has starting window. control.setSkipAnimationOnce(true); control.setSkipAnimationOnce(true); if (imeVisible) { // Verify IME applyAnimation should be triggered when control becomes available, // and expect skip animation state after getAndClearSkipAnimationOnce invoked. mController.onControlsChanged(new InsetsSourceControl[]{ control }); verify(control).getAndClearSkipAnimationOnce(); verify(mController).applyAnimation(eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(false) /* fromIme */, eq(expectSkipAnim) /* skipAnim */); } // If previously hasViewFocus is false, verify when requesting the IME visible next // time will not skip animation. if (!hasViewFocus) { mController.show(WindowInsets.Type.ime(), true); mController.onControlsChanged(new InsetsSourceControl[]{ control }); mController.onControlsChanged(new InsetsSourceControl[]{ control }); // Verify IME show animation should be triggered when control becomes available and // Verify IME show animation should be triggered when control becomes available and // the animation will be skipped by getAndClearSkipAnimationOnce invoked. // the animation will be skipped by getAndClearSkipAnimationOnce invoked. verify(control).getAndClearSkipAnimationOnce(); verify(control).getAndClearSkipAnimationOnce(); verify(mController).applyAnimation( verify(mController).applyAnimation(eq(WindowInsets.Type.ime()), eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(false) /* fromIme */, eq(true) /* show */, eq(true) /* fromIme */, eq(true) /* skipAnim */); eq(false) /* skipAnim */); } }); }); } } } }