1 /* 2 * Copyright (C) 2018 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.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; 20 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; 21 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; 22 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; 23 import static android.view.InsetsState.ITYPE_CAPTION_BAR; 24 import static android.view.InsetsState.ITYPE_CLIMATE_BAR; 25 import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; 26 import static android.view.InsetsState.ITYPE_IME; 27 import static android.view.InsetsState.ITYPE_INVALID; 28 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; 29 import static android.view.InsetsState.ITYPE_STATUS_BAR; 30 import static android.view.WindowInsets.Type.displayCutout; 31 import static android.view.WindowInsets.Type.mandatorySystemGestures; 32 import static android.view.WindowInsets.Type.systemGestures; 33 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; 34 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; 35 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; 36 37 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IME; 38 39 import android.annotation.NonNull; 40 import android.annotation.Nullable; 41 import android.app.WindowConfiguration; 42 import android.app.WindowConfiguration.WindowingMode; 43 import android.os.Trace; 44 import android.util.ArrayMap; 45 import android.util.ArraySet; 46 import android.util.SparseArray; 47 import android.view.InsetsSource; 48 import android.view.InsetsSourceControl; 49 import android.view.InsetsState; 50 import android.view.InsetsState.InternalInsetsType; 51 import android.view.WindowManager; 52 import android.view.WindowManager.LayoutParams.WindowType; 53 54 import com.android.internal.protolog.common.ProtoLog; 55 import com.android.server.inputmethod.InputMethodManagerInternal; 56 57 import java.io.PrintWriter; 58 import java.util.ArrayList; 59 import java.util.function.Consumer; 60 61 /** 62 * Manages global window inset state in the system represented by {@link InsetsState}. 63 */ 64 class InsetsStateController { 65 66 private final InsetsState mLastState = new InsetsState(); 67 private final InsetsState mState = new InsetsState(); 68 private final DisplayContent mDisplayContent; 69 70 private final ArrayMap<Integer, InsetsSourceProvider> mProviders = new ArrayMap<>(); 71 private final ArrayMap<InsetsControlTarget, ArrayList<Integer>> mControlTargetTypeMap = 72 new ArrayMap<>(); 73 private final SparseArray<InsetsControlTarget> mTypeControlTargetMap = new SparseArray<>(); 74 75 /** @see #onControlFakeTargetChanged */ 76 private final SparseArray<InsetsControlTarget> mTypeFakeControlTargetMap = new SparseArray<>(); 77 78 private final ArraySet<InsetsControlTarget> mPendingControlChanged = new ArraySet<>(); 79 80 private final Consumer<WindowState> mDispatchInsetsChanged = w -> { 81 if (w.isReadyToDispatchInsetsState()) { 82 w.notifyInsetsChanged(); 83 } 84 }; 85 private final InsetsControlTarget mEmptyImeControlTarget = new InsetsControlTarget() { 86 @Override 87 public void notifyInsetsControlChanged() { 88 InsetsSourceControl[] controls = getControlsForDispatch(this); 89 if (controls == null) { 90 return; 91 } 92 for (InsetsSourceControl control : controls) { 93 if (control.getType() == ITYPE_IME) { 94 mDisplayContent.mWmService.mH.post(() -> 95 InputMethodManagerInternal.get().removeImeSurface()); 96 } 97 } 98 } 99 }; 100 InsetsStateController(DisplayContent displayContent)101 InsetsStateController(DisplayContent displayContent) { 102 mDisplayContent = displayContent; 103 } 104 105 /** 106 * Gets the insets state from the perspective of the target. When performing layout of the 107 * target or dispatching insets to the target, we need to exclude sources which should not be 108 * visible to the target. e.g., the source which represents the target window itself, and the 109 * IME source when the target is above IME. We also need to exclude certain types of insets 110 * source for client within specific windowing modes. 111 * This is to get the insets for a window layout on the screen. If the window is not there, use 112 * the {@link #getInsetsForWindowMetrics} to get insets instead. 113 * 114 * @param target The window associate with the perspective. 115 * @return The state stripped of the necessary information. 116 */ getInsetsForWindow(@onNull WindowState target)117 InsetsState getInsetsForWindow(@NonNull WindowState target) { 118 final InsetsState rotatedState = target.mToken.getFixedRotationTransformInsetsState(); 119 if (rotatedState != null) { 120 return rotatedState; 121 } 122 final InsetsSourceProvider provider = target.getControllableInsetProvider(); 123 final @InternalInsetsType int type = provider != null 124 ? provider.getSource().getType() : ITYPE_INVALID; 125 return getInsetsForTarget(type, target.getWindowingMode(), target.isAlwaysOnTop(), 126 target.getFrozenInsetsState() != null ? target.getFrozenInsetsState() : 127 (target.mAttrs.receiveInsetsIgnoringZOrder ? mState : 128 target.mAboveInsetsState)); 129 } 130 getInsetsForWindowMetrics(@onNull WindowManager.LayoutParams attrs)131 InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) { 132 final @InternalInsetsType int type = getInsetsTypeForLayoutParams(attrs); 133 final WindowToken token = mDisplayContent.getWindowToken(attrs.token); 134 if (token != null) { 135 final InsetsState rotatedState = token.getFixedRotationTransformInsetsState(); 136 if (rotatedState != null) { 137 return rotatedState; 138 } 139 } 140 final @WindowingMode int windowingMode = token != null 141 ? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED; 142 final boolean alwaysOnTop = token != null && token.isAlwaysOnTop(); 143 return getInsetsForTarget(type, windowingMode, alwaysOnTop, mState); 144 } 145 146 private static @InternalInsetsType getInsetsTypeForLayoutParams(WindowManager.LayoutParams attrs)147 int getInsetsTypeForLayoutParams(WindowManager.LayoutParams attrs) { 148 @WindowType int type = attrs.type; 149 switch (type) { 150 case TYPE_STATUS_BAR: 151 return ITYPE_STATUS_BAR; 152 case TYPE_NAVIGATION_BAR: 153 return ITYPE_NAVIGATION_BAR; 154 case TYPE_INPUT_METHOD: 155 return ITYPE_IME; 156 } 157 158 // If not one of the types above, check whether an internal inset mapping is specified. 159 if (attrs.providesInsetsTypes != null) { 160 for (@InternalInsetsType int insetsType : attrs.providesInsetsTypes) { 161 switch (insetsType) { 162 case ITYPE_STATUS_BAR: 163 case ITYPE_NAVIGATION_BAR: 164 case ITYPE_CLIMATE_BAR: 165 case ITYPE_EXTRA_NAVIGATION_BAR: 166 return insetsType; 167 } 168 } 169 } 170 171 return ITYPE_INVALID; 172 } 173 174 /** 175 * @see #getInsetsForWindow 176 * @see #getInsetsForWindowMetrics 177 */ getInsetsForTarget(@nternalInsetsType int type, @WindowingMode int windowingMode, boolean isAlwaysOnTop, InsetsState state)178 private InsetsState getInsetsForTarget(@InternalInsetsType int type, 179 @WindowingMode int windowingMode, boolean isAlwaysOnTop, InsetsState state) { 180 boolean stateCopied = false; 181 182 if (type != ITYPE_INVALID) { 183 state = new InsetsState(state); 184 stateCopied = true; 185 state.removeSource(type); 186 187 // Navigation bar doesn't get influenced by anything else 188 if (type == ITYPE_NAVIGATION_BAR || type == ITYPE_EXTRA_NAVIGATION_BAR) { 189 state.removeSource(ITYPE_IME); 190 state.removeSource(ITYPE_STATUS_BAR); 191 state.removeSource(ITYPE_CLIMATE_BAR); 192 state.removeSource(ITYPE_CAPTION_BAR); 193 state.removeSource(ITYPE_NAVIGATION_BAR); 194 state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR); 195 } 196 197 // Status bar doesn't get influenced by caption bar 198 if (type == ITYPE_STATUS_BAR || type == ITYPE_CLIMATE_BAR) { 199 state.removeSource(ITYPE_CAPTION_BAR); 200 } 201 202 // IME needs different frames for certain cases (e.g. navigation bar in gesture nav). 203 if (type == ITYPE_IME) { 204 for (int i = mProviders.size() - 1; i >= 0; i--) { 205 InsetsSourceProvider otherProvider = mProviders.valueAt(i); 206 if (otherProvider.overridesImeFrame()) { 207 InsetsSource override = 208 new InsetsSource( 209 state.getSource(otherProvider.getSource().getType())); 210 override.setFrame(otherProvider.getImeOverrideFrame()); 211 state.addSource(override); 212 } 213 } 214 } 215 } 216 217 if (WindowConfiguration.isFloating(windowingMode) 218 || (windowingMode == WINDOWING_MODE_MULTI_WINDOW && isAlwaysOnTop)) { 219 if (!stateCopied) { 220 state = new InsetsState(state); 221 stateCopied = true; 222 } 223 state.removeSource(ITYPE_STATUS_BAR); 224 state.removeSource(ITYPE_NAVIGATION_BAR); 225 state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR); 226 if (windowingMode == WINDOWING_MODE_PINNED) { 227 state.removeSource(ITYPE_IME); 228 } 229 } 230 231 return state; 232 } 233 getRawInsetsState()234 InsetsState getRawInsetsState() { 235 return mState; 236 } 237 getControlsForDispatch(InsetsControlTarget target)238 @Nullable InsetsSourceControl[] getControlsForDispatch(InsetsControlTarget target) { 239 ArrayList<Integer> controlled = mControlTargetTypeMap.get(target); 240 if (controlled == null) { 241 return null; 242 } 243 final int size = controlled.size(); 244 final InsetsSourceControl[] result = new InsetsSourceControl[size]; 245 for (int i = 0; i < size; i++) { 246 result[i] = mProviders.get(controlled.get(i)).getControl(target); 247 } 248 return result; 249 } 250 addProvidersToTransition()251 public void addProvidersToTransition() { 252 for (int i = mProviders.size() - 1; i >= 0; --i) { 253 final InsetsSourceProvider p = mProviders.valueAt(i); 254 if (p == null) continue; 255 final WindowContainer wc = p.mWin; 256 if (wc == null) continue; 257 mDisplayContent.mAtmService.getTransitionController().collect(wc); 258 } 259 } 260 261 /** 262 * @return The provider of a specific type. 263 */ getSourceProvider(@nternalInsetsType int type)264 InsetsSourceProvider getSourceProvider(@InternalInsetsType int type) { 265 if (type == ITYPE_IME) { 266 return mProviders.computeIfAbsent(type, 267 key -> new ImeInsetsSourceProvider( 268 mState.getSource(key), this, mDisplayContent)); 269 } else { 270 return mProviders.computeIfAbsent(type, 271 key -> new InsetsSourceProvider(mState.getSource(key), this, mDisplayContent)); 272 } 273 } 274 getImeSourceProvider()275 ImeInsetsSourceProvider getImeSourceProvider() { 276 return (ImeInsetsSourceProvider) getSourceProvider(ITYPE_IME); 277 } 278 279 /** 280 * @return The provider of a specific type or null if we don't have it. 281 */ peekSourceProvider(@nternalInsetsType int type)282 @Nullable InsetsSourceProvider peekSourceProvider(@InternalInsetsType int type) { 283 return mProviders.get(type); 284 } 285 286 /** 287 * Called when a layout pass has occurred. 288 */ onPostLayout()289 void onPostLayout() { 290 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "ISC.onPostLayout"); 291 for (int i = mProviders.size() - 1; i >= 0; i--) { 292 mProviders.valueAt(i).onPostLayout(); 293 } 294 final ArrayList<WindowState> winInsetsChanged = mDisplayContent.mWinInsetsChanged; 295 if (!mLastState.equals(mState)) { 296 mLastState.set(mState, true /* copySources */); 297 notifyInsetsChanged(); 298 } else { 299 // The global insets state has not changed but there might be windows whose conditions 300 // (e.g., z-order) have changed. They can affect the insets states that we dispatch to 301 // the clients. 302 for (int i = winInsetsChanged.size() - 1; i >= 0; i--) { 303 mDispatchInsetsChanged.accept(winInsetsChanged.get(i)); 304 } 305 } 306 winInsetsChanged.clear(); 307 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 308 } 309 310 /** 311 * Updates {@link WindowState#mAboveInsetsState} for all windows in the display while the 312 * z-order of a window is changed. 313 * 314 * @param win The window whose z-order has changed. 315 * @param notifyInsetsChange {@code true} if the clients should be notified about the change. 316 */ updateAboveInsetsState(WindowState win, boolean notifyInsetsChange)317 void updateAboveInsetsState(WindowState win, boolean notifyInsetsChange) { 318 if (win == null || win.getDisplayContent() != mDisplayContent) { 319 return; 320 } 321 final boolean[] aboveWin = { true }; 322 final InsetsState aboveInsetsState = new InsetsState(); 323 aboveInsetsState.set(mState, 324 displayCutout() | systemGestures() | mandatorySystemGestures()); 325 final SparseArray<InsetsSource> winProvidedSources = win.mProvidedInsetsSources; 326 final ArrayList<WindowState> insetsChangedWindows = new ArrayList<>(); 327 mDisplayContent.forAllWindows(w -> { 328 if (aboveWin[0]) { 329 if (w == win) { 330 aboveWin[0] = false; 331 if (!win.mAboveInsetsState.equals(aboveInsetsState)) { 332 win.mAboveInsetsState.set(aboveInsetsState); 333 insetsChangedWindows.add(win); 334 } 335 return winProvidedSources.size() == 0; 336 } else { 337 final SparseArray<InsetsSource> providedSources = w.mProvidedInsetsSources; 338 for (int i = providedSources.size() - 1; i >= 0; i--) { 339 aboveInsetsState.addSource(providedSources.valueAt(i)); 340 } 341 if (winProvidedSources.size() == 0) { 342 return false; 343 } 344 boolean changed = false; 345 for (int i = winProvidedSources.size() - 1; i >= 0; i--) { 346 changed |= w.mAboveInsetsState.removeSource(winProvidedSources.keyAt(i)); 347 } 348 if (changed) { 349 insetsChangedWindows.add(w); 350 } 351 } 352 } else { 353 for (int i = winProvidedSources.size() - 1; i >= 0; i--) { 354 w.mAboveInsetsState.addSource(winProvidedSources.valueAt(i)); 355 } 356 insetsChangedWindows.add(w); 357 } 358 return false; 359 }, true /* traverseTopToBottom */); 360 if (notifyInsetsChange) { 361 for (int i = insetsChangedWindows.size() - 1; i >= 0; i--) { 362 mDispatchInsetsChanged.accept(insetsChangedWindows.get(i)); 363 } 364 } 365 } 366 onDisplayInfoUpdated(boolean notifyInsetsChange)367 void onDisplayInfoUpdated(boolean notifyInsetsChange) { 368 final ArrayList<WindowState> insetsChangedWindows = new ArrayList<>(); 369 mDisplayContent.forAllWindows(w -> { 370 w.mAboveInsetsState.set(mState, displayCutout()); 371 insetsChangedWindows.add(w); 372 }, true /* traverseTopToBottom */); 373 if (notifyInsetsChange) { 374 for (int i = insetsChangedWindows.size() - 1; i >= 0; i--) { 375 mDispatchInsetsChanged.accept(insetsChangedWindows.get(i)); 376 } 377 } 378 } 379 onInsetsModified(InsetsControlTarget caller)380 void onInsetsModified(InsetsControlTarget caller) { 381 boolean changed = false; 382 for (int i = mProviders.size() - 1; i >= 0; i--) { 383 changed |= mProviders.valueAt(i).updateClientVisibility(caller); 384 } 385 if (changed) { 386 notifyInsetsChanged(); 387 mDisplayContent.updateSystemGestureExclusion(); 388 mDisplayContent.getDisplayPolicy().updateSystemUiVisibilityLw(); 389 } 390 } 391 392 /** 393 * Computes insets state of the insets provider window in the display frames. 394 * 395 * @param win The owner window of insets provider. 396 * @param displayFrames The display frames to create insets source. 397 * @param windowFrames The specified frames to represent the owner window. 398 */ computeSimulatedState(WindowState win, DisplayFrames displayFrames, WindowFrames windowFrames)399 void computeSimulatedState(WindowState win, DisplayFrames displayFrames, 400 WindowFrames windowFrames) { 401 final InsetsState state = displayFrames.mInsetsState; 402 for (int i = mProviders.size() - 1; i >= 0; i--) { 403 final InsetsSourceProvider provider = mProviders.valueAt(i); 404 if (provider.mWin == win) { 405 state.addSource(provider.createSimulatedSource(displayFrames, windowFrames)); 406 } 407 } 408 } 409 isFakeTarget(@nternalInsetsType int type, InsetsControlTarget target)410 boolean isFakeTarget(@InternalInsetsType int type, InsetsControlTarget target) { 411 return mTypeFakeControlTargetMap.get(type) == target; 412 } 413 onImeControlTargetChanged(@ullable InsetsControlTarget imeTarget)414 void onImeControlTargetChanged(@Nullable InsetsControlTarget imeTarget) { 415 416 // Make sure that we always have a control target for the IME, even if the IME target is 417 // null. Otherwise there is no leash that will hide it and IME becomes "randomly" visible. 418 InsetsControlTarget target = imeTarget != null ? imeTarget : mEmptyImeControlTarget; 419 onControlChanged(ITYPE_IME, target); 420 ProtoLog.d(WM_DEBUG_IME, "onImeControlTargetChanged %s", 421 target != null ? target.getWindow() : "null"); 422 notifyPendingInsetsControlChanged(); 423 } 424 425 /** 426 * Called when the focused window that is able to control the system bars changes. 427 * 428 * @param statusControlling The target that is now able to control the status bar appearance 429 * and visibility. 430 * @param navControlling The target that is now able to control the nav bar appearance 431 * and visibility. 432 */ onBarControlTargetChanged(@ullable InsetsControlTarget statusControlling, @Nullable InsetsControlTarget fakeStatusControlling, @Nullable InsetsControlTarget navControlling, @Nullable InsetsControlTarget fakeNavControlling)433 void onBarControlTargetChanged(@Nullable InsetsControlTarget statusControlling, 434 @Nullable InsetsControlTarget fakeStatusControlling, 435 @Nullable InsetsControlTarget navControlling, 436 @Nullable InsetsControlTarget fakeNavControlling) { 437 onControlChanged(ITYPE_STATUS_BAR, statusControlling); 438 onControlChanged(ITYPE_NAVIGATION_BAR, navControlling); 439 onControlChanged(ITYPE_CLIMATE_BAR, statusControlling); 440 onControlChanged(ITYPE_EXTRA_NAVIGATION_BAR, navControlling); 441 onControlFakeTargetChanged(ITYPE_STATUS_BAR, fakeStatusControlling); 442 onControlFakeTargetChanged(ITYPE_NAVIGATION_BAR, fakeNavControlling); 443 onControlFakeTargetChanged(ITYPE_CLIMATE_BAR, fakeStatusControlling); 444 onControlFakeTargetChanged(ITYPE_EXTRA_NAVIGATION_BAR, fakeNavControlling); 445 notifyPendingInsetsControlChanged(); 446 } 447 notifyControlRevoked(@onNull InsetsControlTarget previousControlTarget, InsetsSourceProvider provider)448 void notifyControlRevoked(@NonNull InsetsControlTarget previousControlTarget, 449 InsetsSourceProvider provider) { 450 removeFromControlMaps(previousControlTarget, provider.getSource().getType(), 451 false /* fake */); 452 } 453 onControlChanged(@nternalInsetsType int type, @Nullable InsetsControlTarget target)454 private void onControlChanged(@InternalInsetsType int type, 455 @Nullable InsetsControlTarget target) { 456 final InsetsControlTarget previous = mTypeControlTargetMap.get(type); 457 if (target == previous) { 458 return; 459 } 460 final InsetsSourceProvider provider = mProviders.get(type); 461 if (provider == null) { 462 return; 463 } 464 if (!provider.isControllable()) { 465 return; 466 } 467 provider.updateControlForTarget(target, false /* force */); 468 target = provider.getControlTarget(); 469 if (previous != null) { 470 removeFromControlMaps(previous, type, false /* fake */); 471 mPendingControlChanged.add(previous); 472 } 473 if (target != null) { 474 addToControlMaps(target, type, false /* fake */); 475 mPendingControlChanged.add(target); 476 } 477 } 478 479 /** 480 * The fake target saved here will be used to pretend to the app that it's still under control 481 * of the bars while it's not really, but we still need to find out the apps intentions around 482 * showing/hiding. For example, when the transient bars are showing, and the fake target 483 * requests to show system bars, the transient state will be aborted. 484 */ onControlFakeTargetChanged(@nternalInsetsType int type, @Nullable InsetsControlTarget fakeTarget)485 void onControlFakeTargetChanged(@InternalInsetsType int type, 486 @Nullable InsetsControlTarget fakeTarget) { 487 final InsetsControlTarget previous = mTypeFakeControlTargetMap.get(type); 488 if (fakeTarget == previous) { 489 return; 490 } 491 final InsetsSourceProvider provider = mProviders.get(type); 492 if (provider == null) { 493 return; 494 } 495 provider.updateControlForFakeTarget(fakeTarget); 496 if (previous != null) { 497 removeFromControlMaps(previous, type, true /* fake */); 498 mPendingControlChanged.add(previous); 499 } 500 if (fakeTarget != null) { 501 addToControlMaps(fakeTarget, type, true /* fake */); 502 mPendingControlChanged.add(fakeTarget); 503 } 504 } 505 removeFromControlMaps(@onNull InsetsControlTarget target, @InternalInsetsType int type, boolean fake)506 private void removeFromControlMaps(@NonNull InsetsControlTarget target, 507 @InternalInsetsType int type, boolean fake) { 508 final ArrayList<Integer> array = mControlTargetTypeMap.get(target); 509 if (array == null) { 510 return; 511 } 512 array.remove((Integer) type); 513 if (array.isEmpty()) { 514 mControlTargetTypeMap.remove(target); 515 } 516 if (fake) { 517 mTypeFakeControlTargetMap.remove(type); 518 } else { 519 mTypeControlTargetMap.remove(type); 520 } 521 } 522 addToControlMaps(@onNull InsetsControlTarget target, @InternalInsetsType int type, boolean fake)523 private void addToControlMaps(@NonNull InsetsControlTarget target, 524 @InternalInsetsType int type, boolean fake) { 525 final ArrayList<Integer> array = mControlTargetTypeMap.computeIfAbsent(target, 526 key -> new ArrayList<>()); 527 array.add(type); 528 if (fake) { 529 mTypeFakeControlTargetMap.put(type, target); 530 } else { 531 mTypeControlTargetMap.put(type, target); 532 } 533 } 534 notifyControlChanged(InsetsControlTarget target)535 void notifyControlChanged(InsetsControlTarget target) { 536 mPendingControlChanged.add(target); 537 notifyPendingInsetsControlChanged(); 538 } 539 notifyPendingInsetsControlChanged()540 private void notifyPendingInsetsControlChanged() { 541 if (mPendingControlChanged.isEmpty()) { 542 return; 543 } 544 mDisplayContent.mWmService.mAnimator.addAfterPrepareSurfacesRunnable(() -> { 545 for (int i = mProviders.size() - 1; i >= 0; i--) { 546 final InsetsSourceProvider provider = mProviders.valueAt(i); 547 provider.onSurfaceTransactionApplied(); 548 } 549 final ArraySet<InsetsControlTarget> newControlTargets = new ArraySet<>(); 550 for (int i = mPendingControlChanged.size() - 1; i >= 0; i--) { 551 final InsetsControlTarget controlTarget = mPendingControlChanged.valueAt(i); 552 controlTarget.notifyInsetsControlChanged(); 553 if (mControlTargetTypeMap.containsKey(controlTarget)) { 554 // We only collect targets who get controls, not lose controls. 555 newControlTargets.add(controlTarget); 556 } 557 } 558 mPendingControlChanged.clear(); 559 560 // This updates the insets visibilities AFTER sending current insets state and controls 561 // to the clients, so that the clients can change the current visibilities to the 562 // requested visibilities with animations. 563 for (int i = newControlTargets.size() - 1; i >= 0; i--) { 564 onInsetsModified(newControlTargets.valueAt(i)); 565 } 566 newControlTargets.clear(); 567 }); 568 } 569 notifyInsetsChanged()570 void notifyInsetsChanged() { 571 mDisplayContent.notifyInsetsChanged(mDispatchInsetsChanged); 572 } 573 dump(String prefix, PrintWriter pw)574 void dump(String prefix, PrintWriter pw) { 575 pw.println(prefix + "WindowInsetsStateController"); 576 prefix = prefix + " "; 577 mState.dump(prefix, pw); 578 pw.println(prefix + "Control map:"); 579 for (int i = mTypeControlTargetMap.size() - 1; i >= 0; i--) { 580 pw.print(prefix + " "); 581 pw.println(InsetsState.typeToString(mTypeControlTargetMap.keyAt(i)) + " -> " 582 + mTypeControlTargetMap.valueAt(i)); 583 } 584 pw.println(prefix + "InsetsSourceProviders:"); 585 for (int i = mProviders.size() - 1; i >= 0; i--) { 586 mProviders.valueAt(i).dump(pw, prefix + " "); 587 } 588 } 589 } 590