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 com.android.wm.shell.common;
18 
19 import static android.view.EventLogTags.IMF_IME_REMOTE_ANIM_CANCEL;
20 import static android.view.EventLogTags.IMF_IME_REMOTE_ANIM_END;
21 import static android.view.EventLogTags.IMF_IME_REMOTE_ANIM_START;
22 import static android.view.inputmethod.ImeTracker.DEBUG_IME_VISIBILITY;
23 import static android.view.inputmethod.ImeTracker.TOKEN_NONE;
24 
25 import android.animation.Animator;
26 import android.animation.AnimatorListenerAdapter;
27 import android.animation.ValueAnimator;
28 import android.annotation.IntDef;
29 import android.annotation.Nullable;
30 import android.content.ComponentName;
31 import android.content.res.Configuration;
32 import android.graphics.Point;
33 import android.graphics.Rect;
34 import android.os.RemoteException;
35 import android.util.EventLog;
36 import android.util.Slog;
37 import android.util.SparseArray;
38 import android.view.IDisplayWindowInsetsController;
39 import android.view.IWindowManager;
40 import android.view.InsetsSource;
41 import android.view.InsetsSourceControl;
42 import android.view.InsetsState;
43 import android.view.Surface;
44 import android.view.SurfaceControl;
45 import android.view.WindowInsets;
46 import android.view.WindowInsets.Type.InsetsType;
47 import android.view.animation.Interpolator;
48 import android.view.animation.PathInterpolator;
49 import android.view.inputmethod.ImeTracker;
50 import android.view.inputmethod.InputMethodManagerGlobal;
51 
52 import androidx.annotation.VisibleForTesting;
53 
54 import com.android.wm.shell.sysui.ShellInit;
55 
56 import java.util.ArrayList;
57 import java.util.Objects;
58 import java.util.concurrent.Executor;
59 
60 /**
61  * Manages IME control at the display-level. This occurs when IME comes up in multi-window mode.
62  */
63 public class DisplayImeController implements DisplayController.OnDisplaysChangedListener {
64     private static final String TAG = "DisplayImeController";
65 
66     private static final boolean DEBUG = false;
67 
68     // NOTE: All these constants came from InsetsController.
69     public static final int ANIMATION_DURATION_SHOW_MS = 275;
70     public static final int ANIMATION_DURATION_HIDE_MS = 340;
71     public static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
72     private static final int DIRECTION_NONE = 0;
73     private static final int DIRECTION_SHOW = 1;
74     private static final int DIRECTION_HIDE = 2;
75     private static final int FLOATING_IME_BOTTOM_INSET = -80;
76 
77     protected final IWindowManager mWmService;
78     protected final Executor mMainExecutor;
79     private final TransactionPool mTransactionPool;
80     private final DisplayController mDisplayController;
81     private final DisplayInsetsController mDisplayInsetsController;
82     private final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>();
83     private final ArrayList<ImePositionProcessor> mPositionProcessors = new ArrayList<>();
84 
85 
DisplayImeController(IWindowManager wmService, ShellInit shellInit, DisplayController displayController, DisplayInsetsController displayInsetsController, TransactionPool transactionPool, Executor mainExecutor)86     public DisplayImeController(IWindowManager wmService,
87             ShellInit shellInit,
88             DisplayController displayController,
89             DisplayInsetsController displayInsetsController,
90             TransactionPool transactionPool,
91             Executor mainExecutor) {
92         mWmService = wmService;
93         mDisplayController = displayController;
94         mDisplayInsetsController = displayInsetsController;
95         mMainExecutor = mainExecutor;
96         mTransactionPool = transactionPool;
97         shellInit.addInitCallback(this::onInit, this);
98     }
99 
100     /**
101      * Starts monitor displays changes and set insets controller for each displays.
102      */
onInit()103     public void onInit() {
104         mDisplayController.addDisplayWindowListener(this);
105     }
106 
107     @Override
onDisplayAdded(int displayId)108     public void onDisplayAdded(int displayId) {
109         // Add's a system-ui window-manager specifically for ime. This type is special because
110         // WM will defer IME inset handling to it in multi-window scenarious.
111         PerDisplay pd = new PerDisplay(displayId,
112                 mDisplayController.getDisplayLayout(displayId).rotation());
113         pd.register();
114         mImePerDisplay.put(displayId, pd);
115     }
116 
117     @Override
onDisplayConfigurationChanged(int displayId, Configuration newConfig)118     public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
119         PerDisplay pd = mImePerDisplay.get(displayId);
120         if (pd == null) {
121             return;
122         }
123         if (mDisplayController.getDisplayLayout(displayId).rotation()
124                 != pd.mRotation && isImeShowing(displayId)) {
125             pd.startAnimation(true, false /* forceRestart */, null /* statsToken */);
126         }
127     }
128 
129     @Override
onDisplayRemoved(int displayId)130     public void onDisplayRemoved(int displayId) {
131         PerDisplay pd = mImePerDisplay.get(displayId);
132         if (pd == null) {
133             return;
134         }
135         pd.unregister();
136         mImePerDisplay.remove(displayId);
137     }
138 
isImeShowing(int displayId)139     private boolean isImeShowing(int displayId) {
140         PerDisplay pd = mImePerDisplay.get(displayId);
141         if (pd == null) {
142             return false;
143         }
144         final InsetsSource imeSource = pd.mInsetsState.peekSource(InsetsSource.ID_IME);
145         return imeSource != null && pd.mImeSourceControl != null && imeSource.isVisible();
146     }
147 
dispatchPositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t)148     private void dispatchPositionChanged(int displayId, int imeTop,
149             SurfaceControl.Transaction t) {
150         synchronized (mPositionProcessors) {
151             for (ImePositionProcessor pp : mPositionProcessors) {
152                 pp.onImePositionChanged(displayId, imeTop, t);
153             }
154         }
155     }
156 
157     @ImePositionProcessor.ImeAnimationFlags
dispatchStartPositioning(int displayId, int hiddenTop, int shownTop, boolean show, boolean isFloating, SurfaceControl.Transaction t)158     private int dispatchStartPositioning(int displayId, int hiddenTop, int shownTop,
159             boolean show, boolean isFloating, SurfaceControl.Transaction t) {
160         synchronized (mPositionProcessors) {
161             int flags = 0;
162             for (ImePositionProcessor pp : mPositionProcessors) {
163                 flags |= pp.onImeStartPositioning(
164                         displayId, hiddenTop, shownTop, show, isFloating, t);
165             }
166             return flags;
167         }
168     }
169 
dispatchEndPositioning(int displayId, boolean cancel, SurfaceControl.Transaction t)170     private void dispatchEndPositioning(int displayId, boolean cancel,
171             SurfaceControl.Transaction t) {
172         synchronized (mPositionProcessors) {
173             for (ImePositionProcessor pp : mPositionProcessors) {
174                 pp.onImeEndPositioning(displayId, cancel, t);
175             }
176         }
177     }
178 
dispatchImeControlTargetChanged(int displayId, boolean controlling)179     private void dispatchImeControlTargetChanged(int displayId, boolean controlling) {
180         synchronized (mPositionProcessors) {
181             for (ImePositionProcessor pp : mPositionProcessors) {
182                 pp.onImeControlTargetChanged(displayId, controlling);
183             }
184         }
185     }
186 
dispatchVisibilityChanged(int displayId, boolean isShowing)187     private void dispatchVisibilityChanged(int displayId, boolean isShowing) {
188         synchronized (mPositionProcessors) {
189             for (ImePositionProcessor pp : mPositionProcessors) {
190                 pp.onImeVisibilityChanged(displayId, isShowing);
191             }
192         }
193     }
194 
195     /**
196      * Adds an {@link ImePositionProcessor} to be called during ime position updates.
197      */
addPositionProcessor(ImePositionProcessor processor)198     public void addPositionProcessor(ImePositionProcessor processor) {
199         synchronized (mPositionProcessors) {
200             if (mPositionProcessors.contains(processor)) {
201                 return;
202             }
203             mPositionProcessors.add(processor);
204         }
205     }
206 
207     /**
208      * Removes an {@link ImePositionProcessor} to be called during ime position updates.
209      */
removePositionProcessor(ImePositionProcessor processor)210     public void removePositionProcessor(ImePositionProcessor processor) {
211         synchronized (mPositionProcessors) {
212             mPositionProcessors.remove(processor);
213         }
214     }
215 
216     /** An implementation of {@link IDisplayWindowInsetsController} for a given display id. */
217     public class PerDisplay implements DisplayInsetsController.OnInsetsChangedListener {
218         final int mDisplayId;
219         final InsetsState mInsetsState = new InsetsState();
220         @InsetsType int mRequestedVisibleTypes = WindowInsets.Type.defaultVisible();
221         InsetsSourceControl mImeSourceControl = null;
222         int mAnimationDirection = DIRECTION_NONE;
223         ValueAnimator mAnimation = null;
224         int mRotation = Surface.ROTATION_0;
225         boolean mImeShowing = false;
226         final Rect mImeFrame = new Rect();
227         boolean mAnimateAlpha = true;
228 
PerDisplay(int displayId, int initialRotation)229         public PerDisplay(int displayId, int initialRotation) {
230             mDisplayId = displayId;
231             mRotation = initialRotation;
232         }
233 
register()234         public void register() {
235             mDisplayInsetsController.addInsetsChangedListener(mDisplayId, this);
236         }
237 
unregister()238         public void unregister() {
239             mDisplayInsetsController.removeInsetsChangedListener(mDisplayId, this);
240         }
241 
242         @Override
insetsChanged(InsetsState insetsState)243         public void insetsChanged(InsetsState insetsState) {
244             if (mInsetsState.equals(insetsState)) {
245                 return;
246             }
247 
248             updateImeVisibility(insetsState.isSourceOrDefaultVisible(InsetsSource.ID_IME,
249                     WindowInsets.Type.ime()));
250 
251             final InsetsSource newSource = insetsState.peekSource(InsetsSource.ID_IME);
252             final Rect newFrame = newSource != null ? newSource.getFrame() : null;
253             final boolean newSourceVisible = newSource != null && newSource.isVisible();
254             final InsetsSource oldSource = mInsetsState.peekSource(InsetsSource.ID_IME);
255             final Rect oldFrame = oldSource != null ? oldSource.getFrame() : null;
256 
257             mInsetsState.set(insetsState, true /* copySources */);
258             if (mImeShowing && !Objects.equals(oldFrame, newFrame) && newSourceVisible) {
259                 if (DEBUG) Slog.d(TAG, "insetsChanged when IME showing, restart animation");
260                 startAnimation(mImeShowing, true /* forceRestart */, null /* statsToken */);
261             }
262         }
263 
264         @Override
265         @VisibleForTesting
insetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls)266         public void insetsControlChanged(InsetsState insetsState,
267                 InsetsSourceControl[] activeControls) {
268             insetsChanged(insetsState);
269             InsetsSourceControl imeSourceControl = null;
270             if (activeControls != null) {
271                 for (InsetsSourceControl activeControl : activeControls) {
272                     if (activeControl == null) {
273                         continue;
274                     }
275                     if (activeControl.getType() == WindowInsets.Type.ime()) {
276                         imeSourceControl = activeControl;
277                     }
278                 }
279             }
280 
281             final boolean hadImeSourceControl = mImeSourceControl != null;
282             final boolean hasImeSourceControl = imeSourceControl != null;
283             if (hadImeSourceControl != hasImeSourceControl) {
284                 dispatchImeControlTargetChanged(mDisplayId, hasImeSourceControl);
285             }
286 
287             if (hasImeSourceControl) {
288                 if (mAnimation != null) {
289                     final Point lastSurfacePosition = hadImeSourceControl
290                             ? mImeSourceControl.getSurfacePosition() : null;
291                     final boolean positionChanged =
292                             !imeSourceControl.getSurfacePosition().equals(lastSurfacePosition);
293                     if (positionChanged) {
294                         startAnimation(mImeShowing, true /* forceRestart */, null /* statsToken */);
295                     }
296                 } else {
297                     if (!haveSameLeash(mImeSourceControl, imeSourceControl)) {
298                         applyVisibilityToLeash(imeSourceControl);
299                     }
300                     if (!mImeShowing) {
301                         removeImeSurface();
302                     }
303                 }
304             } else if (mAnimation != null) {
305                 mAnimation.cancel();
306             }
307 
308             if (hadImeSourceControl && mImeSourceControl != imeSourceControl) {
309                 mImeSourceControl.release(SurfaceControl::release);
310             }
311             mImeSourceControl = imeSourceControl;
312         }
313 
applyVisibilityToLeash(InsetsSourceControl imeSourceControl)314         private void applyVisibilityToLeash(InsetsSourceControl imeSourceControl) {
315             SurfaceControl leash = imeSourceControl.getLeash();
316             if (leash != null) {
317                 SurfaceControl.Transaction t = mTransactionPool.acquire();
318                 if (mImeShowing) {
319                     t.show(leash);
320                 } else {
321                     t.hide(leash);
322                 }
323                 t.apply();
324                 mTransactionPool.release(t);
325             }
326         }
327 
328         @Override
showInsets(@nsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken)329         public void showInsets(@InsetsType int types, boolean fromIme,
330                 @Nullable ImeTracker.Token statsToken) {
331             if ((types & WindowInsets.Type.ime()) == 0) {
332                 return;
333             }
334             if (DEBUG) Slog.d(TAG, "Got showInsets for ime");
335             startAnimation(true /* show */, false /* forceRestart */, statsToken);
336         }
337 
338         @Override
hideInsets(@nsetsType int types, boolean fromIme, @Nullable ImeTracker.Token statsToken)339         public void hideInsets(@InsetsType int types, boolean fromIme,
340                 @Nullable ImeTracker.Token statsToken) {
341             if ((types & WindowInsets.Type.ime()) == 0) {
342                 return;
343             }
344             if (DEBUG) Slog.d(TAG, "Got hideInsets for ime");
345             startAnimation(false /* show */, false /* forceRestart */, statsToken);
346         }
347 
348         @Override
topFocusedWindowChanged(ComponentName component, int requestedVisibleTypes)349         public void topFocusedWindowChanged(ComponentName component, int requestedVisibleTypes) {
350             // Do nothing
351         }
352 
353         /**
354          * Sends the local visibility state back to window manager. Needed for legacy adjustForIme.
355          */
setVisibleDirectly(boolean visible)356         private void setVisibleDirectly(boolean visible) {
357             mInsetsState.setSourceVisible(InsetsSource.ID_IME, visible);
358             mRequestedVisibleTypes = visible
359                     ? mRequestedVisibleTypes | WindowInsets.Type.ime()
360                     : mRequestedVisibleTypes & ~WindowInsets.Type.ime();
361             try {
362                 mWmService.updateDisplayWindowRequestedVisibleTypes(mDisplayId,
363                         mRequestedVisibleTypes);
364             } catch (RemoteException e) {
365             }
366         }
367 
imeTop(float surfaceOffset)368         private int imeTop(float surfaceOffset) {
369             return mImeFrame.top + (int) surfaceOffset;
370         }
371 
calcIsFloating(InsetsSource imeSource)372         private boolean calcIsFloating(InsetsSource imeSource) {
373             final Rect frame = imeSource.getFrame();
374             if (frame.height() == 0) {
375                 return true;
376             }
377             // Some Floating Input Methods will still report a frame, but the frame is actually
378             // a nav-bar inset created by WM and not part of the IME (despite being reported as
379             // an IME inset). For now, we assume that no non-floating IME will be <= this nav bar
380             // frame height so any reported frame that is <= nav-bar frame height is assumed to
381             // be floating.
382             return frame.height() <= mDisplayController.getDisplayLayout(mDisplayId)
383                     .navBarFrameHeight();
384         }
385 
startAnimation(final boolean show, final boolean forceRestart, @Nullable ImeTracker.Token statsToken)386         private void startAnimation(final boolean show, final boolean forceRestart,
387                 @Nullable ImeTracker.Token statsToken) {
388             final InsetsSource imeSource = mInsetsState.peekSource(InsetsSource.ID_IME);
389             if (imeSource == null || mImeSourceControl == null) {
390                 ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_WM_ANIMATION_CREATE);
391                 return;
392             }
393             final Rect newFrame = imeSource.getFrame();
394             final boolean isFloating = calcIsFloating(imeSource) && show;
395             if (isFloating) {
396                 // This is a "floating" or "expanded" IME, so to get animations, just
397                 // pretend the ime has some size just below the screen.
398                 mImeFrame.set(newFrame);
399                 final int floatingInset = (int) (mDisplayController.getDisplayLayout(mDisplayId)
400                         .density() * FLOATING_IME_BOTTOM_INSET);
401                 mImeFrame.bottom -= floatingInset;
402             } else if (newFrame.height() != 0) {
403                 // Don't set a new frame if it's empty and hiding -- this maintains continuity
404                 mImeFrame.set(newFrame);
405             }
406             if (DEBUG) {
407                 Slog.d(TAG, "Run startAnim  show:" + show + "  was:"
408                         + (mAnimationDirection == DIRECTION_SHOW ? "SHOW"
409                         : (mAnimationDirection == DIRECTION_HIDE ? "HIDE" : "NONE")));
410             }
411             if ((!forceRestart && (mAnimationDirection == DIRECTION_SHOW && show))
412                     || (mAnimationDirection == DIRECTION_HIDE && !show)) {
413                 ImeTracker.forLogging().onCancelled(
414                         statsToken, ImeTracker.PHASE_WM_ANIMATION_CREATE);
415                 return;
416             }
417             boolean seek = false;
418             float seekValue = 0;
419             if (mAnimation != null) {
420                 if (mAnimation.isRunning()) {
421                     seekValue = (float) mAnimation.getAnimatedValue();
422                     seek = true;
423                 }
424                 mAnimation.cancel();
425             }
426             final float defaultY = mImeSourceControl.getSurfacePosition().y;
427             final float x = mImeSourceControl.getSurfacePosition().x;
428             final float hiddenY = defaultY + mImeFrame.height();
429             final float shownY = defaultY;
430             final float startY = show ? hiddenY : shownY;
431             final float endY = show ? shownY : hiddenY;
432             if (mAnimationDirection == DIRECTION_NONE && mImeShowing && show) {
433                 // IME is already showing, so set seek to end
434                 seekValue = shownY;
435                 seek = true;
436             }
437             mAnimationDirection = show ? DIRECTION_SHOW : DIRECTION_HIDE;
438             updateImeVisibility(show);
439             mAnimation = ValueAnimator.ofFloat(startY, endY);
440             mAnimation.setDuration(
441                     show ? ANIMATION_DURATION_SHOW_MS : ANIMATION_DURATION_HIDE_MS);
442             if (seek) {
443                 mAnimation.setCurrentFraction((seekValue - startY) / (endY - startY));
444             }
445 
446             mAnimation.addUpdateListener(animation -> {
447                 SurfaceControl.Transaction t = mTransactionPool.acquire();
448                 float value = (float) animation.getAnimatedValue();
449                 t.setPosition(mImeSourceControl.getLeash(), x, value);
450                 final float alpha = (mAnimateAlpha || isFloating)
451                         ? (value - hiddenY) / (shownY - hiddenY) : 1.f;
452                 t.setAlpha(mImeSourceControl.getLeash(), alpha);
453                 dispatchPositionChanged(mDisplayId, imeTop(value), t);
454                 t.apply();
455                 mTransactionPool.release(t);
456             });
457             mAnimation.setInterpolator(INTERPOLATOR);
458             ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_WM_ANIMATION_CREATE);
459             mAnimation.addListener(new AnimatorListenerAdapter() {
460                 private boolean mCancelled = false;
461                 @Nullable
462                 private final ImeTracker.Token mStatsToken = statsToken;
463 
464                 @Override
465                 public void onAnimationStart(Animator animation) {
466                     SurfaceControl.Transaction t = mTransactionPool.acquire();
467                     t.setPosition(mImeSourceControl.getLeash(), x, startY);
468                     if (DEBUG) {
469                         Slog.d(TAG, "onAnimationStart d:" + mDisplayId + " top:"
470                                 + imeTop(hiddenY) + "->" + imeTop(shownY)
471                                 + " showing:" + (mAnimationDirection == DIRECTION_SHOW));
472                     }
473                     int flags = dispatchStartPositioning(mDisplayId, imeTop(hiddenY),
474                             imeTop(shownY), mAnimationDirection == DIRECTION_SHOW, isFloating, t);
475                     mAnimateAlpha = (flags & ImePositionProcessor.IME_ANIMATION_NO_ALPHA) == 0;
476                     final float alpha = (mAnimateAlpha || isFloating)
477                             ? (startY - hiddenY) / (shownY - hiddenY)
478                             : 1.f;
479                     t.setAlpha(mImeSourceControl.getLeash(), alpha);
480                     if (mAnimationDirection == DIRECTION_SHOW) {
481                         ImeTracker.forLogging().onProgress(mStatsToken,
482                                 ImeTracker.PHASE_WM_ANIMATION_RUNNING);
483                         t.show(mImeSourceControl.getLeash());
484                     }
485                     if (DEBUG_IME_VISIBILITY) {
486                         EventLog.writeEvent(IMF_IME_REMOTE_ANIM_START,
487                                 statsToken != null ? statsToken.getTag() : TOKEN_NONE,
488                                 mDisplayId, mAnimationDirection, alpha, startY , endY,
489                                 Objects.toString(mImeSourceControl.getLeash()),
490                                 Objects.toString(mImeSourceControl.getInsetsHint()),
491                                 Objects.toString(mImeSourceControl.getSurfacePosition()),
492                                 Objects.toString(mImeFrame));
493                     }
494                     t.apply();
495                     mTransactionPool.release(t);
496                 }
497 
498                 @Override
499                 public void onAnimationCancel(Animator animation) {
500                     mCancelled = true;
501                     if (DEBUG_IME_VISIBILITY) {
502                         EventLog.writeEvent(IMF_IME_REMOTE_ANIM_CANCEL,
503                                 statsToken != null ? statsToken.getTag() : TOKEN_NONE, mDisplayId,
504                                 Objects.toString(mImeSourceControl.getInsetsHint()));
505                     }
506                 }
507 
508                 @Override
509                 public void onAnimationEnd(Animator animation) {
510                     if (DEBUG) Slog.d(TAG, "onAnimationEnd " + mCancelled);
511                     SurfaceControl.Transaction t = mTransactionPool.acquire();
512                     if (!mCancelled) {
513                         t.setPosition(mImeSourceControl.getLeash(), x, endY);
514                         t.setAlpha(mImeSourceControl.getLeash(), 1.f);
515                     }
516                     dispatchEndPositioning(mDisplayId, mCancelled, t);
517                     if (mAnimationDirection == DIRECTION_HIDE && !mCancelled) {
518                         ImeTracker.forLogging().onProgress(mStatsToken,
519                                 ImeTracker.PHASE_WM_ANIMATION_RUNNING);
520                         t.hide(mImeSourceControl.getLeash());
521                         removeImeSurface();
522                         ImeTracker.forLogging().onHidden(mStatsToken);
523                     } else if (mAnimationDirection == DIRECTION_SHOW && !mCancelled) {
524                         ImeTracker.forLogging().onShown(mStatsToken);
525                     } else if (mCancelled) {
526                         ImeTracker.forLogging().onCancelled(mStatsToken,
527                                 ImeTracker.PHASE_WM_ANIMATION_RUNNING);
528                     }
529                     if (DEBUG_IME_VISIBILITY) {
530                         EventLog.writeEvent(IMF_IME_REMOTE_ANIM_END,
531                                 statsToken != null ? statsToken.getTag() : TOKEN_NONE,
532                                 mDisplayId, mAnimationDirection, endY,
533                                 Objects.toString(mImeSourceControl.getLeash()),
534                                 Objects.toString(mImeSourceControl.getInsetsHint()),
535                                 Objects.toString(mImeSourceControl.getSurfacePosition()),
536                                 Objects.toString(mImeFrame));
537                     }
538                     t.apply();
539                     mTransactionPool.release(t);
540 
541                     mAnimationDirection = DIRECTION_NONE;
542                     mAnimation = null;
543                 }
544             });
545             if (!show) {
546                 // When going away, queue up insets change first, otherwise any bounds changes
547                 // can have a "flicker" of ime-provided insets.
548                 setVisibleDirectly(false /* visible */);
549             }
550             mAnimation.start();
551             if (show) {
552                 // When showing away, queue up insets change last, otherwise any bounds changes
553                 // can have a "flicker" of ime-provided insets.
554                 setVisibleDirectly(true /* visible */);
555             }
556         }
557 
updateImeVisibility(boolean isShowing)558         private void updateImeVisibility(boolean isShowing) {
559             if (mImeShowing != isShowing) {
560                 mImeShowing = isShowing;
561                 dispatchVisibilityChanged(mDisplayId, isShowing);
562             }
563         }
564 
565         @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
getImeSourceControl()566         public InsetsSourceControl getImeSourceControl() {
567             return mImeSourceControl;
568         }
569     }
570 
removeImeSurface()571     void removeImeSurface() {
572         // Remove the IME surface to make the insets invisible for
573         // non-client controlled insets.
574         InputMethodManagerGlobal.removeImeSurface(
575                 e -> Slog.e(TAG, "Failed to remove IME surface.", e));
576     }
577 
578     /**
579      * Allows other things to synchronize with the ime position
580      */
581     public interface ImePositionProcessor {
582         /**
583          * Indicates that ime shouldn't animate alpha. It will always be opaque. Used when stuff
584          * behind the IME shouldn't be visible (for example during split-screen adjustment where
585          * there is nothing behind the ime).
586          */
587         int IME_ANIMATION_NO_ALPHA = 1;
588 
589         /** @hide */
590         @IntDef(prefix = {"IME_ANIMATION_"}, value = {
591                 IME_ANIMATION_NO_ALPHA,
592         })
593         @interface ImeAnimationFlags {
594         }
595 
596         /**
597          * Called when the IME position is starting to animate.
598          *
599          * @param hiddenTop  The y position of the top of the IME surface when it is hidden.
600          * @param shownTop   The y position of the top of the IME surface when it is shown.
601          * @param showing    {@code true} when we are animating from hidden to shown, {@code false}
602          *                   when animating from shown to hidden.
603          * @param isFloating {@code true} when the ime is a floating ime (doesn't inset).
604          * @return flags that may alter how ime itself is animated (eg. no-alpha).
605          */
606         @ImeAnimationFlags
onImeStartPositioning(int displayId, int hiddenTop, int shownTop, boolean showing, boolean isFloating, SurfaceControl.Transaction t)607         default int onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
608                 boolean showing, boolean isFloating, SurfaceControl.Transaction t) {
609             return 0;
610         }
611 
612         /**
613          * Called when the ime position changed. This is expected to be a synchronous call on the
614          * animation thread. Operations can be added to the transaction to be applied in sync.
615          *
616          * @param imeTop The current y position of the top of the IME surface.
617          */
onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t)618         default void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t) {
619         }
620 
621         /**
622          * Called when the IME position is done animating.
623          *
624          * @param cancel {@code true} if this was cancelled. This implies another start is coming.
625          */
onImeEndPositioning(int displayId, boolean cancel, SurfaceControl.Transaction t)626         default void onImeEndPositioning(int displayId, boolean cancel,
627                 SurfaceControl.Transaction t) {
628         }
629 
630         /**
631          * Called when the IME control target changed. So that the processor can restore its
632          * adjusted layout when the IME insets is not controlling by the current controller anymore.
633          *
634          * @param controlling indicates whether the current controller is controlling IME insets.
635          */
onImeControlTargetChanged(int displayId, boolean controlling)636         default void onImeControlTargetChanged(int displayId, boolean controlling) {
637         }
638 
639         /**
640          * Called when the IME visibility changed.
641          *
642          * @param isShowing {@code true} if the IME is shown.
643          */
onImeVisibilityChanged(int displayId, boolean isShowing)644         default void onImeVisibilityChanged(int displayId, boolean isShowing) {
645 
646         }
647     }
648 
haveSameLeash(InsetsSourceControl a, InsetsSourceControl b)649     private static boolean haveSameLeash(InsetsSourceControl a, InsetsSourceControl b) {
650         if (a == b) {
651             return true;
652         }
653         if (a == null || b == null) {
654             return false;
655         }
656         if (a.getLeash() == b.getLeash()) {
657             return true;
658         }
659         if (a.getLeash() == null || b.getLeash() == null) {
660             return false;
661         }
662         return a.getLeash().isSameSurface(b.getLeash());
663     }
664 }
665