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