Remove InputMethodManagerDelegate#getLockObject()
This is a follow up CL to our previous CL [1], which introduced
InputMethodManagerDelegate#getLockObject()
to clarify that "mServedView" and "mNextServedView" need to be guarded
with
InputMethodManager#mH
as a short term workaround.
With this CL, above two fields are moved back to InputMethodManager
like they used to be before we introduce ImeFocusController [2].
This is a mostly mechanical refactoring. Hopefully there should be no
observable behavior change.
[1]: I9c072b829d1db1e68b65e766d764ee71cb16e6a2
31e467846378c12ca51c92cc4cc35e59d495288d
[2]: Ib455704fe1e9d243f93190a84f230210dbceac2a
970d9d2e0c979cf9a0ff0a79ef49044ed1363d4f
Bug: 244504062
Test: presubmit
Change-Id: Ie3446e1d0f62c489195e31fc7f073020884d6cae
diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java
index d4875d4..1afd048 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.StartInputFlags;
import com.android.internal.inputmethod.StartInputReason;
-import java.util.Objects;
-
/**
* Responsible for IME focus handling inside {@link ViewRootImpl}.
* @hide
@@ -43,21 +39,6 @@
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 @@
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 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 @@
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 c1fe686..a2a9209 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -92,6 +92,7 @@
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;
@@ -457,6 +458,22 @@
// -----------------------------------------------------------
/**
+ * 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.
*/
@@ -691,8 +708,7 @@
if (mCurRootView == null) {
return null;
}
- final View servedView = mCurRootView.getImeFocusController().getServedViewLocked();
- return servedView != null ? servedView.getContext() : null;
+ return mServedView != null ? mServedView.getContext() : null;
}
}
@@ -727,11 +743,7 @@
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 */);
@@ -760,19 +772,28 @@
}
}
- /**
- * 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;
@@ -808,8 +829,7 @@
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"
@@ -831,16 +851,102 @@
}
}
- /**
- * 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);
+ }
}
/**
@@ -865,26 +971,6 @@
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 */
@@ -938,14 +1024,12 @@
@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() {
@@ -1794,8 +1878,12 @@
@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="