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.server.wm; 18 19 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN; 20 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; 21 import static android.view.InsetsController.ANIMATION_TYPE_HIDE; 22 import static android.view.InsetsController.ANIMATION_TYPE_SHOW; 23 import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN; 24 import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN; 25 import static android.view.InsetsState.ITYPE_IME; 26 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; 27 import static android.view.InsetsState.ITYPE_STATUS_BAR; 28 import static android.view.SyncRtSurfaceTransactionApplier.applyParams; 29 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; 30 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR; 31 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION; 32 33 import android.annotation.NonNull; 34 import android.annotation.Nullable; 35 import android.app.StatusBarManager; 36 import android.util.IntArray; 37 import android.util.SparseArray; 38 import android.view.InsetsAnimationControlCallbacks; 39 import android.view.InsetsAnimationControlImpl; 40 import android.view.InsetsAnimationControlRunner; 41 import android.view.InsetsController; 42 import android.view.InsetsSource; 43 import android.view.InsetsSourceControl; 44 import android.view.InsetsState; 45 import android.view.InsetsState.InternalInsetsType; 46 import android.view.InternalInsetsAnimationController; 47 import android.view.SurfaceControl; 48 import android.view.SyncRtSurfaceTransactionApplier; 49 import android.view.WindowInsets.Type; 50 import android.view.WindowInsetsAnimation; 51 import android.view.WindowInsetsAnimation.Bounds; 52 import android.view.WindowInsetsAnimationControlListener; 53 import android.view.WindowInsetsAnimationController; 54 import android.view.WindowManager; 55 56 import com.android.internal.R; 57 import com.android.internal.annotations.VisibleForTesting; 58 import com.android.server.DisplayThread; 59 import com.android.server.statusbar.StatusBarManagerInternal; 60 61 /** 62 * Policy that implements who gets control over the windows generating insets. 63 */ 64 class InsetsPolicy { 65 66 private final InsetsStateController mStateController; 67 private final DisplayContent mDisplayContent; 68 private final DisplayPolicy mPolicy; 69 private final IntArray mShowingTransientTypes = new IntArray(); 70 71 /** For resetting visibilities of insets sources. */ 72 private final InsetsControlTarget mDummyControlTarget = new InsetsControlTarget() { 73 74 @Override 75 public void notifyInsetsControlChanged() { 76 boolean hasLeash = false; 77 final InsetsSourceControl[] controls = 78 mStateController.getControlsForDispatch(this); 79 if (controls == null) { 80 return; 81 } 82 for (InsetsSourceControl control : controls) { 83 final @InternalInsetsType int type = control.getType(); 84 if (mShowingTransientTypes.indexOf(type) != -1) { 85 // The visibilities of transient bars will be handled with animations. 86 continue; 87 } 88 final SurfaceControl leash = control.getLeash(); 89 if (leash != null) { 90 hasLeash = true; 91 92 // We use alpha to control the visibility here which aligns the logic at 93 // SurfaceAnimator.createAnimationLeash 94 mDisplayContent.getPendingTransaction().setAlpha( 95 leash, InsetsState.getDefaultVisibility(type) ? 1f : 0f); 96 } 97 } 98 if (hasLeash) { 99 mDisplayContent.scheduleAnimation(); 100 } 101 } 102 }; 103 104 private WindowState mFocusedWin; 105 private BarWindow mStatusBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR); 106 private BarWindow mNavBar = new BarWindow(StatusBarManager.WINDOW_NAVIGATION_BAR); 107 private boolean mAnimatingShown; 108 /** 109 * Let remote insets controller control system bars regardless of other settings. 110 */ 111 private boolean mRemoteInsetsControllerControlsSystemBars; 112 private final float[] mTmpFloat9 = new float[9]; 113 InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent)114 InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) { 115 mStateController = stateController; 116 mDisplayContent = displayContent; 117 mPolicy = displayContent.getDisplayPolicy(); 118 mRemoteInsetsControllerControlsSystemBars = mPolicy.getContext().getResources().getBoolean( 119 R.bool.config_remoteInsetsControllerControlsSystemBars); 120 } 121 getRemoteInsetsControllerControlsSystemBars()122 boolean getRemoteInsetsControllerControlsSystemBars() { 123 return mRemoteInsetsControllerControlsSystemBars; 124 } 125 126 /** 127 * Used only for testing. 128 */ 129 @VisibleForTesting setRemoteInsetsControllerControlsSystemBars(boolean controlsSystemBars)130 void setRemoteInsetsControllerControlsSystemBars(boolean controlsSystemBars) { 131 mRemoteInsetsControllerControlsSystemBars = controlsSystemBars; 132 } 133 134 /** Updates the target which can control system bars. */ updateBarControlTarget(@ullable WindowState focusedWin)135 void updateBarControlTarget(@Nullable WindowState focusedWin) { 136 if (mFocusedWin != focusedWin){ 137 abortTransient(); 138 } 139 mFocusedWin = focusedWin; 140 final InsetsControlTarget statusControlTarget = 141 getStatusControlTarget(focusedWin, false /* fake */); 142 final InsetsControlTarget navControlTarget = 143 getNavControlTarget(focusedWin, false /* fake */); 144 mStateController.onBarControlTargetChanged( 145 statusControlTarget, 146 statusControlTarget == mDummyControlTarget 147 ? getStatusControlTarget(focusedWin, true /* fake */) 148 : null, 149 navControlTarget, 150 navControlTarget == mDummyControlTarget 151 ? getNavControlTarget(focusedWin, true /* fake */) 152 : null); 153 mStatusBar.updateVisibility(statusControlTarget, ITYPE_STATUS_BAR); 154 mNavBar.updateVisibility(navControlTarget, ITYPE_NAVIGATION_BAR); 155 } 156 isHidden(@nternalInsetsType int type)157 boolean isHidden(@InternalInsetsType int type) { 158 final InsetsSourceProvider provider = mStateController.peekSourceProvider(type); 159 return provider != null && provider.hasWindow() && !provider.getSource().isVisible(); 160 } 161 showTransient(@nternalInsetsType int[] types, boolean isGestureOnSystemBar)162 void showTransient(@InternalInsetsType int[] types, boolean isGestureOnSystemBar) { 163 boolean changed = false; 164 for (int i = types.length - 1; i >= 0; i--) { 165 final @InternalInsetsType int type = types[i]; 166 if (!isHidden(type)) { 167 continue; 168 } 169 if (mShowingTransientTypes.indexOf(type) != -1) { 170 continue; 171 } 172 mShowingTransientTypes.add(type); 173 changed = true; 174 } 175 if (changed) { 176 StatusBarManagerInternal statusBarManagerInternal = 177 mPolicy.getStatusBarManagerInternal(); 178 if (statusBarManagerInternal != null) { 179 statusBarManagerInternal.showTransient(mDisplayContent.getDisplayId(), 180 mShowingTransientTypes.toArray(), isGestureOnSystemBar); 181 } 182 updateBarControlTarget(mFocusedWin); 183 184 // The leashes can be created while updating bar control target. The surface transaction 185 // of the new leashes might not be applied yet. The callback posted here ensures we can 186 // get the valid leashes because the surface transaction will be applied in the next 187 // animation frame which will be triggered if a new leash is created. 188 mDisplayContent.mWmService.mAnimator.getChoreographer().postFrameCallback(time -> { 189 synchronized (mDisplayContent.mWmService.mGlobalLock) { 190 startAnimation(true /* show */, null /* callback */); 191 } 192 }); 193 } 194 } 195 hideTransient()196 void hideTransient() { 197 if (mShowingTransientTypes.size() == 0) { 198 return; 199 } 200 startAnimation(false /* show */, () -> { 201 synchronized (mDisplayContent.mWmService.mGlobalLock) { 202 for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) { 203 // We are about to clear mShowingTransientTypes, we don't want the transient bar 204 // can cause insets on the client. Restore the client visibility. 205 final @InternalInsetsType int type = mShowingTransientTypes.get(i); 206 mStateController.getSourceProvider(type).setClientVisible(false); 207 } 208 mShowingTransientTypes.clear(); 209 updateBarControlTarget(mFocusedWin); 210 } 211 }); 212 } 213 isTransient(@nternalInsetsType int type)214 boolean isTransient(@InternalInsetsType int type) { 215 return mShowingTransientTypes.indexOf(type) != -1; 216 } 217 218 /** 219 * @see InsetsStateController#getInsetsForWindow 220 */ getInsetsForWindow(WindowState target)221 InsetsState getInsetsForWindow(WindowState target) { 222 final InsetsState originalState = mStateController.getInsetsForWindow(target); 223 final InsetsState state = adjustVisibilityForTransientTypes(originalState); 224 return adjustVisibilityForIme(target, state, state == originalState); 225 } 226 227 /** 228 * @see InsetsStateController#getInsetsForWindowMetrics 229 */ getInsetsForWindowMetrics(@onNull WindowManager.LayoutParams attrs)230 InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) { 231 final InsetsState originalState = mStateController.getInsetsForWindowMetrics(attrs); 232 return adjustVisibilityForTransientTypes(originalState); 233 } 234 adjustVisibilityForTransientTypes(InsetsState originalState)235 private InsetsState adjustVisibilityForTransientTypes(InsetsState originalState) { 236 InsetsState state = originalState; 237 for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) { 238 final @InternalInsetsType int type = mShowingTransientTypes.get(i); 239 final InsetsSource originalSource = state.peekSource(type); 240 if (originalSource != null && originalSource.isVisible()) { 241 if (state == originalState) { 242 // The source will be modified, create a non-deep copy to store the new one. 243 state = new InsetsState(originalState); 244 } 245 // Replace the source with a copy in invisible state. 246 final InsetsSource source = new InsetsSource(originalSource); 247 source.setVisible(false); 248 state.addSource(source); 249 } 250 } 251 return state; 252 } 253 adjustVisibilityForIme(WindowState w, InsetsState originalState, boolean copyState)254 private InsetsState adjustVisibilityForIme(WindowState w, InsetsState originalState, 255 boolean copyState) { 256 if (w.mIsImWindow) { 257 // Navigation bar insets is always visible to IME. 258 final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR); 259 if (originalNavSource != null && !originalNavSource.isVisible()) { 260 final InsetsState state = copyState ? new InsetsState(originalState) 261 : originalState; 262 final InsetsSource navSource = new InsetsSource(originalNavSource); 263 navSource.setVisible(true); 264 state.addSource(navSource); 265 return state; 266 } 267 } else if (w.mActivityRecord != null && w.mActivityRecord.mImeInsetsFrozenUntilStartInput) { 268 // During switching tasks with gestural navigation, if the IME is attached to 269 // one app window on that time, even the next app window is behind the IME window, 270 // conceptually the window should not receive the IME insets if the next window is 271 // not eligible IME requester and ready to show IME on top of it. 272 final boolean shouldImeAttachedToApp = mDisplayContent.shouldImeAttachedToApp(); 273 final InsetsSource originalImeSource = originalState.peekSource(ITYPE_IME); 274 275 if (shouldImeAttachedToApp && originalImeSource != null) { 276 final boolean imeVisibility = 277 w.mActivityRecord.mLastImeShown || w.getRequestedVisibility(ITYPE_IME); 278 final InsetsState state = copyState ? new InsetsState(originalState) 279 : originalState; 280 final InsetsSource imeSource = new InsetsSource(originalImeSource); 281 imeSource.setVisible(imeVisibility); 282 state.addSource(imeSource); 283 return state; 284 } 285 } 286 return originalState; 287 } 288 onInsetsModified(InsetsControlTarget caller)289 void onInsetsModified(InsetsControlTarget caller) { 290 mStateController.onInsetsModified(caller); 291 checkAbortTransient(caller); 292 updateBarControlTarget(mFocusedWin); 293 } 294 295 /** 296 * Called when a control target modified the insets state. If the target set a insets source to 297 * visible while it is shown transiently, we need to abort the transient state. While IME is 298 * requested visible, we also need to abort the transient state of navigation bar if it is shown 299 * transiently. 300 * 301 * @param caller who changed the insets state. 302 */ checkAbortTransient(InsetsControlTarget caller)303 private void checkAbortTransient(InsetsControlTarget caller) { 304 if (mShowingTransientTypes.size() != 0) { 305 final IntArray abortTypes = new IntArray(); 306 final boolean imeRequestedVisible = caller.getRequestedVisibility(ITYPE_IME); 307 for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) { 308 final @InternalInsetsType int type = mShowingTransientTypes.get(i); 309 if ((mStateController.isFakeTarget(type, caller) 310 && caller.getRequestedVisibility(type)) 311 || (type == ITYPE_NAVIGATION_BAR && imeRequestedVisible)) { 312 mShowingTransientTypes.remove(i); 313 abortTypes.add(type); 314 } 315 } 316 StatusBarManagerInternal statusBarManagerInternal = 317 mPolicy.getStatusBarManagerInternal(); 318 if (abortTypes.size() > 0 && statusBarManagerInternal != null) { 319 statusBarManagerInternal.abortTransient( 320 mDisplayContent.getDisplayId(), abortTypes.toArray()); 321 } 322 } 323 } 324 325 /** 326 * If the caller is not {@link #updateBarControlTarget}, it should call 327 * updateBarControlTarget(mFocusedWin) after this invocation. 328 */ abortTransient()329 private void abortTransient() { 330 StatusBarManagerInternal statusBarManagerInternal = mPolicy.getStatusBarManagerInternal(); 331 if (statusBarManagerInternal != null) { 332 statusBarManagerInternal.abortTransient( 333 mDisplayContent.getDisplayId(), mShowingTransientTypes.toArray()); 334 } 335 mShowingTransientTypes.clear(); 336 } 337 getStatusControlTarget(@ullable WindowState focusedWin, boolean fake)338 private @Nullable InsetsControlTarget getStatusControlTarget(@Nullable WindowState focusedWin, 339 boolean fake) { 340 if (!fake && isShowingTransientTypes(Type.statusBars())) { 341 return mDummyControlTarget; 342 } 343 final WindowState notificationShade = mPolicy.getNotificationShade(); 344 if (focusedWin == notificationShade) { 345 // Notification shade has control anyways, no reason to force anything. 346 return focusedWin; 347 } 348 if (remoteInsetsControllerControlsSystemBars(focusedWin)) { 349 mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged( 350 focusedWin.mAttrs.packageName); 351 return mDisplayContent.mRemoteInsetsControlTarget; 352 } 353 if (mPolicy.areSystemBarsForcedShownLw()) { 354 // Status bar is forcibly shown. We don't want the client to control the status bar, and 355 // we will dispatch the real visibility of status bar to the client. 356 return null; 357 } 358 if (forceShowsStatusBarTransiently() && !fake) { 359 // Status bar is forcibly shown transiently, and its new visibility won't be 360 // dispatched to the client so that we can keep the layout stable. We will dispatch the 361 // fake control to the client, so that it can re-show the bar during this scenario. 362 return mDummyControlTarget; 363 } 364 if (!canBeTopFullscreenOpaqueWindow(focusedWin) && mPolicy.topAppHidesStatusBar() 365 && (notificationShade == null || !notificationShade.canReceiveKeys())) { 366 // Non-fullscreen focused window should not break the state that the top-fullscreen-app 367 // window hides status bar, unless the notification shade can receive keys. 368 return mPolicy.getTopFullscreenOpaqueWindow(); 369 } 370 return focusedWin; 371 } 372 canBeTopFullscreenOpaqueWindow(@ullable WindowState win)373 private static boolean canBeTopFullscreenOpaqueWindow(@Nullable WindowState win) { 374 // The condition doesn't use WindowState#canAffectSystemUiFlags because the window may 375 // haven't drawn or committed the visibility. 376 final boolean nonAttachedAppWindow = win != null 377 && win.mAttrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW 378 && win.mAttrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; 379 return nonAttachedAppWindow && win.mAttrs.isFullscreen() && !win.isFullyTransparent() 380 && !win.inMultiWindowMode(); 381 } 382 getNavControlTarget(@ullable WindowState focusedWin, boolean fake)383 private @Nullable InsetsControlTarget getNavControlTarget(@Nullable WindowState focusedWin, 384 boolean fake) { 385 final WindowState imeWin = mDisplayContent.mInputMethodWindow; 386 if (imeWin != null && imeWin.isVisible()) { 387 // Force showing navigation bar while IME is visible. 388 return null; 389 } 390 if (!fake && isShowingTransientTypes(Type.navigationBars())) { 391 return mDummyControlTarget; 392 } 393 if (focusedWin == mPolicy.getNotificationShade()) { 394 // Notification shade has control anyways, no reason to force anything. 395 return focusedWin; 396 } 397 if (remoteInsetsControllerControlsSystemBars(focusedWin)) { 398 mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged( 399 focusedWin.mAttrs.packageName); 400 return mDisplayContent.mRemoteInsetsControlTarget; 401 } 402 if (mPolicy.areSystemBarsForcedShownLw()) { 403 // Navigation bar is forcibly shown. We don't want the client to control the navigation 404 // bar, and we will dispatch the real visibility of navigation bar to the client. 405 return null; 406 } 407 if (forceShowsNavigationBarTransiently() && !fake) { 408 // Navigation bar is forcibly shown transiently, and its new visibility won't be 409 // dispatched to the client so that we can keep the layout stable. We will dispatch the 410 // fake control to the client, so that it can re-show the bar during this scenario. 411 return mDummyControlTarget; 412 } 413 return focusedWin; 414 } 415 isShowingTransientTypes(@ype.InsetsType int types)416 private boolean isShowingTransientTypes(@Type.InsetsType int types) { 417 final IntArray showingTransientTypes = mShowingTransientTypes; 418 for (int i = showingTransientTypes.size() - 1; i >= 0; i--) { 419 if ((InsetsState.toPublicType(showingTransientTypes.get(i)) & types) != 0) { 420 return true; 421 } 422 } 423 return false; 424 } 425 426 /** 427 * Determines whether the remote insets controller should take control of system bars for all 428 * windows. 429 */ remoteInsetsControllerControlsSystemBars(@ullable WindowState focusedWin)430 boolean remoteInsetsControllerControlsSystemBars(@Nullable WindowState focusedWin) { 431 if (focusedWin == null) { 432 return false; 433 } 434 if (!mRemoteInsetsControllerControlsSystemBars) { 435 return false; 436 } 437 if (mDisplayContent == null || mDisplayContent.mRemoteInsetsControlTarget == null) { 438 // No remote insets control target to take control of insets. 439 return false; 440 } 441 // If necessary, auto can control application windows when 442 // config_remoteInsetsControllerControlsSystemBars is set to true. This is useful in cases 443 // where we want to dictate system bar inset state for applications. 444 return focusedWin.getAttrs().type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW 445 && focusedWin.getAttrs().type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; 446 } 447 forceShowsStatusBarTransiently()448 private boolean forceShowsStatusBarTransiently() { 449 final WindowState win = mPolicy.getStatusBar(); 450 return win != null && (win.mAttrs.privateFlags & PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR) != 0; 451 } 452 forceShowsNavigationBarTransiently()453 private boolean forceShowsNavigationBarTransiently() { 454 final WindowState win = mPolicy.getNotificationShade(); 455 return win != null 456 && (win.mAttrs.privateFlags & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0; 457 } 458 459 @VisibleForTesting startAnimation(boolean show, Runnable callback)460 void startAnimation(boolean show, Runnable callback) { 461 int typesReady = 0; 462 final SparseArray<InsetsSourceControl> controls = new SparseArray<>(); 463 final IntArray showingTransientTypes = mShowingTransientTypes; 464 for (int i = showingTransientTypes.size() - 1; i >= 0; i--) { 465 final @InternalInsetsType int type = showingTransientTypes.get(i); 466 InsetsSourceProvider provider = mStateController.getSourceProvider(type); 467 InsetsSourceControl control = provider.getControl(mDummyControlTarget); 468 if (control == null || control.getLeash() == null) { 469 continue; 470 } 471 typesReady |= InsetsState.toPublicType(type); 472 controls.put(control.getType(), new InsetsSourceControl(control)); 473 } 474 controlAnimationUnchecked(typesReady, controls, show, callback); 475 } 476 controlAnimationUnchecked(int typesReady, SparseArray<InsetsSourceControl> controls, boolean show, Runnable callback)477 private void controlAnimationUnchecked(int typesReady, 478 SparseArray<InsetsSourceControl> controls, boolean show, Runnable callback) { 479 InsetsPolicyAnimationControlListener listener = 480 new InsetsPolicyAnimationControlListener(show, callback, typesReady); 481 listener.mControlCallbacks.controlAnimationUnchecked(typesReady, controls, show); 482 } 483 484 private class BarWindow { 485 486 private final int mId; 487 private @StatusBarManager.WindowVisibleState int mState = 488 StatusBarManager.WINDOW_STATE_SHOWING; 489 BarWindow(int id)490 BarWindow(int id) { 491 mId = id; 492 } 493 updateVisibility(@ullable InsetsControlTarget controlTarget, @InternalInsetsType int type)494 private void updateVisibility(@Nullable InsetsControlTarget controlTarget, 495 @InternalInsetsType int type) { 496 setVisible(controlTarget == null || controlTarget.getRequestedVisibility(type)); 497 } 498 setVisible(boolean visible)499 private void setVisible(boolean visible) { 500 final int state = visible ? WINDOW_STATE_SHOWING : WINDOW_STATE_HIDDEN; 501 if (mState != state) { 502 mState = state; 503 StatusBarManagerInternal statusBarManagerInternal = 504 mPolicy.getStatusBarManagerInternal(); 505 if (statusBarManagerInternal != null) { 506 statusBarManagerInternal.setWindowState( 507 mDisplayContent.getDisplayId(), mId, state); 508 } 509 } 510 } 511 } 512 513 private class InsetsPolicyAnimationControlListener extends 514 InsetsController.InternalAnimationControlListener { 515 Runnable mFinishCallback; 516 InsetsPolicyAnimationControlCallbacks mControlCallbacks; 517 InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback, int types)518 InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback, int types) { 519 super(show, false /* hasCallbacks */, types, BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE, 520 false /* disable */, 0 /* floatingImeBottomInsets */); 521 mFinishCallback = finishCallback; 522 mControlCallbacks = new InsetsPolicyAnimationControlCallbacks(this); 523 } 524 525 @Override onAnimationFinish()526 protected void onAnimationFinish() { 527 super.onAnimationFinish(); 528 if (mFinishCallback != null) { 529 DisplayThread.getHandler().post(mFinishCallback); 530 } 531 } 532 533 private class InsetsPolicyAnimationControlCallbacks implements 534 InsetsAnimationControlCallbacks { 535 private InsetsAnimationControlImpl mAnimationControl = null; 536 private InsetsPolicyAnimationControlListener mListener; 537 InsetsPolicyAnimationControlCallbacks(InsetsPolicyAnimationControlListener listener)538 InsetsPolicyAnimationControlCallbacks(InsetsPolicyAnimationControlListener listener) { 539 mListener = listener; 540 } 541 controlAnimationUnchecked(int typesReady, SparseArray<InsetsSourceControl> controls, boolean show)542 private void controlAnimationUnchecked(int typesReady, 543 SparseArray<InsetsSourceControl> controls, boolean show) { 544 if (typesReady == 0) { 545 // nothing to animate. 546 return; 547 } 548 mAnimatingShown = show; 549 550 final InsetsState state = getInsetsForWindow(mFocusedWin); 551 552 // We are about to playing the default animation. Passing a null frame indicates 553 // the controlled types should be animated regardless of the frame. 554 mAnimationControl = new InsetsAnimationControlImpl(controls, 555 null /* frame */, state, mListener, typesReady, this, 556 mListener.getDurationMs(), getInsetsInterpolator(), 557 show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE, show 558 ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN 559 : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN, 560 null /* translator */); 561 SurfaceAnimationThread.getHandler().post( 562 () -> mListener.onReady(mAnimationControl, typesReady)); 563 } 564 565 /** Called on SurfaceAnimationThread without global WM lock held. */ 566 @Override scheduleApplyChangeInsets(InsetsAnimationControlRunner runner)567 public void scheduleApplyChangeInsets(InsetsAnimationControlRunner runner) { 568 if (mAnimationControl.applyChangeInsets(null /* outState */)) { 569 mAnimationControl.finish(mAnimatingShown); 570 } 571 } 572 573 @Override notifyFinished(InsetsAnimationControlRunner runner, boolean shown)574 public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) { 575 // Nothing's needed here. Finish steps is handled in the listener 576 // onAnimationFinished callback. 577 } 578 579 /** Called on SurfaceAnimationThread without global WM lock held. */ 580 @Override applySurfaceParams( final SyncRtSurfaceTransactionApplier.SurfaceParams... params)581 public void applySurfaceParams( 582 final SyncRtSurfaceTransactionApplier.SurfaceParams... params) { 583 SurfaceControl.Transaction t = new SurfaceControl.Transaction(); 584 for (int i = params.length - 1; i >= 0; i--) { 585 SyncRtSurfaceTransactionApplier.SurfaceParams surfaceParams = params[i]; 586 applyParams(t, surfaceParams, mTmpFloat9); 587 } 588 t.apply(); 589 t.close(); 590 } 591 592 // Since we don't push applySurfaceParams to a Handler-queue we don't need 593 // to push release in this case. 594 @Override releaseSurfaceControlFromRt(SurfaceControl sc)595 public void releaseSurfaceControlFromRt(SurfaceControl sc) { 596 sc.release(); 597 } 598 599 @Override 600 public <T extends InsetsAnimationControlRunner & InternalInsetsAnimationController> startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types, WindowInsetsAnimation animation, Bounds bounds)601 void startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types, 602 WindowInsetsAnimation animation, 603 Bounds bounds) { 604 } 605 606 @Override reportPerceptible(int types, boolean perceptible)607 public void reportPerceptible(int types, boolean perceptible) { 608 // No-op for now - only client windows report perceptibility for now, with policy 609 // controllers assumed to always be perceptible. 610 } 611 } 612 } 613 } 614