summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Cosmin Băieș <cosminbaies@google.com> 2023-04-18 17:51:17 +0200
committer Cosmin Băieș <cosminbaies@google.com> 2023-04-25 10:37:01 +0200
commita981617a4cba56b42e804e442e7739afb7c823ff (patch)
tree44a7c93a53ca9529d17015f48aece3ac78489dc2
parent9c6a31ea6583b3c89761c5ac19f28391a8498bac (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.txt1
-rw-r--r--core/java/android/view/ImeFocusController.java8
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java65
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);