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