diff options
| author | 2023-04-18 17:51:17 +0200 | |
|---|---|---|
| committer | 2023-04-25 10:37:01 +0200 | |
| commit | a981617a4cba56b42e804e442e7739afb7c823ff (patch) | |
| tree | 44a7c93a53ca9529d17015f48aece3ac78489dc2 | |
| parent | 9c6a31ea6583b3c89761c5ac19f28391a8498bac (diff) | |
Clear IMM#mCurRootView when needed
This clears the mCurRootView property from InputMethodManager
when it becomes inactive or loses window focus.
Before, when going from an app that had the IME shown straight
to the launcher home screen, and tapping on the Google search bar,
this would attempt to show the IME but would sometimes fail. This
is because the show flow would progress further than normal, due to
the leftover mCurRootView value in the launcher process.
Clearing this value ensures the erronous flow is cancelled early, and
due to a subsequent retry, can always succeed in showing the IME.
Test: atest android.view.inputmethod.cts.FocusHandlingTest#testClearCurRootViewWhenDifferentProcessBecomesActive
Bug: 276742733
Change-Id: Id08dece0a6b7531da6d6f8c456cd2e1d6489a9be
| -rw-r--r-- | core/api/test-current.txt | 1 | ||||
| -rw-r--r-- | core/java/android/view/ImeFocusController.java | 8 | ||||
| -rw-r--r-- | core/java/android/view/inputmethod/InputMethodManager.java | 65 |
3 files changed, 67 insertions, 7 deletions
diff --git a/core/api/test-current.txt b/core/api/test-current.txt index ae63816945e1..b4caa7ffa4e2 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -3783,6 +3783,7 @@ package android.view.inputmethod { method @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public java.util.List<android.view.inputmethod.InputMethodInfo> getInputMethodListAsUser(int); method public boolean hasActiveInputConnection(@Nullable android.view.View); method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean hasPendingImeVisibilityRequests(); + method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean isCurrentRootView(@NonNull android.view.View); method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean isInputMethodPickerShown(); method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public void setStylusWindowIdleTimeoutForTest(long); field public static final long CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING = 214016041L; // 0xcc1a029L diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java index 43828d58afc4..97148969e17f 100644 --- a/core/java/android/view/ImeFocusController.java +++ b/core/java/android/view/ImeFocusController.java @@ -86,9 +86,12 @@ public final class ImeFocusController { void onPreWindowFocus(boolean hasWindowFocus, WindowManager.LayoutParams windowAttribute) { mHasImeFocus = WindowManager.LayoutParams.mayUseInputMethod(windowAttribute.flags); if (!hasWindowFocus || !mHasImeFocus || isInLocalFocusMode(windowAttribute)) { - return; + if (!hasWindowFocus) { + getImmDelegate().onWindowLostFocus(mViewRootImpl); + } + } else { + getImmDelegate().onPreWindowGainedFocus(mViewRootImpl); } - getImmDelegate().onPreWindowGainedFocus(mViewRootImpl); } @UiThread @@ -163,6 +166,7 @@ public final class ImeFocusController { void onPreWindowGainedFocus(ViewRootImpl viewRootImpl); void onPostWindowGainedFocus(View viewForWindowFocus, @NonNull WindowManager.LayoutParams windowAttribute); + void onWindowLostFocus(@NonNull ViewRootImpl viewRootImpl); void onViewFocusChanged(@NonNull View view, boolean hasFocus); void onScheduledCheckFocus(@NonNull ViewRootImpl viewRootImpl); void onViewDetachedFromWindow(View view, ViewRootImpl viewRootImpl); diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 82cf07355a56..41ef44e1ac1f 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -482,13 +482,21 @@ public final class InputMethodManager { private View mNextServedView; /** - * This is the root view of the overall window that currently has input - * method focus. + * The latest {@link ViewRootImpl} that has, or most recently had, input method focus. + * + * <p>This value will be cleared when it becomes inactive and no longer has window focus. */ + @Nullable @GuardedBy("mH") ViewRootImpl mCurRootView; /** + * Whether the {@link #mCurRootView} currently has window focus. + */ + @GuardedBy("mH") + boolean mCurRootViewWindowFocused; + + /** * This is set when we are in the process of connecting, to determine * when we have actually finished. */ @@ -745,6 +753,7 @@ public final class InputMethodManager { public void onPreWindowGainedFocus(ViewRootImpl viewRootImpl) { synchronized (mH) { setCurrentRootViewLocked(viewRootImpl); + mCurRootViewWindowFocused = true; } } @@ -822,6 +831,17 @@ public final class InputMethodManager { } @Override + public void onWindowLostFocus(@NonNull ViewRootImpl viewRootImpl) { + synchronized (mH) { + if (mCurRootView == viewRootImpl) { + mCurRootViewWindowFocused = false; + + clearCurRootViewIfNeeded(); + } + } + } + + @Override public void onViewFocusChanged(@Nullable View view, boolean hasFocus) { onViewFocusChangedInternal(view, hasFocus); } @@ -1114,6 +1134,10 @@ public final class InputMethodManager { // Note that finishComposingText() is allowed to run // even when we are not active. mFallbackInputConnection.finishComposingTextFromImm(); + + if (clearCurRootViewIfNeeded()) { + return; + } } // Check focus again in case that "onWindowFocus" is called before // handling this message. @@ -1756,8 +1780,7 @@ public final class InputMethodManager { } /** - * Return true if the given view is the currently active view for the - * input method. + * Return {@code true} if the given view is the currently active view for the input method. */ public boolean isActive(View view) { // Re-dispatch if there is a context mismatch. @@ -1773,7 +1796,7 @@ public final class InputMethodManager { } /** - * Return true if any view is currently active in the input method. + * Return {@code true} if any view is currently active for the input method. */ public boolean isActive() { checkFocus(); @@ -1783,6 +1806,20 @@ public final class InputMethodManager { } /** + * Returns {@code true} if the given view's {@link ViewRootImpl} is the currently active one + * for the {@code InputMethodManager}. + * + * @hide + */ + @TestApi + @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD) + public boolean isCurrentRootView(@NonNull View attachedView) { + synchronized (mH) { + return mCurRootView == attachedView.getViewRootImpl(); + } + } + + /** * Return {@code true} if the currently served view is accepting full text edits. * If {@code false}, it has no input connection, so it can only handle raw key events. */ @@ -1908,6 +1945,24 @@ public final class InputMethodManager { mImeDispatcher.clear(); } + /** + * Clears the {@link #mCurRootView} if it's no longer window focused and the connection is + * no longer active. + * + * @return {@code} true iff it was cleared. + */ + @GuardedBy("mH") + private boolean clearCurRootViewIfNeeded() { + if (!mActive && !mCurRootViewWindowFocused) { + finishInputLocked(); + mDelegate.setCurrentRootViewLocked(null); + + return true; + } + + return false; + } + public void displayCompletions(View view, CompletionInfo[] completions) { // Re-dispatch if there is a context mismatch. final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view); |