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