diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index f20003a2ee045..b7fcd3f19334f 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -3569,9 +3569,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (!calledWithValidTokenLocked(token)) { return; } - if (mCurClient != null && mCurClient.client != null) { - executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO( - MSG_APPLY_IME_VISIBILITY, setVisible ? 1 : 0, mCurClient)); + if (!setVisible) { + // Client hides the IME directly. + if (mCurClient != null && mCurClient.client != null) { + executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO( + MSG_APPLY_IME_VISIBILITY, setVisible ? 1 : 0, mCurClient)); + } + } else { + // Send to window manager to show IME after IME layout finishes. + mWindowManagerInternal.showImePostLayout(mLastImeTargetWindow); } } } diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java new file mode 100644 index 0000000000000..7e085f677be9b --- /dev/null +++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import android.view.InsetsSource; +import android.view.WindowInsets; + +/** + * Controller for IME inset source on the server. It's called provider as it provides the + * {@link InsetsSource} to the client that uses it in {@link InsetsSourceConsumer}. + */ +class ImeInsetsSourceProvider extends InsetsSourceProvider { + + private WindowState mCurImeTarget; + private Runnable mShowImeRunner; + private boolean mIsImeLayoutDrawn; + + ImeInsetsSourceProvider(InsetsSource source, + InsetsStateController stateController, DisplayContent displayContent) { + super(source, stateController, displayContent); + } + + /** + * Called when a layout pass has occurred. + */ + void onPostLayout() { + super.onPostLayout(); + + if (mCurImeTarget != null + && mCurImeTarget == mDisplayContent.mInputMethodTarget + && mWin != null + && mWin.isDrawnLw() + && !mWin.mGivenInsetsPending) { + mIsImeLayoutDrawn = true; + } + } + + /** + * Called when Insets have been dispatched to client. + */ + void onPostInsetsDispatched() { + if (mIsImeLayoutDrawn && mShowImeRunner != null) { + // Show IME if InputMethodService requested to be shown and it's layout has finished. + mShowImeRunner.run(); + mIsImeLayoutDrawn = false; + mShowImeRunner = null; + } + } + + /** + * Called from {@link WindowManagerInternal#showImePostLayout} when {@link InputMethodService} + * requests to show IME on {@param imeTarget}. + * @param imeTarget imeTarget on which IME is displayed. + */ + void scheduleShowImePostLayout(WindowState imeTarget) { + mCurImeTarget = imeTarget; + mShowImeRunner = () -> { + // Target should still be the same. + if (mCurImeTarget == mDisplayContent.mInputMethodTarget) { + mDisplayContent.mInputMethodTarget.showInsets( + WindowInsets.Type.ime(), true /* fromIme */); + } + mCurImeTarget = null; + }; + } + +} diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 8426864414657..cc55e0137e207 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -47,9 +47,11 @@ import java.io.PrintWriter; */ class InsetsSourceProvider { + protected final DisplayContent mDisplayContent; + protected final @NonNull InsetsSource mSource; + protected WindowState mWin; + private final Rect mTmpRect = new Rect(); - private final @NonNull InsetsSource mSource; - private final DisplayContent mDisplayContent; private final InsetsStateController mStateController; private final InsetsSourceControl mFakeControl; private @Nullable InsetsSourceControl mControl; @@ -57,7 +59,6 @@ class InsetsSourceProvider { private @Nullable InsetsControlTarget mFakeControlTarget; private @Nullable ControlAdapter mAdapter; - private WindowState mWin; private TriConsumer mFrameProvider; /** The visibility override from the current controlling window. */ diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index 4ebb553318e8e..b0410335c5cd4 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -108,8 +108,18 @@ class InsetsStateController { * @return The provider of a specific type. */ InsetsSourceProvider getSourceProvider(@InternalInsetType int type) { - return mProviders.computeIfAbsent(type, - key -> new InsetsSourceProvider(mState.getSource(key), this, mDisplayContent)); + if (type == TYPE_IME) { + return mProviders.computeIfAbsent(type, + key -> new ImeInsetsSourceProvider( + mState.getSource(key), this, mDisplayContent)); + } else { + return mProviders.computeIfAbsent(type, + key -> new InsetsSourceProvider(mState.getSource(key), this, mDisplayContent)); + } + } + + ImeInsetsSourceProvider getImeSourceProvider() { + return (ImeInsetsSourceProvider) getSourceProvider(TYPE_IME); } /** @@ -124,6 +134,7 @@ class InsetsStateController { mLastState.set(mState, true /* copySources */); notifyInsetsChanged(); } + getImeSourceProvider().onPostInsetsDispatched(); } void onInsetsModified(WindowState windowState, InsetsState state) { diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index abbd1abff614a..bb3caa9ed4ed5 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -501,6 +501,13 @@ public abstract class WindowManagerInternal { */ public abstract boolean shouldShowIme(int displayId); + /** + * Show IME on imeTargetWindow once IME has finished layout. + * + * @param imeTargetWindowToken token of the (IME target) window on which IME should be shown. + */ + public abstract void showImePostLayout(IBinder imeTargetWindowToken); + /** * Tell window manager about a package that should not be running with high refresh rate * setting until removeNonHighRefreshRatePackage is called for the same package. diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 6db447d3e7ad3..20812e98bf4a4 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -7302,6 +7302,29 @@ public class WindowManagerService extends IWindowManager.Stub return false; } + @Override + public void showImePostLayout(IBinder imeTargetWindowToken) { + synchronized (mGlobalLock) { + final WindowState imeTarget = mWindowMap.get(imeTargetWindowToken); + if (imeTarget == null) { + return; + } + final DisplayContent displayContent = imeTarget.getDisplayContent(); + if (displayContent == null) { + Slog.w(TAG_WM, "Attempted to show IME on an IME target that does not exist: " + + imeTarget.getName()); + return; + } + if (displayContent.isUntrustedVirtualDisplay()) { + throw new SecurityException("Attempted to show IME on an untrusted " + + "virtual display: " + displayContent.getDisplayId()); + } + + displayContent.getInsetsStateController().getImeSourceProvider() + .scheduleShowImePostLayout(imeTarget); + } + } + @Override public boolean isUidAllowedOnDisplay(int displayId, int uid) { if (displayId == Display.DEFAULT_DISPLAY) {