• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_HIDE_ANIMATION_RUNNING;
22 import static android.view.ImeInsetsSourceConsumerProto.IS_REQUESTED_VISIBLE_AWAITING_CONTROL;
23 import static android.view.ImeInsetsSourceConsumerProto.IS_SHOW_REQUESTED_DURING_HIDE_ANIMATION;
24 import static android.view.InsetsController.AnimationType;
25 import static android.view.InsetsState.ITYPE_IME;
26 
27 import android.annotation.Nullable;
28 import android.os.IBinder;
29 import android.os.Trace;
30 import android.util.proto.ProtoOutputStream;
31 import android.view.SurfaceControl.Transaction;
32 import android.view.inputmethod.InputMethodManager;
33 
34 import java.util.function.Supplier;
35 
36 /**
37  * Controls the visibility and animations of IME window insets source.
38  * @hide
39  */
40 public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
41 
42     /**
43      * Tracks whether we have an outstanding request from the IME to show, but weren't able to
44      * execute it because we didn't have control yet.
45      */
46     private boolean mIsRequestedVisibleAwaitingControl;
47 
48     private boolean mIsHideAnimationRunning;
49 
50     /**
51      * Tracks whether {@link WindowInsetsController#show(int)} or
52      * {@link InputMethodManager#showSoftInput(View, int)} is called during IME hide animation.
53      * If it was called, we should not call {@link InputMethodManager#notifyImeHidden(IBinder)},
54      * because the IME is being shown.
55      */
56     private boolean mIsShowRequestedDuringHideAnimation;
57 
ImeInsetsSourceConsumer( InsetsState state, Supplier<Transaction> transactionSupplier, InsetsController controller)58     public ImeInsetsSourceConsumer(
59             InsetsState state, Supplier<Transaction> transactionSupplier,
60             InsetsController controller) {
61         super(ITYPE_IME, state, transactionSupplier, controller);
62     }
63 
64     @Override
onWindowFocusGained(boolean hasViewFocus)65     public void onWindowFocusGained(boolean hasViewFocus) {
66         super.onWindowFocusGained(hasViewFocus);
67         getImm().registerImeConsumer(this);
68         if (isRequestedVisible() && getControl() == null) {
69             mIsRequestedVisibleAwaitingControl = true;
70         }
71     }
72 
73     @Override
onWindowFocusLost()74     public void onWindowFocusLost() {
75         super.onWindowFocusLost();
76         getImm().unregisterImeConsumer(this);
77         mIsRequestedVisibleAwaitingControl = false;
78     }
79 
80     @Override
show(boolean fromIme)81     public void show(boolean fromIme) {
82         super.show(fromIme);
83         onShowRequested();
84     }
85 
86     @Override
hide()87     public void hide() {
88         super.hide();
89         mIsRequestedVisibleAwaitingControl = false;
90     }
91 
92     @Override
hide(boolean animationFinished, @AnimationType int animationType)93     void hide(boolean animationFinished, @AnimationType int animationType) {
94         hide();
95 
96         if (animationFinished) {
97             // Remove IME surface as IME has finished hide animation, if there is no pending
98             // show request.
99             if (!mIsShowRequestedDuringHideAnimation) {
100                 notifyHidden();
101                 removeSurface();
102             }
103         }
104         // This method is called
105         // (1) before the hide animation starts.
106         // (2) after the hide animation ends.
107         // (3) if the IME is not controllable (animationFinished == true in this case).
108         // We should reset mIsShowRequestedDuringHideAnimation in all cases.
109         mIsHideAnimationRunning = !animationFinished;
110         mIsShowRequestedDuringHideAnimation = false;
111     }
112 
113     /**
114      * Request {@link InputMethodManager} to show the IME.
115      * @return @see {@link android.view.InsetsSourceConsumer.ShowResult}.
116      */
117     @Override
requestShow(boolean fromIme)118     public @ShowResult int requestShow(boolean fromIme) {
119         // TODO: ResultReceiver for IME.
120         // TODO: Set mShowOnNextImeRender to automatically show IME and guard it with a flag.
121         if (getControl() == null) {
122             // If control is null, schedule to show IME when control is available.
123             mIsRequestedVisibleAwaitingControl = true;
124         }
125         // If we had a request before to show from IME (tracked with mImeRequestedShow), reaching
126         // this code here means that we now got control, so we can start the animation immediately.
127         // If client window is trying to control IME and IME is already visible, it is immediate.
128         if (fromIme || mState.getSource(getType()).isVisible() && getControl() != null) {
129             return ShowResult.SHOW_IMMEDIATELY;
130         }
131 
132         return getImm().requestImeShow(mController.getHost().getWindowToken())
133                 ? ShowResult.IME_SHOW_DELAYED : ShowResult.IME_SHOW_FAILED;
134     }
135 
136     /**
137      * Notify {@link com.android.server.inputmethod.InputMethodManagerService} that
138      * IME insets are hidden.
139      */
140     @Override
notifyHidden()141     void notifyHidden() {
142         getImm().notifyImeHidden(mController.getHost().getWindowToken());
143         Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.hideRequestFromApi", 0);
144     }
145 
146     @Override
removeSurface()147     public void removeSurface() {
148         final IBinder window = mController.getHost().getWindowToken();
149         if (window != null) {
150             getImm().removeImeSurface(window);
151         }
152     }
153 
154     @Override
setControl(@ullable InsetsSourceControl control, int[] showTypes, int[] hideTypes)155     public boolean setControl(@Nullable InsetsSourceControl control, int[] showTypes,
156             int[] hideTypes) {
157         if (!super.setControl(control, showTypes, hideTypes)) {
158             return false;
159         }
160         if (control == null && !mIsRequestedVisibleAwaitingControl) {
161             hide();
162             removeSurface();
163         }
164         if (control != null) {
165             mIsRequestedVisibleAwaitingControl = false;
166         }
167         return true;
168     }
169 
170     @Override
isRequestedVisibleAwaitingControl()171     protected boolean isRequestedVisibleAwaitingControl() {
172         return mIsRequestedVisibleAwaitingControl || isRequestedVisible();
173     }
174 
175     @Override
onPerceptible(boolean perceptible)176     public void onPerceptible(boolean perceptible) {
177         super.onPerceptible(perceptible);
178         final IBinder window = mController.getHost().getWindowToken();
179         if (window != null) {
180             getImm().reportPerceptible(window, perceptible);
181         }
182     }
183 
184     @Override
dumpDebug(ProtoOutputStream proto, long fieldId)185     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
186         final long token = proto.start(fieldId);
187         super.dumpDebug(proto, INSETS_SOURCE_CONSUMER);
188         proto.write(IS_REQUESTED_VISIBLE_AWAITING_CONTROL, mIsRequestedVisibleAwaitingControl);
189         proto.write(IS_HIDE_ANIMATION_RUNNING, mIsHideAnimationRunning);
190         proto.write(IS_SHOW_REQUESTED_DURING_HIDE_ANIMATION, mIsShowRequestedDuringHideAnimation);
191         proto.end(token);
192     }
193 
194     /**
195      * Called when {@link #show} or {@link InputMethodManager#showSoftInput(View, int)} is called.
196      */
onShowRequested()197     public void onShowRequested() {
198         if (mIsHideAnimationRunning) {
199             mIsShowRequestedDuringHideAnimation = true;
200         }
201     }
202 
getImm()203     private InputMethodManager getImm() {
204         return mController.getHost().getInputMethodManager();
205     }
206 }
207