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

Commit 6f13d20b authored by lumark's avatar lumark
Browse files

Show IME correctly when top-most display focus changed.

In previous design, the IME focus is changed when receiving
window focus change from ViewRootImpl.

For Multi-Display concept, it should be needed to aware the top
display focus changed in different displays but both are already
had the focused window without change case.

Sending REPORT_FOCUS_CHANGE when top display focus changed for
ViewRootImpl, let IMM can re-focus IME window on right display.

Bug: 117491872
Test: atest ActivityManagerMultiDisplayTests
Test: atest FrameworksServicesTests:DisplayContentTests

Change-Id: Ia46738a5da6dbc334bf937b0f6656a57523c28a7
parent b2117bbc
Loading
Loading
Loading
Loading
+26 −3
Original line number Diff line number Diff line
@@ -58,10 +58,33 @@ oneway interface IWindow {
    void dispatchGetNewSurface();

    /**
     * Tell the window that it is either gaining or losing focus.  Keep it up
     * to date on the current state showing navigational focus (touch mode) too.
     * Tell the window that it is either gaining or losing focus.
     *
     * @param hasFocus       {@code true} if window has focus, {@code false} otherwise.
     * @param inTouchMode    {@code true} if screen is in touch mode, {@code false} otherwise.
     * @param reportToClient {@code true} when need to report to child view with
     *                       {@link View#onWindowFocusChanged(boolean)}, {@code false} otherwise.
     * <p>
     * Note: In the previous design, there is only one window focus state tracked by
     * WindowManagerService.
     * For multi-display, the window focus state is tracked by each display independently.
     * <p>
     * It will introduce a problem if the window was already focused on one display and then
     * switched to another display, since the window focus state on each display is independent,
     * there is no global window focus state in WindowManagerService, so the window focus state of
     * the former display remains unchanged.
     * <p>
     * When switched back to former display, some flows that rely on the global window focus state
     * in view root will be missed due to the window focus state remaining unchanged.
     * (i.e: Showing single IME window when switching between displays.)
     * <p>
     * To solve the problem, WindowManagerService tracks the top focused display change and then
     * callbacks to the client via this method to make sure that the client side will request the
     * IME on the top focused display, and then set {@param reportToClient} as {@code false} to
     * ignore reporting to the application, since its focus remains unchanged on its display.
     *
     */
    void windowFocusChanged(boolean hasFocus, boolean inTouchMode);
    void windowFocusChanged(boolean hasFocus, boolean inTouchMode, boolean reportToClient);
    
    void closeSystemDialogs(String reason);
    
+24 −10
Original line number Diff line number Diff line
@@ -2694,7 +2694,7 @@ public final class ViewRootImpl implements ViewParent,
        }
    }

    private void handleWindowFocusChanged() {
    private void handleWindowFocusChanged(boolean reportToClient) {
        final boolean hasWindowFocus;
        final boolean inTouchMode;
        synchronized (this) {
@@ -2729,8 +2729,9 @@ public final class ViewRootImpl implements ViewParent,
                        } catch (RemoteException ex) {
                        }
                        // Retry in a bit.
                        mHandler.sendMessageDelayed(mHandler.obtainMessage(
                                MSG_WINDOW_FOCUS_CHANGED), 500);
                        final Message msg = mHandler.obtainMessage(MSG_WINDOW_FOCUS_CHANGED);
                        msg.arg1 = reportToClient ? 1 : 0;
                        mHandler.sendMessageDelayed(msg, 500);
                        return;
                    }
                }
@@ -2747,9 +2748,15 @@ public final class ViewRootImpl implements ViewParent,
            }
            if (mView != null) {
                mAttachInfo.mKeyDispatchState.reset();
                // We dispatch onWindowFocusChanged to child view only when window is gaining /
                // losing focus.
                // If the focus is updated from top display change but window focus on the display
                // remains unchanged, will not callback onWindowFocusChanged again since it may
                // be redundant & can affect the state when it callbacks.
                if (reportToClient) {
                    mView.dispatchWindowFocusChanged(hasWindowFocus);
                    mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus);

                }
                if (mAttachInfo.mTooltipHost != null) {
                    mAttachInfo.mTooltipHost.hideTooltip();
                }
@@ -4339,7 +4346,7 @@ public final class ViewRootImpl implements ViewParent,
                    }
                    break;
                case MSG_WINDOW_FOCUS_CHANGED: {
                    handleWindowFocusChanged();
                    handleWindowFocusChanged(msg.arg1 != 0 /* reportToClient */);
                } break;
                case MSG_DIE:
                    doDie();
@@ -7262,7 +7269,7 @@ public final class ViewRootImpl implements ViewParent,
        }

        if (stage != null) {
            handleWindowFocusChanged();
            handleWindowFocusChanged(true /* reportToClient */);
            stage.deliver(q);
        } else {
            finishInputEvent(q);
@@ -7579,6 +7586,11 @@ public final class ViewRootImpl implements ViewParent,
    }

    public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
        windowFocusChanged(hasFocus, inTouchMode, true /* reportToClient */);
    }

    public void windowFocusChanged(boolean hasFocus, boolean inTouchMode,
            boolean reportToClient) {
        synchronized (this) {
            mWindowFocusChanged = true;
            mUpcomingWindowFocus = hasFocus;
@@ -7586,6 +7598,7 @@ public final class ViewRootImpl implements ViewParent,
        }
        Message msg = Message.obtain();
        msg.what = MSG_WINDOW_FOCUS_CHANGED;
        msg.arg1 = reportToClient ? 1 : 0;
        mHandler.sendMessage(msg);
    }

