diff options
| -rw-r--r-- | core/java/android/view/ImeFocusController.java | 195 | ||||
| -rw-r--r-- | core/java/android/view/inputmethod/InputMethodManager.java | 186 |
2 files changed, 151 insertions, 230 deletions
diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java index d4875d400c5b..1afd048c1623 100644 --- a/core/java/android/view/ImeFocusController.java +++ b/core/java/android/view/ImeFocusController.java @@ -17,8 +17,6 @@ package android.view; import static android.view.ImeFocusControllerProto.HAS_IME_FOCUS; -import static android.view.ImeFocusControllerProto.NEXT_SERVED_VIEW; -import static android.view.ImeFocusControllerProto.SERVED_VIEW; import android.annotation.AnyThread; import android.annotation.NonNull; @@ -31,8 +29,6 @@ import com.android.internal.inputmethod.InputMethodDebug; import com.android.internal.inputmethod.StartInputFlags; import com.android.internal.inputmethod.StartInputReason; -import java.util.Objects; - /** * Responsible for IME focus handling inside {@link ViewRootImpl}. * @hide @@ -43,21 +39,6 @@ public final class ImeFocusController { private final ViewRootImpl mViewRootImpl; private boolean mHasImeFocus = false; - - /** - * This is the view that should currently be served by an input method, - * regardless of the state of setting that up. - * @see InputMethodManagerDelegate#getLockObject() - */ - private View mServedView; - - /** - * This is the next view that will be served by the input method, when - * we get around to updating things. - * @see InputMethodManagerDelegate#getLockObject() - */ - private View mNextServedView; - private InputMethodManagerDelegate mDelegate; @UiThread @@ -134,122 +115,30 @@ public final class ImeFocusController { windowAttribute.softInputMode)); } - boolean forceFocus = false; - final InputMethodManagerDelegate immDelegate = getImmDelegate(); - synchronized (immDelegate.getLockObject()) { - // Update mNextServedView when focusedView changed. - onViewFocusChanged(viewForWindowFocus, true); - - // Starting new input when the next focused view is same as served view but the - // currently active connection (if any) is not associated with it. - final boolean nextFocusIsServedView = mServedView == viewForWindowFocus; - - if (nextFocusIsServedView && !immDelegate.hasActiveConnection(viewForWindowFocus)) { - forceFocus = true; - } - } - - immDelegate.startInputOnWindowFocusGain(viewForWindowFocus, - windowAttribute.softInputMode, windowAttribute.flags, forceFocus); + getImmDelegate().onPostWindowFocus(viewForWindowFocus, windowAttribute); } /** * @see InputMethodManager#checkFocus() */ public boolean checkFocus(boolean forceNewFocus, boolean startInput) { - final InputMethodManagerDelegate immDelegate = getImmDelegate(); - synchronized (immDelegate.getLockObject()) { - if (!immDelegate.isCurrentRootView(mViewRootImpl)) { - return false; - } - if (mServedView == mNextServedView && !forceNewFocus) { - return false; - } - if (DEBUG) { - Log.v(TAG, "checkFocus: view=" + mServedView - + " next=" + mNextServedView - + " force=" + forceNewFocus - + " package=" - + (mServedView != null ? mServedView.getContext().getPackageName() - : "<none>")); - } - // Close the connection when no next served view coming. - if (mNextServedView == null) { - immDelegate.finishInput(); - immDelegate.closeCurrentIme(); - return false; - } - mServedView = mNextServedView; - immDelegate.finishComposingText(); - } - - if (startInput) { - immDelegate.startInput(StartInputReason.CHECK_FOCUS, null /* focusedView */, - 0 /* startInputFlags */, 0 /* softInputMode */, 0 /* windowFlags */); - } - return true; + return getImmDelegate().checkFocus(forceNewFocus, startInput, mViewRootImpl); } @UiThread void onViewFocusChanged(View view, boolean hasFocus) { - if (view == null || view.isTemporarilyDetached()) { - return; - } - final InputMethodManagerDelegate immDelegate = getImmDelegate(); - synchronized (immDelegate.getLockObject()) { - if (!immDelegate.isCurrentRootView(view.getViewRootImpl())) { - return; - } - if (!view.hasImeFocus() || !view.hasWindowFocus()) { - return; - } - if (DEBUG) { - Log.d(TAG, "onViewFocusChanged, view=" + InputMethodDebug.dumpViewInfo(view) - + ", mServedView=" + InputMethodDebug.dumpViewInfo(mServedView)); - } - - // We don't need to track the next served view when the view lost focus here because: - // 1) The current view focus may be cleared temporary when in touch mode, closing input - // at this moment isn't the right way. - // 2) We only care about the served view change when it focused, since changing input - // connection when the focus target changed is reasonable. - // 3) Setting the next served view as null when no more served view should be handled in - // other special events (e.g. view detached from window or the window dismissed). - if (hasFocus) { - mNextServedView = view; - } - } - mViewRootImpl.dispatchCheckFocus(); + getImmDelegate().onViewFocusChanged(view, hasFocus); } @UiThread void onViewDetachedFromWindow(View view) { - final InputMethodManagerDelegate immDelegate = getImmDelegate(); - synchronized (immDelegate.getLockObject()) { - if (!immDelegate.isCurrentRootView(view.getViewRootImpl())) { - return; - } - if (mNextServedView == view) { - mNextServedView = null; - } - if (mServedView == view) { - mViewRootImpl.dispatchCheckFocus(); - } - } + getImmDelegate().onViewDetachedFromWindow(view, mViewRootImpl); + } @UiThread void onWindowDismissed() { - final InputMethodManagerDelegate immDelegate = getImmDelegate(); - synchronized (immDelegate.getLockObject()) { - if (!immDelegate.isCurrentRootView(mViewRootImpl)) { - return; - } - if (mServedView != null) { - immDelegate.finishInput(); - } - immDelegate.setCurrentRootView(null); - } + getImmDelegate().onWindowDismissed(mViewRootImpl); mHasImeFocus = false; } @@ -302,75 +191,21 @@ public final class ImeFocusController { public interface InputMethodManagerDelegate { /** * Starts the input connection. - * Note that this method must not hold the {@link InputMethodManager} lock with - * {@link InputMethodManagerDelegate#getLockObject()} while {@link InputMethodManager} - * calling into app-code in different threads. */ boolean startInput(@StartInputReason int startInputReason, View focusedView, @StartInputFlags int startInputFlags, @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, int windowFlags); - /** - * Starts the input connection when gaining the window focus. - * Note that this method must not hold the {@link InputMethodManager} lock with - * {@link InputMethodManagerDelegate#getLockObject()} while {@link InputMethodManager} - * calling into app-code in different threads. - */ - void startInputOnWindowFocusGain(View rootView, - @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, int windowFlags, - boolean forceNewFocus); - void finishInput(); + + void onPostWindowFocus(View viewForWindowFocus, + @NonNull WindowManager.LayoutParams windowAttribute); + void onViewFocusChanged(@NonNull View view, boolean hasFocus); + boolean checkFocus(boolean forceNewFocus, boolean startInput, ViewRootImpl viewRootImpl); + void onViewDetachedFromWindow(View view, ViewRootImpl viewRootImpl); + void onWindowDismissed(ViewRootImpl viewRootImpl); + void finishInputAndReportToIme(); - void closeCurrentIme(); - void finishComposingText(); void setCurrentRootView(ViewRootImpl rootView); boolean isCurrentRootView(ViewRootImpl rootView); - boolean hasActiveConnection(View view); - - /** - * Returns the {@code InputMethodManager#mH} lock object. - * Used for {@link ImeFocusController} to guard the served view being accessed by - * {@link InputMethodManager} in different threads. - */ - Object getLockObject(); - } - - /** - * Returns The current IME served view for {@link InputMethodManager}. - * Used to start input connection or check the caller's validity when calling - * {@link InputMethodManager} APIs. - * Note that this method requires to be called inside {@code InputMethodManager#mH} lock for - * data consistency. - */ - public View getServedViewLocked() { - return mServedView; - } - - /** - * Returns The next incoming IME served view for {@link InputMethodManager}. - * Note that this method requires to be called inside {@code InputMethodManager#mH} lock for - * data consistency. - */ - public View getNextServedViewLocked() { - return mNextServedView; - } - - /** - * Clears the served & the next served view when the controller triggers - * {@link InputMethodManagerDelegate#finishInput()} or - * {@link InputMethodManagerDelegate#finishInputAndReportToIme()}. - * Note that this method requires to be called inside {@code InputMethodManager#mH} lock for - * data consistency. - * - * @return The {@code mServedView} that has cleared, or {@code null} means nothing to clear. - */ - public View clearServedViewsLocked() { - View clearedView = null; - mNextServedView = null; - if (mServedView != null) { - clearedView = mServedView; - mServedView = null; - } - return clearedView; } /** @@ -384,8 +219,6 @@ public final class ImeFocusController { void dumpDebug(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); proto.write(HAS_IME_FOCUS, mHasImeFocus); - proto.write(SERVED_VIEW, Objects.toString(mServedView)); - proto.write(NEXT_SERVED_VIEW, Objects.toString(mNextServedView)); proto.end(token); } } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index d0405f0e5991..08a7583a80e9 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -94,6 +94,7 @@ import android.view.KeyEvent; import android.view.View; import android.view.ViewRootImpl; import android.view.WindowInsets; +import android.view.WindowManager; import android.view.WindowManager.LayoutParams.SoftInputModeFlags; import android.view.autofill.AutofillId; import android.view.autofill.AutofillManager; @@ -465,6 +466,22 @@ public final class InputMethodManager { // ----------------------------------------------------------- /** + * This is the view that should currently be served by an input method, + * regardless of the state of setting that up. + */ + @Nullable + @GuardedBy("mH") + private View mServedView; + + /** + * This is the next view that will be served by the input method, when + * we get around to updating things. + */ + @Nullable + @GuardedBy("mH") + private View mNextServedView; + + /** * This is the root view of the overall window that currently has input * method focus. */ @@ -699,8 +716,7 @@ public final class InputMethodManager { if (mCurRootView == null) { return null; } - final View servedView = mCurRootView.getImeFocusController().getServedViewLocked(); - return servedView != null ? servedView.getContext() : null; + return mServedView != null ? mServedView.getContext() : null; } } @@ -735,11 +751,7 @@ public final class InputMethodManager { startInputFlags, softInputMode, windowFlags); } - /** - * Used by {@link ImeFocusController} to finish input connection. - */ - @Override - public void finishInput() { + private void finishInput() { ImeTracing.getInstance().triggerClientDump( "InputMethodManager.DelegateImpl#finishInput", InputMethodManager.this, null /* icProto */); @@ -768,19 +780,28 @@ public final class InputMethodManager { } } - /** - * Used by {@link ImeFocusController} to hide current input method editor. - */ @Override - public void closeCurrentIme() { - closeCurrentInput(); + public void onPostWindowFocus(View viewForWindowFocus, + @NonNull WindowManager.LayoutParams windowAttribute) { + boolean forceFocus = false; + synchronized (mH) { + // Update mNextServedView when focusedView changed. + onViewFocusChanged(viewForWindowFocus, true); + + // Starting new input when the next focused view is same as served view but the + // currently active connection (if any) is not associated with it. + final boolean nextFocusIsServedView = mServedView == viewForWindowFocus; + + if (nextFocusIsServedView + && !hasActiveInputConnectionInternal(viewForWindowFocus)) { + forceFocus = true; + } + } + startInputOnWindowFocusGain(viewForWindowFocus, + windowAttribute.softInputMode, windowAttribute.flags, forceFocus); } - /** - * For {@link ImeFocusController} to start input when gaining the window focus. - */ - @Override - public void startInputOnWindowFocusGain(View focusedView, + private void startInputOnWindowFocusGain(View focusedView, @SoftInputModeFlags int softInputMode, int windowFlags, boolean forceNewFocus) { int startInputFlags = getStartInputFlags(focusedView, 0); startInputFlags |= StartInputFlags.WINDOW_GAINED_FOCUS; @@ -816,8 +837,7 @@ public final class InputMethodManager { synchronized (mH) { // For some reason we didn't do a startInput + windowFocusGain, so // we'll just do a window focus gain and call it a day. - View servedView = controller.getServedViewLocked(); - boolean nextFocusHasConnection = servedView != null && servedView == focusedView + boolean nextFocusHasConnection = mServedView != null && mServedView == focusedView && hasActiveInputConnectionInternal(focusedView); if (DEBUG) { Log.v(TAG, "Reporting focus gain, without startInput" @@ -839,16 +859,102 @@ public final class InputMethodManager { } } - /** - * Used by {@link ImeFocusController} to finish current composing text. - */ @Override - public void finishComposingText() { + public void onViewFocusChanged(@Nullable View view, boolean hasFocus) { + if (view == null || view.isTemporarilyDetached()) { + return; + } + final ViewRootImpl viewRootImpl = view.getViewRootImpl(); synchronized (mH) { + if (mCurRootView != viewRootImpl) { + return; + } + if (!view.hasImeFocus() || !view.hasWindowFocus()) { + return; + } + if (DEBUG) { + Log.d(TAG, "onViewFocusChanged, view=" + InputMethodDebug.dumpViewInfo(view)); + } + + // We don't need to track the next served view when the view lost focus here + // because: + // 1) The current view focus may be cleared temporary when in touch mode, closing + // input at this moment isn't the right way. + // 2) We only care about the served view change when it focused, since changing + // input connection when the focus target changed is reasonable. + // 3) Setting the next served view as null when no more served view should be + // handled in other special events (e.g. view detached from window or the window + // dismissed). + if (hasFocus) { + mNextServedView = view; + } + } + viewRootImpl.dispatchCheckFocus(); + } + + @Override + public boolean checkFocus(boolean forceNewFocus, boolean startInput, + ViewRootImpl viewRootImpl) { + synchronized (mH) { + if (mCurRootView != viewRootImpl) { + return false; + } + if (mServedView == mNextServedView && !forceNewFocus) { + return false; + } + if (DEBUG) { + Log.v(TAG, "checkFocus: view=" + mServedView + + " next=" + mNextServedView + + " force=" + forceNewFocus + + " package=" + + (mServedView != null ? mServedView.getContext().getPackageName() + : "<none>")); + } + // Close the connection when no next served view coming. + if (mNextServedView == null) { + finishInput(); + closeCurrentInput(); + return false; + } + mServedView = mNextServedView; if (mServedInputConnection != null) { mServedInputConnection.finishComposingTextFromImm(); } } + + if (startInput) { + startInput(StartInputReason.CHECK_FOCUS, null /* focusedView */, + 0 /* startInputFlags */, 0 /* softInputMode */, 0 /* windowFlags */); + } + return true; + } + + @Override + public void onViewDetachedFromWindow(View view, ViewRootImpl viewRootImpl) { + synchronized (mH) { + if (mCurRootView != view.getViewRootImpl()) { + return; + } + if (mNextServedView == view) { + mNextServedView = null; + } + if (mServedView == view) { + viewRootImpl.dispatchCheckFocus(); + } + } + } + + @Override + public void onWindowDismissed(ViewRootImpl viewRootImpl) { + synchronized (mH) { + if (mCurRootView != viewRootImpl) { + return; + } + if (mServedView != null) { + finishInput(); + } + setCurrentRootView(null); + } } /** @@ -873,26 +979,6 @@ public final class InputMethodManager { return mCurRootView == rootView; } } - - /** - * Checks whether the active input connection (if any) is for the given view. - * - * @see #hasActiveInputConnectionInternal(View)} - */ - @Override - public boolean hasActiveConnection(View view) { - return hasActiveInputConnectionInternal(view); - } - - /** - * Returns the {@link InputMethodManager#mH} lock object. - * Used for {@link ImeFocusController} to guard the served view being accessed by - * {@link InputMethodManager} in different threads. - */ - @Override - public Object getLockObject() { - return mH; - } } /** @hide */ @@ -946,14 +1032,12 @@ public final class InputMethodManager { @GuardedBy("mH") private View getServedViewLocked() { - return mCurRootView != null ? mCurRootView.getImeFocusController().getServedViewLocked() - : null; + return mCurRootView != null ? mServedView : null; } @GuardedBy("mH") private View getNextServedViewLocked() { - return mCurRootView != null ? mCurRootView.getImeFocusController().getNextServedViewLocked() - : null; + return mCurRootView != null ? mNextServedView : null; } private ImeFocusController getFocusController() { @@ -1802,8 +1886,12 @@ public final class InputMethodManager { @GuardedBy("mH") void finishInputLocked() { mVirtualDisplayToScreenMatrix = null; - final ImeFocusController controller = getFocusController(); - final View clearedView = controller != null ? controller.clearServedViewsLocked() : null; + View clearedView = null; + mNextServedView = null; + if (mServedView != null) { + clearedView = mServedView; + mServedView = null; + } if (clearedView != null) { if (DEBUG) { Log.v(TAG, "FINISH INPUT: mServedView=" |