From 48bfc3165c4c688ee5a8e78cf6d2b685f1ad032b Mon Sep 17 00:00:00 2001 From: Ming-Shin Lu Date: Mon, 15 Jun 2020 20:06:09 +0800 Subject: [PATCH] Ignore onStartInput when WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR CL[1] introduces new WINDOW_FOCUS_GAIN_REPORT_ONLY flows to notify InputMethodService only reports IME input target to WM when focusing to the next window and its input connection remains. Originally in android Q and prior devices, we don't need such report mechnism but just skip to start new input connection and ignore onStartInput / onFinishInput for the above use case. Since starts from Android R, new IME insets control APIs relying on this mechanism (see CL[2]) to keep the actual IME input target up-to-date. As we expected there should be no new input connection and additional onFinishInput when CL[1] landed. However, in IMMS, startInputUncheckedLocked will be called to callback additional onStartInput for InputMethodService, which mostly is not expected, except when focusing the same window after device turned screen on, we need to start input and callback onStartInput to align with the behavior of android Q or the prior platform. Besides, to have more clear code logic and debugging concept of ignoring onStartInput and onFinishInput only when focused the same editor with input connection remains, we remove WINDOW_FOCUS_GAIN_REPORT_ONLY reason and introduced 2 more start input reasons to distinguish the different behavior: - WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR - WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR [1]: I45a9814d812ad906f417c24200fd4219959e2423 [2]: I9e8984b7e5aa989a53ece9e2576393f795b9ef94 Fix: 158624922 Test: atest FocusHandlingTest InputMethodStartInputLifecycleTest Test: manual as below steps: 1. Use Gboard, Open the emoji keyboard 2. Swipe down to reveal notification shade 3. Swipe up to dismiss notifications 4. Expect the Emoji keyboard is still open without close Change-Id: I2da99ae67b9ce4051dec0c0f0e975ebe6e1ab118 --- .../view/inputmethod/InputMethodManager.java | 8 ++++- .../inputmethod/InputMethodDebug.java | 6 ++-- .../inputmethod/StartInputReason.java | 32 +++++++++++-------- .../InputMethodManagerService.java | 20 +++++++++++- 4 files changed, 49 insertions(+), 17 deletions(-) diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 477dd1d132953..821dd742db9ab 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -19,6 +19,9 @@ package android.view.inputmethod; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; import static android.Manifest.permission.WRITE_SECURE_SETTINGS; +import static com.android.internal.inputmethod.StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR; +import static com.android.internal.inputmethod.StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR; + import android.annotation.DrawableRes; import android.annotation.NonNull; import android.annotation.Nullable; @@ -622,8 +625,11 @@ public final class InputMethodManager { Log.v(TAG, "Reporting focus gain, without startInput" + ", nextFocusIsServedView=" + nextFocusIsServedView); } + final int startInputReason = + nextFocusIsServedView ? WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR + : WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR; mService.startInputOrWindowGainedFocus( - StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient, + startInputReason, mClient, focusedView.getWindowToken(), startInputFlags, softInputMode, windowFlags, nextFocusIsServedView ? mCurrentTextBoxAttribute : null, diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java index a660493f4613b..085cdfcf94623 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java +++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java @@ -46,8 +46,10 @@ public final class InputMethodDebug { return "UNSPECIFIED"; case StartInputReason.WINDOW_FOCUS_GAIN: return "WINDOW_FOCUS_GAIN"; - case StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY: - return "WINDOW_FOCUS_GAIN_REPORT_ONLY"; + case StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR: + return "WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR"; + case StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR: + return "WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR"; case StartInputReason.APP_CALLED_RESTART_INPUT_API: return "APP_CALLED_RESTART_INPUT_API"; case StartInputReason.CHECK_FOCUS: diff --git a/core/java/com/android/internal/inputmethod/StartInputReason.java b/core/java/com/android/internal/inputmethod/StartInputReason.java index a4eaa21538f73..946ce858c12df 100644 --- a/core/java/com/android/internal/inputmethod/StartInputReason.java +++ b/core/java/com/android/internal/inputmethod/StartInputReason.java @@ -30,7 +30,8 @@ import java.lang.annotation.Retention; @IntDef(value = { StartInputReason.UNSPECIFIED, StartInputReason.WINDOW_FOCUS_GAIN, - StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY, + StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR, + StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR, StartInputReason.APP_CALLED_RESTART_INPUT_API, StartInputReason.CHECK_FOCUS, StartInputReason.BOUND_TO_IMMS, @@ -49,45 +50,50 @@ public @interface StartInputReason { */ int WINDOW_FOCUS_GAIN = 1; /** - * {@link android.view.Window} gained focus but there is no {@link android.view.View} that is - * eligible to have IME focus, or the focused view is same as current served view and its - * input connection remains. {@link android.view.inputmethod.InputMethodManager} just reports - * this window focus change event to sync IME input target for system. + * {@link android.view.Window} gained focus and the focused view is same as current served + * view and its input connection remains. {@link android.view.inputmethod.InputMethodManager} + * just reports this window focus change event to sync IME input target for system. */ - int WINDOW_FOCUS_GAIN_REPORT_ONLY = 2; + int WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR = 2; + /** + * {@link android.view.Window} gained focus but there is no {@link android.view.View} that is + * eligible to have IME focus. {@link android.view.inputmethod.InputMethodManager} just reports + * this window focus change event for logging. + */ + int WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR = 3; /** * {@link android.view.inputmethod.InputMethodManager#restartInput(android.view.View)} is * either explicitly called by the application or indirectly called by some Framework class * (e.g. {@link android.widget.EditText}). */ - int APP_CALLED_RESTART_INPUT_API = 3; + int APP_CALLED_RESTART_INPUT_API = 4; /** * {@link android.view.View} requested a new connection because of view focus change. */ - int CHECK_FOCUS = 4; + int CHECK_FOCUS = 5; /** * {@link android.view.inputmethod.InputMethodManager} is responding to * {@link com.android.internal.view.IInputMethodClient#onBindMethod}. */ - int BOUND_TO_IMMS = 5; + int BOUND_TO_IMMS = 6; /** * {@link android.view.inputmethod.InputMethodManager} is responding to * {@link com.android.internal.view.IInputMethodClient#onUnbindMethod}. */ - int UNBOUND_FROM_IMMS = 6; + int UNBOUND_FROM_IMMS = 7; /** * {@link android.view.inputmethod.InputMethodManager} is responding to * {@link com.android.internal.view.IInputMethodClient#setActive}. */ - int ACTIVATED_BY_IMMS = 7; + int ACTIVATED_BY_IMMS = 8; /** * {@link android.view.inputmethod.InputMethodManager} is responding to * {@link com.android.internal.view.IInputMethodClient#setActive}. */ - int DEACTIVATED_BY_IMMS = 8; + int DEACTIVATED_BY_IMMS = 9; /** * {@link com.android.server.inputmethod.InputMethodManagerService} is responding to * {@link com.android.internal.view.IInputSessionCallback#sessionCreated}. */ - int SESSION_CREATED_BY_IME = 9; + int SESSION_CREATED_BY_IME = 10; } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index b647a1ab9873a..fea7980c1c246 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -18,6 +18,8 @@ package com.android.server.inputmethod; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; +import static com.android.internal.inputmethod.StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR; + import static java.lang.annotation.RetentionPolicy.SOURCE; import android.Manifest; @@ -710,6 +712,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub */ int mImeWindowVis; + /** + * Checks if the client needs to start input. + */ + private boolean mCurClientNeedStartInput = false; + private AlertDialog.Builder mDialogBuilder; private AlertDialog mSwitchingDialog; private IBinder mSwitchingDialogToken = new Binder(); @@ -3425,10 +3432,18 @@ public class InputMethodManagerService extends IInputMethodManager.Stub Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client + " attribute=" + attribute + ", token = " + windowToken); } - if (attribute != null) { + // Needs to start input when the same window focus gain but not with the same editor, + // or when the current client needs to start input (e.g. when focusing the same + // window after device turned screen on). + if (attribute != null && (startInputReason != WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR + || mCurClientNeedStartInput)) { + if (mIsInteractive) { + mCurClientNeedStartInput = false; + } return startInputUncheckedLocked(cs, inputContext, missingMethods, attribute, startInputFlags, startInputReason); } + return new InputBindResult( InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY, null, null, null, -1, null); @@ -4381,6 +4396,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private void handleSetInteractive(final boolean interactive) { synchronized (mMethodMap) { mIsInteractive = interactive; + if (!interactive) { + mCurClientNeedStartInput = true; + } updateSystemUiLocked(interactive ? mImeWindowVis : 0, mBackDisposition); // Inform the current client of the change in active status