@@ -8130,10 +8143,11 @@ public final class ViewRootImpl implements ViewParent,
        }

        @Override
        public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
        public void windowFocusChanged(boolean hasFocus, boolean inTouchMode,
                boolean reportToClient) {
            final ViewRootImpl viewAncestor = mViewAncestor.get();
            if (viewAncestor != null) {
                viewAncestor.windowFocusChanged(hasFocus, inTouchMode);
                viewAncestor.windowFocusChanged(hasFocus, inTouchMode, reportToClient);
            }
        }

+1 −2
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package com.android.internal.view;

import android.annotation.UnsupportedAppUsage;
import android.graphics.Rect;
import android.hardware.input.InputManager;
import android.os.Bundle;
@@ -66,7 +65,7 @@ public class BaseIWindow extends IWindow.Stub {
    }

    @Override
    public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) {
    public void windowFocusChanged(boolean hasFocus, boolean touchEnabled, boolean reportToClient) {
    }

    @Override
+1 −3
Original line number Diff line number Diff line
@@ -100,7 +100,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.CUSTOM_SCREEN_ROTATION;
import static com.android.server.wm.WindowManagerService.H.REPORT_FOCUS_CHANGE;
import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS;
import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
import static com.android.server.wm.WindowManagerService.H.UPDATE_DOCKED_STACK_DIVIDER;
@@ -157,8 +156,8 @@ import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.SurfaceSession;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants.PointerEventListener;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ToBooleanFunction;
@@ -2787,7 +2786,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
        if (mCurrentFocus == newFocus) {
            return false;
        }
        mService.mH.obtainMessage(REPORT_FOCUS_CHANGE, this).sendToTarget();
        boolean imWindowChanged = false;
        // TODO (b/111080190): Multi-Session IME
        if (!focusFound) {
+29 −5
Original line number Diff line number Diff line
@@ -126,7 +126,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {

    // The ID of the display which is responsible for receiving display-unspecified key and pointer
    // events.
    private int mTopFocusedDisplayId = INVALID_DISPLAY;
    int mTopFocusedDisplayId = INVALID_DISPLAY;

    // Only a seperate transaction until we seperate the apply surface changes
    // transaction from the global transaction.
@@ -160,7 +160,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
    boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
        boolean changed = false;
        int topFocusedDisplayId = INVALID_DISPLAY;
        for (int i = mChildren.size() - 1; i >= 0; i--) {

        for (int i = mChildren.size() - 1; i >= 0; --i) {
            final DisplayContent dc = mChildren.get(i);
            changed |= dc.updateFocusedWindowLocked(mode, updateInputWindows,
                    topFocusedDisplayId != INVALID_DISPLAY /* focusFound */);
@@ -171,12 +172,35 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
        if (topFocusedDisplayId == INVALID_DISPLAY) {
            topFocusedDisplayId = DEFAULT_DISPLAY;
        }
        // TODO(b/118865114): Review if need callback top focus display change to view component.
        // (i.e. Activity or View)
        // Currently we only tracked topFocusedDisplayChanged for notifying InputMethodManager via
        // ViewRootImpl.windowFocusChanged to refocus IME window when top display focus changed
        // but window focus remain the same case.
        // It may need to review if any use case that need to add new callback for reporting
        // this change.
        final boolean topFocusedDisplayChanged =
                mTopFocusedDisplayId != topFocusedDisplayId && mode == UPDATE_FOCUS_NORMAL;
        if (mTopFocusedDisplayId != topFocusedDisplayId) {
            mTopFocusedDisplayId = topFocusedDisplayId;
            mService.mInputManager.setFocusedDisplay(topFocusedDisplayId);
            mService.mInputManager.setFocusedDisplay(mTopFocusedDisplayId);
            if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "New topFocusedDisplayId="
                    + topFocusedDisplayId);
                    + mTopFocusedDisplayId);
        }

        // Report window focus or top display focus changed through REPORT_FOCUS_CHANGE.
        forAllDisplays((dc) -> {
            final boolean windowFocusChanged =
                    dc.mCurrentFocus != null && dc.mCurrentFocus != dc.mLastFocus;
            final boolean isTopFocusedDisplay =
                    topFocusedDisplayChanged && dc.getDisplayId() == mTopFocusedDisplayId;
            if (windowFocusChanged || isTopFocusedDisplay) {
                final Message msg = mService.mH.obtainMessage(
                        WindowManagerService.H.REPORT_FOCUS_CHANGE, dc);
                msg.arg1 = topFocusedDisplayChanged ? 1 : 0;
                mService.mH.sendMessage(msg);
            }
        });
        final WindowState topFocusedWindow = getTopFocusedDisplayContent().mCurrentFocus;
        mService.mInputManager.setFocusedWindow(
                topFocusedWindow != null ? topFocusedWindow.mInputWindowHandle : null);
Loading