diff --git a/core/api/test-current.txt b/core/api/test-current.txt index ae63816945e18..b4caa7ffa4e20 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 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 43828d58afc41..97148969e17f0 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 82cf07355a568..41ef44e1ac1f6 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -482,12 +482,20 @@ 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. + * + *

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; } } @@ -821,6 +830,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(); @@ -1782,6 +1805,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);