1 /* 2 * Copyright (C) 2019 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 android.view; 18 19 import static android.os.Trace.TRACE_TAG_VIEW; 20 import static android.view.ImeInsetsSourceConsumerProto.INSETS_SOURCE_CONSUMER; 21 import static android.view.ImeInsetsSourceConsumerProto.IS_REQUESTED_VISIBLE_AWAITING_CONTROL; 22 import static android.view.InsetsController.AnimationType; 23 import static android.view.InsetsState.ITYPE_IME; 24 25 import android.annotation.Nullable; 26 import android.inputmethodservice.InputMethodService; 27 import android.os.IBinder; 28 import android.os.Trace; 29 import android.util.proto.ProtoOutputStream; 30 import android.view.SurfaceControl.Transaction; 31 import android.view.inputmethod.InputMethodManager; 32 33 import java.util.function.Supplier; 34 35 /** 36 * Controls the visibility and animations of IME window insets source. 37 * @hide 38 */ 39 public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { 40 41 /** 42 * Tracks whether we have an outstanding request from the IME to show, but weren't able to 43 * execute it because we didn't have control yet. 44 */ 45 private boolean mIsRequestedVisibleAwaitingControl; 46 ImeInsetsSourceConsumer( InsetsState state, Supplier<Transaction> transactionSupplier, InsetsController controller)47 public ImeInsetsSourceConsumer( 48 InsetsState state, Supplier<Transaction> transactionSupplier, 49 InsetsController controller) { 50 super(ITYPE_IME, state, transactionSupplier, controller); 51 } 52 53 @Override onWindowFocusGained(boolean hasViewFocus)54 public void onWindowFocusGained(boolean hasViewFocus) { 55 super.onWindowFocusGained(hasViewFocus); 56 getImm().registerImeConsumer(this); 57 } 58 59 @Override onWindowFocusLost()60 public void onWindowFocusLost() { 61 super.onWindowFocusLost(); 62 getImm().unregisterImeConsumer(this); 63 mIsRequestedVisibleAwaitingControl = false; 64 } 65 66 @Override hide()67 public void hide() { 68 super.hide(); 69 mIsRequestedVisibleAwaitingControl = false; 70 } 71 72 @Override hide(boolean animationFinished, @AnimationType int animationType)73 void hide(boolean animationFinished, @AnimationType int animationType) { 74 hide(); 75 76 if (animationFinished) { 77 // remove IME surface as IME has finished hide animation. 78 notifyHidden(); 79 removeSurface(); 80 } 81 } 82 83 /** 84 * Request {@link InputMethodManager} to show the IME. 85 * @return @see {@link android.view.InsetsSourceConsumer.ShowResult}. 86 */ 87 @Override requestShow(boolean fromIme)88 public @ShowResult int requestShow(boolean fromIme) { 89 // TODO: ResultReceiver for IME. 90 // TODO: Set mShowOnNextImeRender to automatically show IME and guard it with a flag. 91 if (getControl() == null) { 92 // If control is null, schedule to show IME when control is available. 93 mIsRequestedVisibleAwaitingControl = true; 94 } 95 // If we had a request before to show from IME (tracked with mImeRequestedShow), reaching 96 // this code here means that we now got control, so we can start the animation immediately. 97 // If client window is trying to control IME and IME is already visible, it is immediate. 98 if (fromIme || mState.getSource(getType()).isVisible() && getControl() != null) { 99 return ShowResult.SHOW_IMMEDIATELY; 100 } 101 102 return getImm().requestImeShow(mController.getHost().getWindowToken()) 103 ? ShowResult.IME_SHOW_DELAYED : ShowResult.IME_SHOW_FAILED; 104 } 105 106 /** 107 * Notify {@link InputMethodService} that IME window is hidden. 108 */ 109 @Override notifyHidden()110 void notifyHidden() { 111 getImm().notifyImeHidden(mController.getHost().getWindowToken()); 112 Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.hideRequestFromApi", 0); 113 } 114 115 @Override removeSurface()116 public void removeSurface() { 117 final IBinder window = mController.getHost().getWindowToken(); 118 if (window != null) { 119 getImm().removeImeSurface(window); 120 } 121 } 122 123 @Override setControl(@ullable InsetsSourceControl control, int[] showTypes, int[] hideTypes)124 public void setControl(@Nullable InsetsSourceControl control, int[] showTypes, 125 int[] hideTypes) { 126 super.setControl(control, showTypes, hideTypes); 127 // TODO(b/204524304): clean-up how to deal with the timing issues of hiding IME: 128 // 1) Already requested show IME, in the meantime of WM callback the control but got null 129 // control when relayout comes first 130 // 2) Make sure no regression on some implicit request IME visibility calls (e.g. 131 // toggleSoftInput) 132 if (control == null && !mIsRequestedVisibleAwaitingControl) { 133 hide(); 134 removeSurface(); 135 } 136 if (control != null) { 137 mIsRequestedVisibleAwaitingControl = false; 138 } 139 } 140 141 @Override isRequestedVisibleAwaitingControl()142 protected boolean isRequestedVisibleAwaitingControl() { 143 return mIsRequestedVisibleAwaitingControl || isRequestedVisible(); 144 } 145 146 @Override onPerceptible(boolean perceptible)147 public void onPerceptible(boolean perceptible) { 148 super.onPerceptible(perceptible); 149 final IBinder window = mController.getHost().getWindowToken(); 150 if (window != null) { 151 getImm().reportPerceptible(window, perceptible); 152 } 153 } 154 155 @Override dumpDebug(ProtoOutputStream proto, long fieldId)156 public void dumpDebug(ProtoOutputStream proto, long fieldId) { 157 final long token = proto.start(fieldId); 158 super.dumpDebug(proto, INSETS_SOURCE_CONSUMER); 159 proto.write(IS_REQUESTED_VISIBLE_AWAITING_CONTROL, mIsRequestedVisibleAwaitingControl); 160 proto.end(token); 161 } 162 getImm()163 private InputMethodManager getImm() { 164 return mController.getHost().getInputMethodManager(); 165 } 166 } 167