diff options
6 files changed, 128 insertions, 2 deletions
diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java index f16e2439f3f4..e2d215ebfed4 100644 --- a/core/java/android/inputmethodservice/AbstractInputMethodService.java +++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java @@ -33,6 +33,8 @@ import android.view.inputmethod.InputMethod; import android.view.inputmethod.InputMethodSession; import android.window.WindowProviderService; +import com.android.internal.annotations.VisibleForTesting; + import java.io.FileDescriptor; import java.io.PrintWriter; @@ -72,8 +74,9 @@ public abstract class AbstractInputMethodService extends WindowProviderService * {@code null} if {@link #onCreateInputMethodInterface()} is not yet called. * @hide */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) @Nullable - protected final InputMethod getInputMethodInternal() { + public final InputMethod getInputMethodInternal() { return mInputMethod; } diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index ba80811e198c..18d3e5e02fbe 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -149,6 +149,7 @@ import android.window.OnBackInvokedDispatcher; import android.window.WindowMetricsHelper; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback; import com.android.internal.inputmethod.IInputContentUriToken; import com.android.internal.inputmethod.IInputMethod; @@ -3997,6 +3998,16 @@ public class InputMethodService extends AbstractInputMethodService { } /** + * Returns whether the IME navigation bar is currently shown, for testing purposes. + * + * @hide + */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + public final boolean isImeNavigationBarShownForTesting() { + return mNavigationBarController.isShown(); + } + + /** * Used to inject custom {@link InputMethodServiceInternal}. * * @return the {@link InputMethodServiceInternal} to be used. diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java index 8be4c5858694..9c55b0ee0623 100644 --- a/core/java/android/inputmethodservice/NavigationBarController.java +++ b/core/java/android/inputmethodservice/NavigationBarController.java @@ -77,6 +77,10 @@ final class NavigationBarController { default void onNavButtonFlagsChanged(@InputMethodNavButtonFlags int navButtonFlags) { } + default boolean isShown() { + return false; + } + default String toDebugString() { return "No-op implementation"; } @@ -117,6 +121,13 @@ final class NavigationBarController { mImpl.onNavButtonFlagsChanged(navButtonFlags); } + /** + * Returns whether the IME navigation bar is currently shown. + */ + boolean isShown() { + return mImpl.isShown(); + } + String toDebugString() { return mImpl.toDebugString(); } @@ -561,6 +572,12 @@ final class NavigationBarController { } @Override + public boolean isShown() { + return mNavigationBarFrame != null + && mNavigationBarFrame.getVisibility() == View.VISIBLE; + } + + @Override public String toDebugString() { return "{mImeDrawsImeNavBar=" + mImeDrawsImeNavBar + " mNavigationBarFrame=" + mNavigationBarFrame diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java index 1acc384b18da..cabab6c80bf5 100644 --- a/core/java/android/view/InsetsSource.java +++ b/core/java/android/view/InsetsSource.java @@ -49,8 +49,9 @@ public class InsetsSource implements Parcelable { /** The insets source ID of IME */ public static final int ID_IME = createId(null, 0, ime()); + /** The insets source ID of the IME caption bar ("fake" IME navigation bar). */ - static final int ID_IME_CAPTION_BAR = + public static final int ID_IME_CAPTION_BAR = InsetsSource.createId(null /* owner */, 1 /* index */, captionBar()); /** diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java index 9595332afc6c..e1bcd4a0727b 100644 --- a/core/tests/coretests/src/android/view/InsetsSourceTest.java +++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java @@ -16,6 +16,7 @@ package android.view; +import static android.view.InsetsSource.ID_IME_CAPTION_BAR; import static android.view.WindowInsets.Type.FIRST; import static android.view.WindowInsets.Type.LAST; import static android.view.WindowInsets.Type.SIZE; @@ -52,12 +53,15 @@ public class InsetsSourceTest { private final InsetsSource mSource = new InsetsSource(0 /* id */, navigationBars()); private final InsetsSource mImeSource = new InsetsSource(1 /* id */, ime()); + private final InsetsSource mImeCaptionSource = new InsetsSource( + ID_IME_CAPTION_BAR, captionBar()); private final InsetsSource mCaptionSource = new InsetsSource(2 /* id */, captionBar()); @Before public void setUp() { mSource.setVisible(true); mImeSource.setVisible(true); + mImeCaptionSource.setVisible(true); mCaptionSource.setVisible(true); } @@ -110,6 +114,18 @@ public class InsetsSourceTest { } @Test + public void testCalculateInsets_imeCaptionBar() { + mImeCaptionSource.setFrame(new Rect(0, 400, 500, 500)); + Insets insets = mImeCaptionSource.calculateInsets(new Rect(0, 0, 500, 500), false); + assertEquals(Insets.of(0, 0, 0, 100), insets); + + // Place caption bar at top; IME caption bar must always return bottom insets + mImeCaptionSource.setFrame(new Rect(0, 0, 500, 100)); + insets = mImeCaptionSource.calculateInsets(new Rect(0, 0, 500, 500), false); + assertEquals(Insets.of(0, 0, 0, 100), insets); + } + + @Test public void testCalculateInsets_caption_resizing() { mCaptionSource.setFrame(new Rect(0, 0, 100, 100)); Insets insets = mCaptionSource.calculateInsets(new Rect(0, 0, 200, 200), false); diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java index b63a58a96b8c..21342783b79c 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java @@ -25,6 +25,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.junit.Assume.assumeFalse; +import static org.junit.Assume.assumeTrue; import android.app.Instrumentation; import android.content.Context; @@ -48,6 +49,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.apps.inputmethod.simpleime.ims.InputMethodServiceWrapper; import com.android.apps.inputmethod.simpleime.testing.TestActivity; +import com.android.internal.inputmethod.InputMethodNavButtonFlags; import org.junit.After; import org.junit.Before; @@ -635,6 +637,82 @@ public class InputMethodServiceTest { .getRootWindowInsets().getInsetsIgnoringVisibility(captionBar())); } + /** + * This checks that trying to show and hide the navigation bar takes effect + * when the IME does draw the IME navigation bar. + */ + @Test + public void testShowHideImeNavigationBar_doesDrawImeNavBar() throws Exception { + boolean hasNavigationBar = WindowManagerGlobal.getWindowManagerService() + .hasNavigationBar(mInputMethodService.getDisplayId()); + assumeTrue("Must have a navigation bar", hasNavigationBar); + + setShowImeWithHardKeyboard(true /* enabled */); + + // Show IME + verifyInputViewStatusOnMainSync( + () -> { + mInputMethodService.getInputMethodInternal().onNavButtonFlagsChanged( + InputMethodNavButtonFlags.IME_DRAWS_IME_NAV_BAR + | InputMethodNavButtonFlags.SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN + ); + assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(); + }, + true /* expected */, + true /* inputViewStarted */); + assertThat(mInputMethodService.isInputViewShown()).isTrue(); + assertThat(mInputMethodService.isImeNavigationBarShownForTesting()).isTrue(); + + // Try to hide IME nav bar + mInstrumentation.runOnMainSync(() -> mInputMethodService.getWindow().getWindow() + .getInsetsController().hide(captionBar())); + mInstrumentation.waitForIdleSync(); + assertThat(mInputMethodService.isImeNavigationBarShownForTesting()).isFalse(); + + // Try to show IME nav bar + mInstrumentation.runOnMainSync(() -> mInputMethodService.getWindow().getWindow() + .getInsetsController().show(captionBar())); + mInstrumentation.waitForIdleSync(); + assertThat(mInputMethodService.isImeNavigationBarShownForTesting()).isTrue(); + } + /** + * This checks that trying to show and hide the navigation bar has no effect + * when the IME does not draw the IME navigation bar. + * + * Note: The IME navigation bar is *never* visible in 3 button navigation mode. + */ + @Test + public void testShowHideImeNavigationBar_doesNotDrawImeNavBar() throws Exception { + boolean hasNavigationBar = WindowManagerGlobal.getWindowManagerService() + .hasNavigationBar(mInputMethodService.getDisplayId()); + assumeTrue("Must have a navigation bar", hasNavigationBar); + + setShowImeWithHardKeyboard(true /* enabled */); + + // Show IME + verifyInputViewStatusOnMainSync(() -> { + mInputMethodService.getInputMethodInternal().onNavButtonFlagsChanged( + 0 /* navButtonFlags */); + assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(); + }, + true /* expected */, + true /* inputViewStarted */); + assertThat(mInputMethodService.isInputViewShown()).isTrue(); + assertThat(mInputMethodService.isImeNavigationBarShownForTesting()).isFalse(); + + // Try to hide IME nav bar + mInstrumentation.runOnMainSync(() -> mInputMethodService.getWindow().getWindow() + .getInsetsController().hide(captionBar())); + mInstrumentation.waitForIdleSync(); + assertThat(mInputMethodService.isImeNavigationBarShownForTesting()).isFalse(); + + // Try to show IME nav bar + mInstrumentation.runOnMainSync(() -> mInputMethodService.getWindow().getWindow() + .getInsetsController().show(captionBar())); + mInstrumentation.waitForIdleSync(); + assertThat(mInputMethodService.isImeNavigationBarShownForTesting()).isFalse(); + } + private void verifyInputViewStatus( Runnable runnable, boolean expected, boolean inputViewStarted) throws InterruptedException { |