1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wm; 18 19 20 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; 21 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.os.IBinder; 25 import android.view.WindowManager; 26 27 /** 28 * A class for {@link com.android.server.inputmethod.InputMethodManagerService} to 29 * control IME visibility operations in {@link WindowManagerService}. 30 */ 31 public abstract class ImeTargetVisibilityPolicy { 32 33 /** 34 * Shows the IME screenshot and attach it to the given IME target window. 35 * 36 * @param imeTarget The target window to show the IME screenshot. 37 * @param displayId A unique id to identify the display. 38 * @return {@code true} if success, {@code false} otherwise. 39 */ showImeScreenshot(@onNull IBinder imeTarget, int displayId)40 public abstract boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId); 41 42 /** 43 * Removes the IME screenshot on the given display. 44 * 45 * @param displayId The target display of showing IME screenshot. 46 * @return {@code true} if success, {@code false} otherwise. 47 */ removeImeScreenshot(int displayId)48 public abstract boolean removeImeScreenshot(int displayId); 49 50 /** 51 * Called when {@link DisplayContent#computeImeParent()} to check if it's valid to keep 52 * computing the ime parent. 53 * 54 * @param imeLayeringTarget The window which IME target to layer on top of it. 55 * @param imeInputTarget The window which start the input connection, receive input from IME. 56 * @return {@code true} to keep computing the ime parent, {@code false} to defer this operation 57 */ canComputeImeParent(@ullable WindowState imeLayeringTarget, @Nullable InputTarget imeInputTarget)58 public static boolean canComputeImeParent(@Nullable WindowState imeLayeringTarget, 59 @Nullable InputTarget imeInputTarget) { 60 if (imeLayeringTarget == null) { 61 return false; 62 } 63 if (shouldComputeImeParentForEmbeddedActivity(imeLayeringTarget, imeInputTarget)) { 64 return true; 65 } 66 // Ensure changing the IME parent when the layering target that may use IME has 67 // became to the input target for preventing IME flickers. 68 // Note that: 69 // 1) For the imeLayeringTarget that may not use IME but requires IME on top 70 // of it (e.g. an overlay window with NOT_FOCUSABLE|ALT_FOCUSABLE_IM flags), we allow 71 // it to re-parent the IME on top the display to keep the legacy behavior. 72 // 2) Even though the starting window won't use IME, the associated activity 73 // behind the starting window may request the input. If so, then we should still hold 74 // the IME parent change until the activity started the input. 75 boolean imeLayeringTargetMayUseIme = 76 WindowManager.LayoutParams.mayUseInputMethod(imeLayeringTarget.mAttrs.flags) 77 || imeLayeringTarget.mAttrs.type == TYPE_APPLICATION_STARTING; 78 // Do not change parent if the window hasn't requested IME. 79 var inputAndLayeringTargetsDisagree = (imeInputTarget == null 80 || imeLayeringTarget.mActivityRecord != imeInputTarget.getActivityRecord()); 81 var inputTargetStale = imeLayeringTargetMayUseIme && inputAndLayeringTargetsDisagree; 82 83 return !inputTargetStale; 84 } 85 86 87 /** 88 * Called from {@link DisplayContent#computeImeParent()} to check the given IME targets if the 89 * IME surface parent should be updated in ActivityEmbeddings. 90 * 91 * As the IME layering target is calculated according to the window hierarchy by 92 * {@link DisplayContent#computeImeTarget}, the layering target and input target may be 93 * different when the window hasn't started input connection, WindowManagerService hasn't yet 94 * received the input target which reported from InputMethodManagerService. To make the IME 95 * surface will be shown on the best fit IME layering target, we basically won't update IME 96 * parent until both IME input and layering target updated for better IME transition. 97 * 98 * However, in activity embedding, tapping a window won't update it to the top window so the 99 * calculated IME layering target may higher than input target. Update IME parent for this case. 100 * 101 * @return {@code true} means the layer of IME layering target is higher than the input target 102 * and {@link DisplayContent#computeImeParent()} should keep progressing to update the IME 103 * surface parent on the display in case the IME surface left behind. 104 */ shouldComputeImeParentForEmbeddedActivity( @ullable WindowState imeLayeringTarget, @Nullable InputTarget imeInputTarget)105 private static boolean shouldComputeImeParentForEmbeddedActivity( 106 @Nullable WindowState imeLayeringTarget, @Nullable InputTarget imeInputTarget) { 107 if (imeInputTarget == null || imeLayeringTarget == null) { 108 return false; 109 } 110 final WindowState inputTargetWindow = imeInputTarget.getWindowState(); 111 if (inputTargetWindow == null || !imeLayeringTarget.isAttached() 112 || !inputTargetWindow.isAttached()) { 113 return false; 114 } 115 116 final ActivityRecord inputTargetRecord = imeInputTarget.getActivityRecord(); 117 final ActivityRecord layeringTargetRecord = imeLayeringTarget.getActivityRecord(); 118 if (inputTargetRecord == null || layeringTargetRecord == null 119 || inputTargetRecord == layeringTargetRecord 120 || (inputTargetRecord.getTask() != layeringTargetRecord.getTask()) 121 || !inputTargetRecord.isEmbedded() || !layeringTargetRecord.isEmbedded()) { 122 // Check whether the input target and layering target are embedded in the same Task. 123 return false; 124 } 125 return imeLayeringTarget.compareTo(inputTargetWindow) > 0; 126 } 127 } 128