Loading core/java/android/view/ImeFocusController.java +14 −181 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 Loading @@ -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 Loading Loading @@ -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; } Loading Loading @@ -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; } /** Loading @@ -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); } } core/java/android/view/inputmethod/InputMethodManager.java +137 −49 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -464,6 +465,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. Loading Loading @@ -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; } } Loading Loading @@ -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 */); Loading Loading @@ -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; Loading Loading @@ -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" Loading @@ -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); } } /** Loading @@ -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 */ Loading Loading @@ -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() { Loading Loading @@ -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=" Loading Loading
core/java/android/view/ImeFocusController.java +14 −181 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 Loading @@ -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 Loading Loading @@ -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; } Loading Loading @@ -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; } /** Loading @@ -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); } }
core/java/android/view/inputmethod/InputMethodManager.java +137 −49 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -464,6 +465,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. Loading Loading @@ -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; } } Loading Loading @@ -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 */); Loading Loading @@ -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; Loading Loading @@ -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" Loading @@ -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); } } /** Loading @@ -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 */ Loading Loading @@ -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() { Loading Loading @@ -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=" Loading