summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Cosmin Băieș <cosminbaies@google.com> 2025-03-11 19:00:19 +0100
committer Cosmin Băieș <cosminbaies@google.com> 2025-03-19 11:07:40 +0100
commit3a0f01da45e46545d81556c53bfacab4aba9c780 (patch)
treed98fb7c89e1ef543eeba7b84b98a740938fd334d
parent663723d70d38ea7893e066820b44738b29cfea5e (diff)
Check system nav bar for custom IME Switcher vis
This enhances the onCustomImeSwitcherButtonRequestedVisible API to also take into account the system navigation bar, in addition to the IME navigation bar. The system bar is generally automatically shown whenever the IME is shown, but this can be overriden through a config. Moreover, the IME navigation bar can be disabled through a sysprop. This takes into account both the config and the sysprop to provide a more accurate response about the custom button requested visibility. Flag: android.view.inputmethod.ime_switcher_revamp_api Bug: 385018555 Test: atest InputMethodServiceTest#testOnCustomImeSwitcherButtonRequestedVisible_gestureNav InputMethodServiceTest#testOnCustomImeSwitcherButtonRequestedVisible_threeButtonNav InputMethodServiceTest#testOnCustomImeSwitcherButtonRequestedVisible_hideNavBarForKeyboard InputMethodServiceTest#testOnCustomImeSwitcherButtonRequestedVisible_imeNavigationBarDisabled InputMethodServiceTest#testOnCustomImeSwitcherButtonRequestedVisible_imeNavigationBarDisabled_hideNavBarForKeyboard Change-Id: I1746e1f4cceb118140747c7ffb2b4266e3fee31e
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java59
-rw-r--r--core/java/android/inputmethodservice/NavigationBarController.java76
2 files changed, 83 insertions, 52 deletions
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 84d96bd1e155..3d6da5452ad2 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -435,6 +435,11 @@ public class InputMethodService extends AbstractInputMethodService {
}
/**
+ * Cached value of {@link #canImeRenderGesturalNavButtons}, as it doesn't change at runtime.
+ */
+ private final boolean mCanImeRenderGesturalNavButtons = canImeRenderGesturalNavButtons();
+
+ /**
* Allows the system to optimize the back button affordance based on the presence of software
* keyboard.
*
@@ -564,6 +569,9 @@ public class InputMethodService extends AbstractInputMethodService {
private final NavigationBarController mNavigationBarController =
new NavigationBarController(this);
+ /** Whether a custom IME Switcher button was requested to be visible. */
+ private boolean mCustomImeSwitcherButtonRequestedVisible;
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
int mTheme = 0;
@@ -783,7 +791,7 @@ public class InputMethodService extends AbstractInputMethodService {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initializeInternal");
mPrivOps.set(params.privilegedOperations);
InputMethodPrivilegedOperationsRegistry.put(params.token, mPrivOps);
- mNavigationBarController.onNavButtonFlagsChanged(params.navigationBarFlags);
+ onNavButtonFlagsChanged(params.navigationBarFlags);
attachToken(params.token);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@@ -893,7 +901,7 @@ public class InputMethodService extends AbstractInputMethodService {
public final void dispatchStartInput(@Nullable InputConnection inputConnection,
@NonNull IInputMethod.StartInputParams params) {
mPrivOps.reportStartInputAsync(params.startInputToken);
- mNavigationBarController.onNavButtonFlagsChanged(params.navigationBarFlags);
+ onNavButtonFlagsChanged(params.navigationBarFlags);
if (params.restarting) {
restartInput(inputConnection, params.editorInfo);
} else {
@@ -918,6 +926,20 @@ public class InputMethodService extends AbstractInputMethodService {
@Override
public void onNavButtonFlagsChanged(@InputMethodNavButtonFlags int navButtonFlags) {
mNavigationBarController.onNavButtonFlagsChanged(navButtonFlags);
+ if (!mCanImeRenderGesturalNavButtons) {
+ final boolean showImeSwitcher = (navButtonFlags
+ & InputMethodNavButtonFlags.SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN) != 0;
+ // The IME cannot draw the IME nav bar, so this will never be visible. In this case
+ // the system nav bar hosts the IME buttons.
+ // The system nav bar will be hidden when the IME is shown and the config is set.
+ final boolean navBarNotVisible = getApplicationContext().getResources()
+ .getBoolean(com.android.internal.R.bool.config_hideNavBarForKeyboard);
+ final boolean visible = showImeSwitcher && navBarNotVisible;
+ if (visible != mCustomImeSwitcherButtonRequestedVisible) {
+ mCustomImeSwitcherButtonRequestedVisible = visible;
+ onCustomImeSwitcherButtonRequestedVisible(visible);
+ }
+ }
}
/**
@@ -4473,28 +4495,27 @@ public class InputMethodService extends AbstractInputMethodService {
/**
* Called when the requested visibility of a custom IME Switcher button changes.
*
- * <p>When the system provides an IME navigation bar, it may decide to show an IME Switcher
- * button inside this bar. However, the IME can request hiding the bar provided by the system
- * with {@code getWindowInsetsController().hide(captionBar())} (the IME navigation bar provides
- * {@link Type#captionBar() captionBar} insets to the IME window). If the request is successful,
- * then it becomes the IME's responsibility to provide a custom IME Switcher button in its
- * input view, with equivalent functionality.</p>
+ * <p>When this method is called with {@code true} by the system, the IME must show a button
+ * within its UI to switch IMEs. When it is called with {@code false}, it must hide this button.
+ *
+ * <p>Normally, the system provides a button for switching to a different IME when that is
+ * appropriate. Under certain circumstances, namely when the IME successfully asks to hide the
+ * system-provided navigation bar (with {@code getWindowInsetsController().hide(captionBar())}),
+ * providing this button is delegated to the IME through this callback.
*
- * <p>This custom button is only requested to be visible when the system provides the IME
- * navigation bar, both the bar and the IME Switcher button inside it should be visible,
- * but the IME successfully requested to hide the bar. This does not depend on the current
- * visibility of the IME. It could be called with {@code true} while the IME is hidden, in
- * which case the IME should prepare to show the button as soon as the IME itself is shown.</p>
+ * <p>This does not depend on the current visibility of the IME. It could be called with
+ * {@code true} while the IME is hidden, in which case the IME should prepare to show the button
+ * as soon as the IME itself is shown.
*
* <p>This is only called when the requested visibility changes. The default value is
* {@code false} and as such, this will not be called initially if the resulting value is
- * {@code false}.</p>
+ * {@code false}.
*
* <p>This can be called at any time after {@link #onCreate}, even if the IME is not currently
- * visible. However, this is not guaranteed to be called before the IME is shown, as it depends
- * on when the IME requested hiding the IME navigation bar. If the request is sent during
- * the showing flow (e.g. during {@link #onStartInputView}), this will be called shortly after
- * {@link #onWindowShown}, but before the first IME frame is drawn.</p>
+ * visible. However, this is not guaranteed to be called before the IME is shown, as it may
+ * depend on the IME requesting to hide the system-provided navigation bar. If the request is
+ * sent during the showing flow (e.g. during {@link #onStartInputView}), this will be called
+ * shortly after {@link #onWindowShown}, but before the first IME frame is drawn.
*
* @param visible whether the button is requested visible or not.
*/
@@ -4686,6 +4707,8 @@ public class InputMethodService extends AbstractInputMethodService {
+ " touchableRegion=" + mTmpInsets.touchableRegion);
p.println(" mSettingsObserver=" + mSettingsObserver);
p.println(" mNavigationBarController=" + mNavigationBarController.toDebugString());
+ p.println(" mCustomImeSwitcherButtonRequestedVisible="
+ + mCustomImeSwitcherButtonRequestedVisible);
}
private final ImeTracing.ServiceDumper mDumper = new ImeTracing.ServiceDumper() {
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
index 7da053d0010e..f1dee89b0b1d 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -170,6 +170,9 @@ final class NavigationBarController {
private boolean mShouldShowImeSwitcherWhenImeIsShown;
+ /** Whether a custom IME Switcher button should be visible. */
+ private boolean mCustomImeSwitcherButtonRequestedVisible;
+
@Appearance
private int mAppearance;
@@ -181,9 +184,6 @@ final class NavigationBarController {
private boolean mDrawLegacyNavigationBarBackground;
- /** Whether a custom IME Switcher button should be visible. */
- private boolean mCustomImeSwitcherVisible;
-
private final Rect mTempRect = new Rect();
private final int[] mTempPos = new int[2];
@@ -275,7 +275,9 @@ final class NavigationBarController {
// IME navigation bar.
boolean visible = insets.isVisible(captionBar());
mNavigationBarFrame.setVisibility(visible ? View.VISIBLE : View.GONE);
- checkCustomImeSwitcherVisibility();
+ checkCustomImeSwitcherButtonRequestedVisible(
+ mShouldShowImeSwitcherWhenImeIsShown, mImeDrawsImeNavBar,
+ !visible /* imeNavBarNotVisible */);
}
return view.onApplyWindowInsets(insets);
});
@@ -502,33 +504,31 @@ final class NavigationBarController {
mShouldShowImeSwitcherWhenImeIsShown;
mShouldShowImeSwitcherWhenImeIsShown = shouldShowImeSwitcherWhenImeIsShown;
- checkCustomImeSwitcherVisibility();
-
mService.mWindow.getWindow().getDecorView().getWindowInsetsController()
.setImeCaptionBarInsetsHeight(getImeCaptionBarHeight(imeDrawsImeNavBar));
if (imeDrawsImeNavBar) {
installNavigationBarFrameIfNecessary();
- if (mNavigationBarFrame == null) {
- return;
- }
- if (mShouldShowImeSwitcherWhenImeIsShown
- == prevShouldShowImeSwitcherWhenImeIsShown) {
- return;
- }
- final NavigationBarView navigationBarView = mNavigationBarFrame.findViewByPredicate(
- NavigationBarView.class::isInstance);
- if (navigationBarView != null) {
- // TODO(b/213337792): Support InputMethodService#setBackDisposition().
- // TODO(b/213337792): Set NAVBAR_IME_VISIBLE only when necessary.
- final int flags = NAVBAR_BACK_DISMISS_IME | NAVBAR_IME_VISIBLE
- | (mShouldShowImeSwitcherWhenImeIsShown
- ? NAVBAR_IME_SWITCHER_BUTTON_VISIBLE : 0);
- navigationBarView.setNavbarFlags(flags);
+ if (mNavigationBarFrame != null && mShouldShowImeSwitcherWhenImeIsShown
+ != prevShouldShowImeSwitcherWhenImeIsShown) {
+ final NavigationBarView navigationBarView = mNavigationBarFrame
+ .findViewByPredicate(NavigationBarView.class::isInstance);
+ if (navigationBarView != null) {
+ // TODO(b/213337792): Support InputMethodService#setBackDisposition().
+ // TODO(b/213337792): Set NAVBAR_IME_VISIBLE only when necessary.
+ final int flags = NAVBAR_BACK_DISMISS_IME | NAVBAR_IME_VISIBLE
+ | (mShouldShowImeSwitcherWhenImeIsShown
+ ? NAVBAR_IME_SWITCHER_BUTTON_VISIBLE : 0);
+ navigationBarView.setNavbarFlags(flags);
+ }
}
} else {
uninstallNavigationBarFrameIfNecessary();
}
+
+ // Check custom IME Switcher button visibility after (un)installing nav bar frame.
+ checkCustomImeSwitcherButtonRequestedVisible(shouldShowImeSwitcherWhenImeIsShown,
+ imeDrawsImeNavBar, !isShown() /* imeNavBarNotVisible */);
}
@Override
@@ -631,22 +631,29 @@ final class NavigationBarController {
}
/**
- * Checks if a custom IME Switcher button should be visible, and notifies the IME when this
- * state changes. This can only be {@code true} if three conditions are met:
+ * Checks if a custom IME Switcher button should be requested visible, and notifies the IME
+ * when this state changes. This is only {@code true} when the IME Switcher button is
+ * requested visible, and the navigation bar is not requested visible.
*
- * <li>The IME should draw the IME navigation bar.</li>
- * <li>The IME Switcher button should be visible when the IME is visible.</li>
- * <li>The IME navigation bar should be visible, but was requested hidden by the IME.</li>
+ * @param buttonVisible whether the IME Switcher button is requested visible.
+ * @param shouldDrawImeNavBar whether the IME navigation bar should be drawn.
+ * @param imeNavBarNotVisible whether the IME navigation bar is not requested visible. This
+ * will be {@code true} if it is requested hidden or not
+ * installed.
*/
- private void checkCustomImeSwitcherVisibility() {
+ private void checkCustomImeSwitcherButtonRequestedVisible(boolean buttonVisible,
+ boolean shouldDrawImeNavBar, boolean imeNavBarNotVisible) {
if (!Flags.imeSwitcherRevampApi()) {
return;
}
- final boolean visible = mImeDrawsImeNavBar && mShouldShowImeSwitcherWhenImeIsShown
- && mNavigationBarFrame != null && !isShown();
- if (visible != mCustomImeSwitcherVisible) {
- mCustomImeSwitcherVisible = visible;
- mService.onCustomImeSwitcherButtonRequestedVisible(mCustomImeSwitcherVisible);
+ // The system nav bar will be hidden when the IME is shown and the config is set.
+ final boolean navBarNotVisible = shouldDrawImeNavBar ? imeNavBarNotVisible
+ : mService.getResources().getBoolean(
+ com.android.internal.R.bool.config_hideNavBarForKeyboard);
+ final boolean visible = buttonVisible && navBarNotVisible;
+ if (visible != mCustomImeSwitcherButtonRequestedVisible) {
+ mCustomImeSwitcherButtonRequestedVisible = visible;
+ mService.onCustomImeSwitcherButtonRequestedVisible(visible);
}
}
@@ -656,7 +663,8 @@ final class NavigationBarController {
+ " mNavigationBarFrame=" + mNavigationBarFrame
+ " mShouldShowImeSwitcherWhenImeIsShown="
+ mShouldShowImeSwitcherWhenImeIsShown
- + " mCustomImeSwitcherVisible=" + mCustomImeSwitcherVisible
+ + " mCustomImeSwitcherButtonRequestedVisible="
+ + mCustomImeSwitcherButtonRequestedVisible
+ " mAppearance=0x" + Integer.toHexString(mAppearance)
+ " mDarkIntensity=" + mDarkIntensity
+ " mDrawLegacyNavigationBarBackground=" + mDrawLegacyNavigationBarBackground