diff options
| author | 2023-04-18 10:15:00 +0000 | |
|---|---|---|
| committer | 2023-04-18 10:15:00 +0000 | |
| commit | 708988976d1def966de08d8d25d64833924e5f08 (patch) | |
| tree | 92ff54f1450dc9b4492ce1ffc7d3ab018328d48d | |
| parent | 427967322d5a0cabc1e2bf6b7bb9d00c2d4356c7 (diff) | |
| parent | db1c6d933fe3faa60c995f4253320389c7f1e92b (diff) | |
Merge changes from topic "cherrypick-ime-target-policy-to-applier-0l2tbz7bqd" into udc-dev
* changes:
Hide IME when the input target was covered by IME layering overlay
ImeVisibilityStateComputer: show IME snapshot when the screen is off
Add ImeTargetVisibilityPolicy functions for applier
Add ImeTargetChangeListener for IME targets visibility
16 files changed, 564 insertions, 21 deletions
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java index 1b4afd6dd39f..bb8bdf57ac62 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java +++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java @@ -255,6 +255,12 @@ public final class InputMethodDebug { return "HIDE_SOFT_INPUT_IMM_DEPRECATION"; case SoftInputShowHideReason.HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR: return "HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR"; + case SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS: + return "SHOW_IME_SCREENSHOT_FROM_IMMS"; + case SoftInputShowHideReason.REMOVE_IME_SCREENSHOT_FROM_IMMS: + return "REMOVE_IME_SCREENSHOT_FROM_IMMS"; + case SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE: + return "HIDE_WHEN_INPUT_TARGET_INVISIBLE"; default: return "Unknown=" + reason; } diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java index ec9184b72ed1..6e9cd44b7818 100644 --- a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java +++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java @@ -65,7 +65,10 @@ import java.lang.annotation.Retention; SoftInputShowHideReason.HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT, SoftInputShowHideReason.HIDE_SOFT_INPUT_EXTRACT_INPUT_CHANGED, SoftInputShowHideReason.HIDE_SOFT_INPUT_IMM_DEPRECATION, - SoftInputShowHideReason.HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR + SoftInputShowHideReason.HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR, + SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS, + SoftInputShowHideReason.REMOVE_IME_SCREENSHOT_FROM_IMMS, + SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE, }) public @interface SoftInputShowHideReason { /** Show soft input by {@link android.view.inputmethod.InputMethodManager#showSoftInput}. */ @@ -259,4 +262,20 @@ public @interface SoftInputShowHideReason { */ int HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR = ImeProtoEnums.REASON_HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR; + + /** + * Shows ime screenshot by {@link com.android.server.inputmethod.InputMethodManagerService}. + */ + int SHOW_IME_SCREENSHOT_FROM_IMMS = ImeProtoEnums.REASON_SHOW_IME_SCREENSHOT_FROM_IMMS; + + /** + * Removes ime screenshot by {@link com.android.server.inputmethod.InputMethodManagerService}. + */ + int REMOVE_IME_SCREENSHOT_FROM_IMMS = ImeProtoEnums.REASON_REMOVE_IME_SCREENSHOT_FROM_IMMS; + + /** + * Hide soft input when the input target being removed or being obscured by an non-IME + * focusable overlay window. + */ + int HIDE_WHEN_INPUT_TARGET_INVISIBLE = ImeProtoEnums.REASON_HIDE_WHEN_INPUT_TARGET_INVISIBLE; } diff --git a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java index 0ae1e8076b81..a1b67e105dd4 100644 --- a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java +++ b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java @@ -18,14 +18,19 @@ package com.android.server.inputmethod; import static android.view.inputmethod.ImeTracker.DEBUG_IME_VISIBILITY; +import static com.android.internal.inputmethod.SoftInputShowHideReason.REMOVE_IME_SCREENSHOT_FROM_IMMS; +import static com.android.internal.inputmethod.SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS; import static com.android.server.EventLogTags.IMF_HIDE_IME; import static com.android.server.EventLogTags.IMF_SHOW_IME; import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME; import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME_EXPLICIT; import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME_NOT_ALWAYS; +import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_REMOVE_IME_SNAPSHOT; import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SHOW_IME; import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SHOW_IME_IMPLICIT; +import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SHOW_IME_SNAPSHOT; +import android.annotation.NonNull; import android.annotation.Nullable; import android.os.IBinder; import android.os.ResultReceiver; @@ -38,6 +43,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.inputmethod.InputMethodDebug; import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.server.LocalServices; +import com.android.server.wm.ImeTargetVisibilityPolicy; import com.android.server.wm.WindowManagerInternal; import java.util.Objects; @@ -56,10 +62,14 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { private final WindowManagerInternal mWindowManagerInternal; + @NonNull + private final ImeTargetVisibilityPolicy mImeTargetVisibilityPolicy; + DefaultImeVisibilityApplier(InputMethodManagerService service) { mService = service; mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class); + mImeTargetVisibilityPolicy = LocalServices.getService(ImeTargetVisibilityPolicy.class); } @GuardedBy("ImfLock.class") @@ -162,8 +172,37 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { mService.showCurrentInputLocked(windowToken, statsToken, InputMethodManager.SHOW_IMPLICIT, null, reason); break; + case STATE_SHOW_IME_SNAPSHOT: + showImeScreenshot(windowToken, mService.getDisplayIdToShowImeLocked(), null); + break; + case STATE_REMOVE_IME_SNAPSHOT: + removeImeScreenshot(mService.getDisplayIdToShowImeLocked()); + break; default: throw new IllegalArgumentException("Invalid IME visibility state: " + state); } } + + @GuardedBy("ImfLock.class") + @Override + public boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId, + @Nullable ImeTracker.Token statsToken) { + if (mImeTargetVisibilityPolicy.showImeScreenshot(imeTarget, displayId)) { + mService.onShowHideSoftInputRequested(false /* show */, imeTarget, + SHOW_IME_SCREENSHOT_FROM_IMMS, statsToken); + return true; + } + return false; + } + + @GuardedBy("ImfLock.class") + @Override + public boolean removeImeScreenshot(int displayId) { + if (mImeTargetVisibilityPolicy.removeImeScreenshot(displayId)) { + mService.onShowHideSoftInputRequested(false /* show */, mService.mCurFocusedWindow, + REMOVE_IME_SCREENSHOT_FROM_IMMS, null); + return true; + } + return false; + } } diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java index f03e985c07e9..27f6a89a73b3 100644 --- a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java +++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java @@ -16,6 +16,7 @@ package com.android.server.inputmethod; +import android.annotation.NonNull; import android.annotation.Nullable; import android.os.IBinder; import android.os.ResultReceiver; @@ -76,4 +77,27 @@ interface ImeVisibilityApplier { // TODO: add a method in WindowManagerInternal to call DC#updateImeInputAndControlTarget // here to end up updating IME layering after IMMS#attachNewInputLocked called. } + + /** + * Shows the IME screenshot and attach it to the given IME target window. + * + * @param windowToken The token of a window to show the IME screenshot. + * @param displayId The unique id to identify the display + * @param statsToken A token that tracks the progress of an IME request. + * @return {@code true} if success, {@code false} otherwise. + */ + default boolean showImeScreenshot(@NonNull IBinder windowToken, int displayId, + @Nullable ImeTracker.Token statsToken) { + return false; + } + + /** + * Removes the IME screenshot on the given display. + * + * @param displayId The target display of showing IME screenshot. + * @return {@code true} if success, {@code false} otherwise. + */ + default boolean removeImeScreenshot(int displayId) { + return false; + } } diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java index 61fe6545f139..19d6fa00a270 100644 --- a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java +++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java @@ -29,6 +29,8 @@ import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFI import static android.view.WindowManager.LayoutParams.SoftInputModeFlags; import static com.android.internal.inputmethod.InputMethodDebug.softInputModeToString; +import static com.android.internal.inputmethod.SoftInputShowHideReason.REMOVE_IME_SCREENSHOT_FROM_IMMS; +import static com.android.internal.inputmethod.SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS; import static com.android.server.inputmethod.InputMethodManagerService.computeImeDisplayIdForTarget; import android.accessibilityservice.AccessibilityService; @@ -49,6 +51,7 @@ import android.view.inputmethod.InputMethodManager; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.server.LocalServices; +import com.android.server.wm.ImeTargetChangeListener; import com.android.server.wm.WindowManagerInternal; import java.io.PrintWriter; @@ -99,6 +102,18 @@ public final class ImeVisibilityStateComputer { */ private boolean mInputShown; + /** + * Set if we called + * {@link com.android.server.wm.ImeTargetVisibilityPolicy#showImeScreenshot(IBinder, int)}. + */ + private boolean mRequestedImeScreenshot; + + /** The window token of the current visible IME layering target overlay. */ + private IBinder mCurVisibleImeLayeringOverlay; + + /** The window token of the current visible IME input target. */ + private IBinder mCurVisibleImeInputTarget; + /** Represent the invalid IME visibility state */ public static final int STATE_INVALID = -1; @@ -122,6 +137,10 @@ public final class ImeVisibilityStateComputer { public static final int STATE_HIDE_IME_NOT_ALWAYS = 6; public static final int STATE_SHOW_IME_IMPLICIT = 7; + + /** State to handle removing an IME preview surface when necessary. */ + public static final int STATE_REMOVE_IME_SNAPSHOT = 8; + @IntDef({ STATE_INVALID, STATE_HIDE_IME, @@ -132,6 +151,7 @@ public final class ImeVisibilityStateComputer { STATE_HIDE_IME_EXPLICIT, STATE_HIDE_IME_NOT_ALWAYS, STATE_SHOW_IME_IMPLICIT, + STATE_REMOVE_IME_SNAPSHOT, }) @interface VisibilityState {} @@ -172,6 +192,24 @@ public final class ImeVisibilityStateComputer { mWindowManagerInternal = wmService; mImeDisplayValidator = imeDisplayValidator; mPolicy = imePolicy; + mWindowManagerInternal.setInputMethodTargetChangeListener(new ImeTargetChangeListener() { + @Override + public void onImeTargetOverlayVisibilityChanged(IBinder overlayWindowToken, + boolean visible, boolean removed) { + mCurVisibleImeLayeringOverlay = (visible && !removed) ? overlayWindowToken : null; + } + + @Override + public void onImeInputTargetVisibilityChanged(IBinder imeInputTarget, + boolean visibleRequested, boolean removed) { + mCurVisibleImeInputTarget = (visibleRequested && !removed) ? imeInputTarget : null; + if (mCurVisibleImeInputTarget == null && mCurVisibleImeLayeringOverlay != null) { + mService.onApplyImeVisibilityFromComputer(imeInputTarget, + new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT, + SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE)); + } + } + }); } /** @@ -453,6 +491,21 @@ public final class ImeVisibilityStateComputer { return null; } + @VisibleForTesting + ImeVisibilityResult onInteractiveChanged(IBinder windowToken, boolean interactive) { + final ImeTargetWindowState state = getWindowStateOrNull(windowToken); + if (state != null && state.isRequestedImeVisible() && mInputShown && !interactive) { + mRequestedImeScreenshot = true; + return new ImeVisibilityResult(STATE_SHOW_IME_SNAPSHOT, SHOW_IME_SCREENSHOT_FROM_IMMS); + } + if (interactive && mRequestedImeScreenshot) { + mRequestedImeScreenshot = false; + return new ImeVisibilityResult(STATE_REMOVE_IME_SNAPSHOT, + REMOVE_IME_SCREENSHOT_FROM_IMMS); + } + return null; + } + IBinder getWindowTokenFrom(IBinder requestImeToken) { for (IBinder windowToken : mRequestWindowStateMap.keySet()) { final ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 24332112ed76..c70d55510493 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -4847,6 +4847,14 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } } + void onApplyImeVisibilityFromComputer(IBinder windowToken, + @NonNull ImeVisibilityResult result) { + synchronized (ImfLock.class) { + mVisibilityApplier.applyImeVisibility(windowToken, null, result.getState(), + result.getReason()); + } + } + @GuardedBy("ImfLock.class") void setEnabledSessionLocked(SessionState session) { if (mEnabledSession != session) { @@ -5083,6 +5091,14 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub return; } if (mImePlatformCompatUtils.shouldUseSetInteractiveProtocol(getCurMethodUidLocked())) { + // Handle IME visibility when interactive changed before finishing the input to + // ensure we preserve the last state as possible. + final ImeVisibilityResult imeVisRes = mVisibilityStateComputer.onInteractiveChanged( + mCurFocusedWindow, interactive); + if (imeVisRes != null) { + mVisibilityApplier.applyImeVisibility(mCurFocusedWindow, null, + imeVisRes.getState(), imeVisRes.getReason()); + } // Eligible IME processes use new "setInteractive" protocol. mCurClient.mClient.setInteractive(mIsInteractive, mInFullscreenMode); } else { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index c2bc4591ce0d..bad64d357b13 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -656,6 +656,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ private InputTarget mLastImeInputTarget; + + /** + * Tracks the windowToken of the input method input target and the corresponding + * {@link WindowContainerListener} for monitoring changes (e.g. the requested visibility + * change). + */ + private @Nullable Pair<IBinder, WindowContainerListener> mImeTargetTokenListenerPair; + /** * This controls the visibility and animation of the input method window. */ @@ -4267,7 +4275,38 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp @VisibleForTesting void setImeInputTarget(InputTarget target) { + if (mImeTargetTokenListenerPair != null) { + // Unregister the listener before changing to the new IME input target. + final WindowToken oldToken = mTokenMap.get(mImeTargetTokenListenerPair.first); + if (oldToken != null) { + oldToken.unregisterWindowContainerListener(mImeTargetTokenListenerPair.second); + } + mImeTargetTokenListenerPair = null; + } mImeInputTarget = target; + // Notify listeners about IME input target window visibility by the target change. + if (target != null) { + // TODO(b/276743705): Let InputTarget register the visibility change of the hierarchy. + final WindowState targetWin = target.getWindowState(); + if (targetWin != null) { + mImeTargetTokenListenerPair = new Pair<>(targetWin.mToken.token, + new WindowContainerListener() { + @Override + public void onVisibleRequestedChanged(boolean isVisibleRequested) { + // Notify listeners for IME input target window visibility change + // requested by the parent container. + mWmService.dispatchImeInputTargetVisibilityChanged( + targetWin.mClient.asBinder(), isVisibleRequested, + targetWin.mActivityRecord != null + && targetWin.mActivityRecord.finishing); + } + }); + targetWin.mToken.registerWindowContainerListener( + mImeTargetTokenListenerPair.second); + mWmService.dispatchImeInputTargetVisibilityChanged(targetWin.mClient.asBinder(), + targetWin.isVisible() /* visible */, false /* removed */); + } + } if (refreshImeSecureFlag(getPendingTransaction())) { mWmService.requestTraversal(); } @@ -4433,6 +4472,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } private void attachImeScreenshotOnTarget(WindowState imeTarget) { + attachImeScreenshotOnTarget(imeTarget, false); + } + + private void attachImeScreenshotOnTarget(WindowState imeTarget, boolean hideImeWindow) { final SurfaceControl.Transaction t = getPendingTransaction(); // Remove the obsoleted IME snapshot first in case the new snapshot happens to // override the current one before the transition finish and the surface never be @@ -4441,6 +4484,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mImeScreenshot = new ImeScreenshot( mWmService.mSurfaceControlFactory.apply(null), imeTarget); mImeScreenshot.attachAndShow(t); + if (mInputMethodWindow != null && hideImeWindow) { + // Hide the IME window when deciding to show IME snapshot on demand. + // InsetsController will make IME visible again before animating it. + mInputMethodWindow.hide(false, false); + } } /** @@ -4458,7 +4506,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ @VisibleForTesting void showImeScreenshot(WindowState imeTarget) { - attachImeScreenshotOnTarget(imeTarget); + attachImeScreenshotOnTarget(imeTarget, true /* hideImeWindow */); } /** diff --git a/services/core/java/com/android/server/wm/ImeTargetChangeListener.java b/services/core/java/com/android/server/wm/ImeTargetChangeListener.java new file mode 100644 index 000000000000..8bc445bc97bb --- /dev/null +++ b/services/core/java/com/android/server/wm/ImeTargetChangeListener.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2023 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.annotation.NonNull; +import android.os.IBinder; + +/** + * Callback the IME targeting window visibility change state for + * {@link com.android.server.inputmethod.InputMethodManagerService} to manage the IME surface + * visibility and z-ordering. + */ +public interface ImeTargetChangeListener { + /** + * Called when a non-IME-focusable overlay window being the IME layering target (e.g. a + * window with {@link android.view.WindowManager.LayoutParams#FLAG_NOT_FOCUSABLE} and + * {@link android.view.WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM} flags) + * has changed its window visibility. + * + * @param overlayWindowToken the window token of the overlay window. + * @param visible the visibility of the overlay window, {@code true} means visible + * and {@code false} otherwise. + * @param removed Whether the IME target overlay window has being removed. + */ + default void onImeTargetOverlayVisibilityChanged(@NonNull IBinder overlayWindowToken, + boolean visible, boolean removed) { + } + + /** + * Called when the visibility of IME input target window has changed. + * + * @param imeInputTarget the window token of the IME input target window. + * @param visible the new window visibility made by {@param imeInputTarget}. visible is + * {@code true} when switching to the new visible IME input target + * window and started input, or the same input target relayout to + * visible from invisible. In contrast, visible is {@code false} when + * closing the input target, or the same input target relayout to + * invisible from visible. + * @param removed Whether the IME input target window has being removed. + */ + default void onImeInputTargetVisibilityChanged(@NonNull IBinder imeInputTarget, boolean visible, + boolean removed) { + } +} diff --git a/services/core/java/com/android/server/wm/ImeTargetVisibilityPolicy.java b/services/core/java/com/android/server/wm/ImeTargetVisibilityPolicy.java index 71dd91785384..1d9f24c4b317 100644 --- a/services/core/java/com/android/server/wm/ImeTargetVisibilityPolicy.java +++ b/services/core/java/com/android/server/wm/ImeTargetVisibilityPolicy.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; +import android.annotation.NonNull; import android.annotation.Nullable; import android.os.IBinder; import android.view.WindowManager; @@ -36,16 +37,15 @@ public abstract class ImeTargetVisibilityPolicy { * @param displayId A unique id to identify the display. * @return {@code true} if success, {@code false} otherwise. */ - public abstract boolean showImeScreenShot(IBinder imeTarget, int displayId); + public abstract boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId); /** - * Updates the IME parent for target window. + * Removes the IME screenshot on the given display. * - * @param imeTarget The target window to update the IME parent. - * @param displayId A unique id to identify the display. + * @param displayId The target display of showing IME screenshot. * @return {@code true} if success, {@code false} otherwise. */ - public abstract boolean updateImeParent(IBinder imeTarget, int displayId); + public abstract boolean removeImeScreenshot(int displayId); /** * Called when {@link DisplayContent#computeImeParent()} to check if it's valid to keep diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 969afe544b18..492252314356 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -848,6 +848,16 @@ public abstract class WindowManagerInternal { } /** + * Sets by the {@link com.android.server.inputmethod.InputMethodManagerService} to monitor + * the visibility change of the IME targeted windows. + * + * @see ImeTargetChangeListener#onImeTargetOverlayVisibilityChanged + * @see ImeTargetChangeListener#onImeInputTargetVisibilityChanged + */ + public abstract void setInputMethodTargetChangeListener( + @NonNull ImeTargetChangeListener listener); + + /** * Moves the {@link WindowToken} {@code binder} to the display specified by {@code displayId}. */ public abstract void moveWindowTokenToDisplay(IBinder binder, int displayId); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 99d0ea86e2f4..62b3c7cd1daf 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -723,6 +723,9 @@ public class WindowManagerService extends IWindowManager.Stub boolean mHardKeyboardAvailable; WindowManagerInternal.OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener; + + @Nullable ImeTargetChangeListener mImeTargetChangeListener; + SettingsObserver mSettingsObserver; final EmbeddedWindowController mEmbeddedWindowController; final AnrController mAnrController; @@ -1807,6 +1810,10 @@ public class WindowManagerService extends IWindowManager.Stub if (imMayMove) { displayContent.computeImeTarget(true /* updateImeTarget */); + if (win.isImeOverlayLayeringTarget()) { + dispatchImeTargetOverlayVisibilityChanged(client.asBinder(), + win.isVisibleRequestedOrAdding(), false /* removed */); + } } // Don't do layout here, the window must call @@ -2328,6 +2335,8 @@ public class WindowManagerService extends IWindowManager.Stub winAnimator.mSurfaceController.setSecure(win.isSecureLocked()); } + final boolean wasVisible = win.isVisible(); + win.mRelayoutCalled = true; win.mInRelayout = true; @@ -2336,7 +2345,6 @@ public class WindowManagerService extends IWindowManager.Stub "Relayout %s: oldVis=%d newVis=%d. %s", win, oldVisibility, viewVisibility, new RuntimeException().fillInStackTrace()); - win.setDisplayLayoutNeeded(); win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0; @@ -2501,6 +2509,18 @@ public class WindowManagerService extends IWindowManager.Stub } win.mInRelayout = false; + final boolean winVisibleChanged = win.isVisible() != wasVisible; + if (win.isImeOverlayLayeringTarget() && winVisibleChanged) { + dispatchImeTargetOverlayVisibilityChanged(client.asBinder(), + win.isVisible(), false /* removed */); + } + // Notify listeners about IME input target window visibility change. + final boolean isImeInputTarget = win.getDisplayContent().getImeInputTarget() == win; + if (isImeInputTarget && winVisibleChanged) { + dispatchImeInputTargetVisibilityChanged(win.mClient.asBinder(), + win.isVisible() /* visible */, false /* removed */); + } + if (outSyncIdBundle != null) { final int maybeSyncSeqId; if (USE_BLAST_SYNC && win.useBLASTSync() && viewVisibility == View.VISIBLE @@ -3325,6 +3345,30 @@ public class WindowManagerService extends IWindowManager.Stub }); } + void dispatchImeTargetOverlayVisibilityChanged(@NonNull IBinder token, boolean visible, + boolean removed) { + if (mImeTargetChangeListener != null) { + if (DEBUG_INPUT_METHOD) { + Slog.d(TAG, "onImeTargetOverlayVisibilityChanged, win=" + mWindowMap.get(token) + + "visible=" + visible + ", removed=" + removed); + } + mH.post(() -> mImeTargetChangeListener.onImeTargetOverlayVisibilityChanged(token, + visible, removed)); + } + } + + void dispatchImeInputTargetVisibilityChanged(@NonNull IBinder token, boolean visible, + boolean removed) { + if (mImeTargetChangeListener != null) { + if (DEBUG_INPUT_METHOD) { + Slog.d(TAG, "onImeInputTargetVisibilityChanged, win=" + mWindowMap.get(token) + + "visible=" + visible + ", removed=" + removed); + } + mH.post(() -> mImeTargetChangeListener.onImeInputTargetVisibilityChanged(token, + visible, removed)); + } + } + @Override public void setSwitchingUser(boolean switching) { if (!checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, @@ -8262,13 +8306,19 @@ public class WindowManagerService extends IWindowManager.Stub } return null; } + + @Override + public void setInputMethodTargetChangeListener(@NonNull ImeTargetChangeListener listener) { + synchronized (mGlobalLock) { + mImeTargetChangeListener = listener; + } + } } private final class ImeTargetVisibilityPolicyImpl extends ImeTargetVisibilityPolicy { - // TODO(b/258048231): Track IME visibility change in bugreport when invocations. @Override - public boolean showImeScreenShot(@NonNull IBinder imeTarget, int displayId) { + public boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId) { synchronized (mGlobalLock) { final WindowState imeTargetWindow = mWindowMap.get(imeTarget); if (imeTargetWindow == null) { @@ -8284,24 +8334,18 @@ public class WindowManagerService extends IWindowManager.Stub return true; } } - - // TODO(b/258048231): Track IME visibility change in bugreport when invocations. @Override - public boolean updateImeParent(@NonNull IBinder imeTarget, int displayId) { + public boolean removeImeScreenshot(int displayId) { synchronized (mGlobalLock) { - final WindowState imeTargetWindow = mWindowMap.get(imeTarget); - if (imeTargetWindow == null) { - return false; - } final DisplayContent dc = mRoot.getDisplayContent(displayId); if (dc == null) { - Slog.w(TAG, "Invalid displayId:" + displayId + ", fail to update ime parent"); + Slog.w(TAG, "Invalid displayId:" + displayId + + ", fail to remove ime screenshot"); return false; } - - dc.updateImeParent(); - return true; + dc.removeImeSurfaceImmediately(); } + return true; } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 678d4c863d5d..a29959297dc7 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -354,6 +354,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // overlay window is hidden because the owning app is suspended private boolean mHiddenWhileSuspended; private boolean mAppOpVisibility = true; + boolean mPermanentlyHidden; // the window should never be shown again // This is a non-system overlay window that is currently force hidden. private boolean mForceHideNonSystemOverlayWindow; @@ -2350,6 +2351,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } super.removeImmediately(); + if (isImeOverlayLayeringTarget()) { + mWmService.dispatchImeTargetOverlayVisibilityChanged(mClient.asBinder(), + false /* visible */, true /* removed */); + } final DisplayContent dc = getDisplayContent(); if (isImeLayeringTarget()) { // Remove the attached IME screenshot surface. @@ -2360,6 +2365,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP dc.computeImeTarget(true /* updateImeTarget */); } if (dc.getImeInputTarget() == this && !inRelaunchingActivity()) { + mWmService.dispatchImeInputTargetVisibilityChanged(mClient.asBinder(), + false /* visible */, true /* removed */); dc.updateImeInputAndControlTarget(null); } @@ -5499,6 +5506,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return getDisplayContent().getImeTarget(IME_TARGET_LAYERING) == this; } + /** + * Whether the window is non-focusable IME overlay layering target. + */ + boolean isImeOverlayLayeringTarget() { + return isImeLayeringTarget() + && (mAttrs.flags & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0; + } + WindowState getImeLayeringTarget() { final InsetsControlTarget target = getDisplayContent().getImeTarget(IME_TARGET_LAYERING); return target != null ? target.getWindow() : null; diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java index 7d4f87d73507..a6ada4d77253 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java @@ -38,6 +38,7 @@ import static org.mockito.Mockito.verify; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; +import android.view.Display; import android.view.inputmethod.InputMethodManager; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -144,6 +145,26 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe } } + @Test + public void testShowImeScreenshot() { + synchronized (ImfLock.class) { + mVisibilityApplier.showImeScreenshot(mWindowToken, Display.DEFAULT_DISPLAY, + null /* statsToken */); + } + + verify(mMockImeTargetVisibilityPolicy).showImeScreenshot(eq(mWindowToken), + eq(Display.DEFAULT_DISPLAY)); + } + + @Test + public void testRemoveImeScreenshot() { + synchronized (ImfLock.class) { + mVisibilityApplier.removeImeScreenshot(Display.DEFAULT_DISPLAY); + } + + verify(mMockImeTargetVisibilityPolicy).removeImeScreenshot(eq(Display.DEFAULT_DISPLAY)); + } + private InputBindResult startInputOrWindowGainedFocus(IBinder windowToken, int softInputMode) { return mInputMethodManagerService.startInputOrWindowGainedFocus( StartInputReason.WINDOW_FOCUS_GAIN /* startInputReason */, diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java index 2a256f262980..3871e1dfd5b0 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java @@ -24,7 +24,15 @@ import static android.view.WindowManager.DISPLAY_IME_POLICY_HIDE; import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.internal.inputmethod.SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE; +import static com.android.internal.inputmethod.SoftInputShowHideReason.REMOVE_IME_SCREENSHOT_FROM_IMMS; +import static com.android.internal.inputmethod.SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS; import static com.android.server.inputmethod.ImeVisibilityStateComputer.ImeTargetWindowState; +import static com.android.server.inputmethod.ImeVisibilityStateComputer.ImeVisibilityResult; +import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME_EXPLICIT; +import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_REMOVE_IME_SNAPSHOT; +import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SHOW_IME_SNAPSHOT; import static com.android.server.inputmethod.InputMethodManagerService.FALLBACK_DISPLAY_ID; import static com.android.server.inputmethod.InputMethodManagerService.ImeDisplayValidator; @@ -37,11 +45,13 @@ import android.view.inputmethod.InputMethodManager; import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.android.server.wm.ImeTargetChangeListener; import com.android.server.wm.WindowManagerInternal; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; /** * Test the behavior of {@link ImeVisibilityStateComputer} and {@link ImeVisibilityApplier} when @@ -53,6 +63,7 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTestBase { private ImeVisibilityStateComputer mComputer; + private ImeTargetChangeListener mListener; private int mImeDisplayPolicy = DISPLAY_IME_POLICY_LOCAL; @Before @@ -69,7 +80,11 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes return displayId -> mImeDisplayPolicy; } }; + ArgumentCaptor<ImeTargetChangeListener> captor = ArgumentCaptor.forClass( + ImeTargetChangeListener.class); + verify(mMockWindowManagerInternal).setInputMethodTargetChangeListener(captor.capture()); mComputer = new ImeVisibilityStateComputer(mInputMethodManagerService, injector); + mListener = captor.getValue(); } @Test @@ -196,6 +211,53 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes lastState.isRequestedImeVisible()); } + @Test + public void testOnInteractiveChanged() { + mComputer.getOrCreateWindowState(mWindowToken); + // Precondition: ensure IME has shown before hiding request. + mComputer.requestImeVisibility(mWindowToken, true); + mComputer.setInputShown(true); + + // No need any visibility change When initially shows IME on the device was interactive. + ImeVisibilityStateComputer.ImeVisibilityResult result = mComputer.onInteractiveChanged( + mWindowToken, true /* interactive */); + assertThat(result).isNull(); + + // Show the IME screenshot to capture the last IME visible state when the device inactive. + result = mComputer.onInteractiveChanged(mWindowToken, false /* interactive */); + assertThat(result).isNotNull(); + assertThat(result.getState()).isEqualTo(STATE_SHOW_IME_SNAPSHOT); + assertThat(result.getReason()).isEqualTo(SHOW_IME_SCREENSHOT_FROM_IMMS); + + // Remove the IME screenshot when the device became interactive again. + result = mComputer.onInteractiveChanged(mWindowToken, true /* interactive */); + assertThat(result).isNotNull(); + assertThat(result.getState()).isEqualTo(STATE_REMOVE_IME_SNAPSHOT); + assertThat(result.getReason()).isEqualTo(REMOVE_IME_SCREENSHOT_FROM_IMMS); + } + + @Test + public void testOnApplyImeVisibilityFromComputer() { + final IBinder testImeTargetOverlay = new Binder(); + final IBinder testImeInputTarget = new Binder(); + + // Simulate a test IME layering target overlay fully occluded the IME input target. + mListener.onImeTargetOverlayVisibilityChanged(testImeTargetOverlay, true, false); + mListener.onImeInputTargetVisibilityChanged(testImeInputTarget, false, false); + final ArgumentCaptor<IBinder> targetCaptor = ArgumentCaptor.forClass(IBinder.class); + final ArgumentCaptor<ImeVisibilityResult> resultCaptor = ArgumentCaptor.forClass( + ImeVisibilityResult.class); + verify(mInputMethodManagerService).onApplyImeVisibilityFromComputer(targetCaptor.capture(), + resultCaptor.capture()); + final IBinder imeInputTarget = targetCaptor.getValue(); + final ImeVisibilityResult result = resultCaptor.getValue(); + + // Verify the computer will callback hiding IME state to IMMS. + assertThat(imeInputTarget).isEqualTo(testImeInputTarget); + assertThat(result.getState()).isEqualTo(STATE_HIDE_IME_EXPLICIT); + assertThat(result.getReason()).isEqualTo(HIDE_WHEN_INPUT_TARGET_INVISIBLE); + } + private ImeTargetWindowState initImeTargetWindowState(IBinder windowToken) { final ImeTargetWindowState state = new ImeTargetWindowState(SOFT_INPUT_STATE_UNCHANGED, 0, true, true, true); diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java index 90691a75aede..c80ecbf62142 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java @@ -62,6 +62,7 @@ import com.android.server.SystemServerInitThreadPool; import com.android.server.SystemService; import com.android.server.input.InputManagerInternal; import com.android.server.pm.UserManagerInternal; +import com.android.server.wm.ImeTargetVisibilityPolicy; import com.android.server.wm.WindowManagerInternal; import org.junit.After; @@ -113,6 +114,7 @@ public class InputMethodManagerServiceTestBase { @Mock protected IInputMethod mMockInputMethod; @Mock protected IBinder mMockInputMethodBinder; @Mock protected IInputManager mMockIInputManager; + @Mock protected ImeTargetVisibilityPolicy mMockImeTargetVisibilityPolicy; protected Context mContext; protected MockitoSession mMockingSession; @@ -166,6 +168,8 @@ public class InputMethodManagerServiceTestBase { .when(() -> LocalServices.getService(DisplayManagerInternal.class)); doReturn(mMockUserManagerInternal) .when(() -> LocalServices.getService(UserManagerInternal.class)); + doReturn(mMockImeTargetVisibilityPolicy) + .when(() -> LocalServices.getService(ImeTargetVisibilityPolicy.class)); doReturn(mMockIInputMethodManager) .when(() -> ServiceManager.getServiceOrThrow(Context.INPUT_METHOD_SERVICE)); doReturn(mMockIPlatformCompat) @@ -218,6 +222,7 @@ public class InputMethodManagerServiceTestBase { false); mInputMethodManagerService = new InputMethodManagerService(mContext, mServiceThread, mMockInputMethodBindingController); + spyOn(mInputMethodManagerService); // Start a InputMethodManagerService.Lifecycle to publish and manage the lifecycle of // InputMethodManagerService, which is closer to the real situation. diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 766e74c85dab..460a603d51a2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.view.Display.DEFAULT_DISPLAY; import static android.view.InsetsSource.ID_IME; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_270; @@ -85,17 +86,25 @@ import android.content.res.Configuration; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; +import android.os.Bundle; import android.os.IBinder; import android.os.InputConfig; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.util.ArraySet; +import android.util.MergedConfiguration; import android.view.Gravity; +import android.view.IWindow; +import android.view.IWindowSessionCallback; import android.view.InputWindowHandle; import android.view.InsetsSource; +import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.SurfaceControl; +import android.view.View; +import android.view.WindowInsets; import android.view.WindowManager; +import android.window.ClientWindowFrames; import android.window.ITaskFragmentOrganizer; import android.window.TaskFragmentOrganizer; @@ -1280,4 +1289,118 @@ public class WindowStateTests extends WindowTestsBase { assertEquals(new ArraySet(Arrays.asList(expectedArea1, expectedArea2)), new ArraySet(unrestrictedKeepClearAreas)); } + + @Test + public void testImeTargetChangeListener_OnImeInputTargetVisibilityChanged() { + final TestImeTargetChangeListener listener = new TestImeTargetChangeListener(); + mWm.mImeTargetChangeListener = listener; + + final WindowState imeTarget = createWindow(null /* parent */, TYPE_BASE_APPLICATION, + createActivityRecord(mDisplayContent), "imeTarget"); + + imeTarget.mActivityRecord.setVisibleRequested(true); + makeWindowVisible(imeTarget); + mDisplayContent.setImeInputTarget(imeTarget); + waitHandlerIdle(mWm.mH); + + assertThat(listener.mImeTargetToken).isEqualTo(imeTarget.mClient.asBinder()); + assertThat(listener.mIsRemoved).isFalse(); + assertThat(listener.mIsVisibleForImeInputTarget).isTrue(); + + imeTarget.mActivityRecord.setVisibleRequested(false); + waitHandlerIdle(mWm.mH); + + assertThat(listener.mImeTargetToken).isEqualTo(imeTarget.mClient.asBinder()); + assertThat(listener.mIsRemoved).isFalse(); + assertThat(listener.mIsVisibleForImeInputTarget).isFalse(); + + imeTarget.removeImmediately(); + assertThat(listener.mImeTargetToken).isEqualTo(imeTarget.mClient.asBinder()); + assertThat(listener.mIsRemoved).isTrue(); + assertThat(listener.mIsVisibleForImeInputTarget).isFalse(); + } + + @SetupWindows(addWindows = {W_INPUT_METHOD}) + @Test + public void testImeTargetChangeListener_OnImeTargetOverlayVisibilityChanged() { + final TestImeTargetChangeListener listener = new TestImeTargetChangeListener(); + mWm.mImeTargetChangeListener = listener; + + // Scenario 1: test addWindow/relayoutWindow to add Ime layering overlay window as visible. + final WindowToken windowToken = createTestWindowToken(TYPE_APPLICATION_OVERLAY, + mDisplayContent); + final IWindow client = new TestIWindow(); + final Session session = new Session(mWm, new IWindowSessionCallback.Stub() { + @Override + public void onAnimatorScaleChanged(float v) throws RemoteException { + + } + }); + final ClientWindowFrames outFrames = new ClientWindowFrames(); + final MergedConfiguration outConfig = new MergedConfiguration(); + final SurfaceControl outSurfaceControl = new SurfaceControl(); + final InsetsState outInsetsState = new InsetsState(); + final InsetsSourceControl.Array outControls = new InsetsSourceControl.Array(); + final Bundle outBundle = new Bundle(); + final WindowManager.LayoutParams params = new WindowManager.LayoutParams( + TYPE_APPLICATION_OVERLAY); + params.setTitle("imeLayeringTargetOverlay"); + params.token = windowToken.token; + params.flags = FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM; + + mWm.addWindow(session, client, params, View.VISIBLE, DEFAULT_DISPLAY, + 0 /* userUd */, WindowInsets.Type.defaultVisible(), null, new InsetsState(), + new InsetsSourceControl.Array(), new Rect(), new float[1]); + mWm.relayoutWindow(session, client, params, 100, 200, View.VISIBLE, 0, 0, 0, + outFrames, outConfig, outSurfaceControl, outInsetsState, outControls, outBundle); + waitHandlerIdle(mWm.mH); + + final WindowState imeLayeringTargetOverlay = mDisplayContent.getWindow( + w -> w.mClient.asBinder() == client.asBinder()); + assertThat(imeLayeringTargetOverlay.isVisible()).isTrue(); + assertThat(listener.mImeTargetToken).isEqualTo(client.asBinder()); + assertThat(listener.mIsRemoved).isFalse(); + assertThat(listener.mIsVisibleForImeTargetOverlay).isTrue(); + + // Scenario 2: test relayoutWindow to let the Ime layering target overlay window invisible. + mWm.relayoutWindow(session, client, params, 100, 200, View.GONE, 0, 0, 0, + outFrames, outConfig, outSurfaceControl, outInsetsState, outControls, outBundle); + waitHandlerIdle(mWm.mH); + + assertThat(imeLayeringTargetOverlay.isVisible()).isFalse(); + assertThat(listener.mImeTargetToken).isEqualTo(client.asBinder()); + assertThat(listener.mIsRemoved).isFalse(); + assertThat(listener.mIsVisibleForImeTargetOverlay).isFalse(); + + // Scenario 3: test removeWindow to remove the Ime layering target overlay window. + mWm.removeWindow(session, client); + waitHandlerIdle(mWm.mH); + + assertThat(listener.mImeTargetToken).isEqualTo(client.asBinder()); + assertThat(listener.mIsRemoved).isTrue(); + assertThat(listener.mIsVisibleForImeTargetOverlay).isFalse(); + } + + private static class TestImeTargetChangeListener implements ImeTargetChangeListener { + private IBinder mImeTargetToken; + private boolean mIsRemoved; + private boolean mIsVisibleForImeTargetOverlay; + private boolean mIsVisibleForImeInputTarget; + + @Override + public void onImeTargetOverlayVisibilityChanged(IBinder overlayWindowToken, boolean visible, + boolean removed) { + mImeTargetToken = overlayWindowToken; + mIsVisibleForImeTargetOverlay = visible; + mIsRemoved = removed; + } + + @Override + public void onImeInputTargetVisibilityChanged(IBinder imeInputTarget, + boolean visibleRequested, boolean removed) { + mImeTargetToken = imeInputTarget; + mIsVisibleForImeInputTarget = visibleRequested; + mIsRemoved = removed; + } + } } |