Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4b173140 authored by Yohei Yukawa's avatar Yohei Yukawa
Browse files

Get InputMethodManager in View only if needed

The perf regression found in my initial attempt [1] to instantiate
InputMethodManager (IMM) for each display revieled that when a Window
gained/lost focus,
  getContext().getSystemService(InputMethodManager.class)
gets called for all the View objects that belong to the Window.

This CL introduces a private utility method
  View.notifyFocusChangeToInputMethodManager()
to replace existing unnecessary acquisitions of IMM in View.java,
including the most concerning one View.onWindowFocusChanged().

There should be no negative side-effect in doing this optimization.

LatencyTests results:
  testExpandNotificationsLatency on taimen-userdebug
    without this CL:
      results=[43, 46, 58, 47, 52, 59, 55, 59, 58, 46]
      min: 43.0, max:59.0, avg:54.7, median:53.5, std_dev:5.967
    with this CL:
      results=[41, 58, 55, 59, 60, 67, 51, 55, 55, 55]
      min: 41.0, max:67.0, avg:55.6, median:55.0, std_dev:6.344

 [1]: I7242e765426353672823fcc8277f20ac361930d7
      c53d78e9

Bug: 115893206
Test: atest ActivityManagerMultiDisplayTests
Test: atest CtsInputMethodTestCases CtsInputMethodServiceHostTestCases
Test: atest FrameworksCoreTests:android.view.inputmethod.InputMethodManagerTest
Test: No perf regression observed in Bug 117434607
Change-Id: I5c64b23c3f5cb16f7f3fb9cdc2be063083566050
parent 24222202
Loading
Loading
Loading
Loading
+30 −15
Original line number Diff line number Diff line
@@ -7315,17 +7315,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        // Here we check whether we still need the default focus highlight, and switch it on/off.
        switchDefaultFocusHighlight();
        InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
        if (!gainFocus) {
            if (isPressed()) {
                setPressed(false);
            }
            if (imm != null && mAttachInfo != null && mAttachInfo.mHasWindowFocus) {
                imm.focusOut(this);
            if (mAttachInfo != null && mAttachInfo.mHasWindowFocus) {
                notifyFocusChangeToInputMethodManager(false /* hasFocus */);
            }
            onFocusLost();
        } else if (imm != null && mAttachInfo != null && mAttachInfo.mHasWindowFocus) {
            imm.focusIn(this);
        } else if (mAttachInfo != null && mAttachInfo.mHasWindowFocus) {
            notifyFocusChangeToInputMethodManager(true /* hasFocus */);
        }
        invalidate(true);
@@ -7341,6 +7340,26 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        notifyEnterOrExitForAutoFillIfNeeded(gainFocus);
    }
    /**
     * Notify {@link InputMethodManager} about the focus change of the {@link View}.
     *
     * <p>Does nothing when {@link InputMethodManager} is not available.</p>
     *
     * @param hasFocus {@code true} when the {@link View} is being focused.
     */
    private void notifyFocusChangeToInputMethodManager(boolean hasFocus) {
        final InputMethodManager imm =
                getContext().getSystemService(InputMethodManager.class);
        if (imm == null) {
            return;
        }
        if (hasFocus) {
            imm.focusIn(this);
        } else {
            imm.focusOut(this);
        }
    }
    /** @hide */
    public void notifyEnterOrExitForAutoFillIfNeeded(boolean enter) {
        if (canNotifyAutofillEnterExitEvent()) {
@@ -12485,7 +12504,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        mPrivateFlags3 &= ~PFLAG3_TEMPORARY_DETACH;
        onFinishTemporaryDetach();
        if (hasWindowFocus() && hasFocus()) {
            getContext().getSystemService(InputMethodManager.class).focusIn(this);
            notifyFocusChangeToInputMethodManager(true /* hasFocus */);
        }
        notifyEnterOrExitForAutoFillIfNeeded(true);
    }
@@ -12876,20 +12895,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     *        focus, false otherwise.
     */
    public void onWindowFocusChanged(boolean hasWindowFocus) {
        InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
        if (!hasWindowFocus) {
            if (isPressed()) {
                setPressed(false);
            }
            mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
            if (imm != null && (mPrivateFlags & PFLAG_FOCUSED) != 0) {
                imm.focusOut(this);
            if ((mPrivateFlags & PFLAG_FOCUSED) != 0) {
                notifyFocusChangeToInputMethodManager(false /* hasFocus */);
            }
            removeLongPressCallback();
            removeTapCallback();
            onFocusLost();
        } else if (imm != null && (mPrivateFlags & PFLAG_FOCUSED) != 0) {
            imm.focusIn(this);
        } else if ((mPrivateFlags & PFLAG_FOCUSED) != 0) {
            notifyFocusChangeToInputMethodManager(true /* hasFocus */);
        }
        refreshDrawableState();
@@ -17981,10 +17999,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        rebuildOutline();
        if (isFocused()) {
            InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
            if (imm != null) {
                imm.focusIn(this);
            }
            notifyFocusChangeToInputMethodManager(true /* hasFocus */);
        }
    }