diff options
| author | 2019-09-26 13:36:40 -0700 | |
|---|---|---|
| committer | 2019-10-07 22:47:16 +0000 | |
| commit | 500a38fce5ba868b1cf54f089b615b7131ad4ff2 (patch) | |
| tree | af186d5c53fc055d768c912df1757069e3eacd37 | |
| parent | 07795adcf59599680147c5cba7d6799fc8c89802 (diff) | |
Postpone IME show animation till IME is drawn in WM.
With this change, we postpone IME show animation until IME layout is fully
drawn in WM.
Earlier, we were showing IME right after InputMethodService called
Window.show(), which didn't guarantee that layout draw was completed.
Fix: 139487540
Bug: 111084606
Test: Manually verified as follows:
1: Kill IME process or restart system to make sure IME window isn't
created.
2. Tap on edit field to make sure IME shows-up first time.
Change-Id: I35f3fdca43e6bae598a08237ba724962d575cb80
6 files changed, 137 insertions, 8 deletions
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index f20003a2ee04..b7fcd3f19334 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 000000000000..7e085f677be9 --- /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 842686441465..cc55e0137e20 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<DisplayFrames, WindowState, Rect> 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 4ebb553318e8..b0410335c5cd 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 abbd1abff614..bb3caa9ed4ed 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -502,6 +502,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 1f45cfb2f53d..3efa05fc9a96 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -7303,6 +7303,29 @@ public class WindowManagerService extends IWindowManager.Stub } @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) { return true; |