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

Commit e16dbabf authored by Yohei Yukawa's avatar Yohei Yukawa Committed by Android (Google) Code Review
Browse files

Merge "Remove InputMethodManagerDelegate#getLockObject()"

parents 9f7a34a4 ff975479
Loading
Loading
Loading
Loading
+14 −181
Original line number Diff line number Diff line
@@ -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);
    }
}
+137 −49
Original line number Diff line number Diff line
@@ -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;
@@ -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.
@@ -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="