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