1 /* 2 * Copyright (C) 2022 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.windowdecor; 18 19 import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED; 20 import static android.view.WindowInsets.Type.captionBar; 21 import static android.view.WindowInsets.Type.mandatorySystemGestures; 22 import static android.view.WindowInsets.Type.statusBars; 23 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 24 import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; 25 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; 26 27 import android.annotation.NonNull; 28 import android.annotation.Nullable; 29 import android.app.ActivityManager.RunningTaskInfo; 30 import android.app.WindowConfiguration.WindowingMode; 31 import android.content.Context; 32 import android.content.res.Configuration; 33 import android.content.res.Resources; 34 import android.graphics.Color; 35 import android.graphics.PixelFormat; 36 import android.graphics.Point; 37 import android.graphics.Rect; 38 import android.graphics.Region; 39 import android.os.Binder; 40 import android.os.Trace; 41 import android.view.Display; 42 import android.view.InsetsSource; 43 import android.view.InsetsState; 44 import android.view.LayoutInflater; 45 import android.view.SurfaceControl; 46 import android.view.SurfaceControlViewHost; 47 import android.view.View; 48 import android.view.WindowManager; 49 import android.view.WindowlessWindowManager; 50 import android.window.DesktopExperienceFlags; 51 import android.window.DesktopModeFlags; 52 import android.window.SurfaceSyncGroup; 53 import android.window.TaskConstants; 54 import android.window.WindowContainerToken; 55 import android.window.WindowContainerTransaction; 56 57 import com.android.internal.annotations.VisibleForTesting; 58 import com.android.wm.shell.ShellTaskOrganizer; 59 import com.android.wm.shell.common.DisplayController; 60 import com.android.wm.shell.desktopmode.DesktopModeEventLogger; 61 import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams.OccludingCaptionElement; 62 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer; 63 import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost; 64 import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier; 65 import com.android.wm.shell.windowdecor.extension.InsetsStateKt; 66 67 import java.util.ArrayList; 68 import java.util.Arrays; 69 import java.util.List; 70 import java.util.Objects; 71 import java.util.function.Supplier; 72 73 /** 74 * Manages a container surface and a windowless window to show window decoration. Responsible to 75 * update window decoration window state and layout parameters on task info changes and so that 76 * window decoration is in correct state and bounds. 77 * 78 * The container surface is a child of the task display area in the same display, so that window 79 * decorations can be drawn out of the task bounds and receive input events from out of the task 80 * bounds to support drag resizing. 81 * 82 * The windowless window that hosts window decoration is positioned in front of all activities, to 83 * allow the foreground activity to draw its own background behind window decorations, such as 84 * the window captions. 85 * 86 * @param <T> The type of the root view 87 */ 88 public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> 89 implements AutoCloseable { 90 91 /** 92 * The Z-order of the caption surface. 93 * <p> 94 * We use {@link #mDecorationContainerSurface} to define input window for task resizing; by 95 * layering it in front of the caption surface, we can allow it to handle input 96 * prior to caption view itself, treating corner inputs as resize events rather than 97 * repositioning. 98 */ 99 static final int CAPTION_LAYER_Z_ORDER = -1; 100 /** 101 * The Z-order of the task input sink in {@link DragPositioningCallback}. 102 * <p> 103 * This task input sink is used to prevent undesired dispatching of motion events out of task 104 * bounds; by layering it behind the caption surface, we allow captions to handle 105 * input events first. 106 */ 107 static final int INPUT_SINK_Z_ORDER = -2; 108 109 /** 110 * Invalid corner radius that signifies that corner radius should not be set. 111 */ 112 static final int INVALID_CORNER_RADIUS = -1; 113 /** 114 * Invalid corner radius that signifies that shadow radius should not be set. 115 */ 116 static final int INVALID_SHADOW_RADIUS = -1; 117 118 /** 119 * System-wide context. Only used to create context with overridden configurations. 120 */ 121 final Context mContext; 122 final @NonNull Context mUserContext; 123 final @NonNull DisplayController mDisplayController; 124 final @NonNull DesktopModeEventLogger mDesktopModeEventLogger; 125 final ShellTaskOrganizer mTaskOrganizer; 126 final Supplier<SurfaceControl.Builder> mSurfaceControlBuilderSupplier; 127 final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier; 128 final Supplier<WindowContainerTransaction> mWindowContainerTransactionSupplier; 129 final SurfaceControlViewHostFactory mSurfaceControlViewHostFactory; 130 @NonNull private final WindowDecorViewHostSupplier<WindowDecorViewHost> 131 mWindowDecorViewHostSupplier; 132 private final DisplayController.OnDisplaysChangedListener mOnDisplaysChangedListener = 133 new DisplayController.OnDisplaysChangedListener() { 134 @Override 135 public void onDisplayAdded(int displayId) { 136 if (mTaskInfo.displayId != displayId) { 137 return; 138 } 139 140 mDisplayController.removeDisplayWindowListener(this); 141 relayout(mTaskInfo, mHasGlobalFocus, mExclusionRegion); 142 } 143 }; 144 145 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 146 public RunningTaskInfo mTaskInfo; 147 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 148 public Context mDecorWindowContext; 149 int mLayoutResId; 150 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 151 public final SurfaceControl mTaskSurface; 152 153 Display mDisplay; 154 SurfaceControl mDecorationContainerSurface; 155 156 private WindowDecorViewHost mViewHost; 157 private Configuration mWindowDecorConfig; 158 TaskDragResizer mTaskDragResizer; 159 boolean mIsCaptionVisible; 160 161 boolean mIsStatusBarVisible; 162 boolean mIsKeyguardVisibleAndOccluded; 163 boolean mHasGlobalFocus; 164 final Region mExclusionRegion = Region.obtain(); 165 166 /** The most recent set of insets applied to this window decoration. */ 167 private WindowDecorationInsets mWindowDecorationInsets; 168 private final Binder mOwner = new Binder(); 169 private final float[] mTmpColor = new float[3]; 170 WindowDecoration( Context context, @NonNull Context userContext, DisplayController displayController, ShellTaskOrganizer taskOrganizer, RunningTaskInfo taskInfo, SurfaceControl taskSurface, @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier)171 WindowDecoration( 172 Context context, 173 @NonNull Context userContext, 174 DisplayController displayController, 175 ShellTaskOrganizer taskOrganizer, 176 RunningTaskInfo taskInfo, 177 SurfaceControl taskSurface, 178 @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier) { 179 this(context, userContext, displayController, taskOrganizer, taskInfo, 180 taskSurface, SurfaceControl.Builder::new, SurfaceControl.Transaction::new, 181 WindowContainerTransaction::new, SurfaceControl::new, 182 new SurfaceControlViewHostFactory() {}, windowDecorViewHostSupplier, 183 new DesktopModeEventLogger()); 184 } 185 WindowDecoration( Context context, @NonNull Context userContext, @NonNull DisplayController displayController, ShellTaskOrganizer taskOrganizer, RunningTaskInfo taskInfo, @NonNull SurfaceControl taskSurface, Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier, Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier, Supplier<WindowContainerTransaction> windowContainerTransactionSupplier, Supplier<SurfaceControl> surfaceControlSupplier, SurfaceControlViewHostFactory surfaceControlViewHostFactory, @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier, @NonNull DesktopModeEventLogger desktopModeEventLogger )186 WindowDecoration( 187 Context context, 188 @NonNull Context userContext, 189 @NonNull DisplayController displayController, 190 ShellTaskOrganizer taskOrganizer, 191 RunningTaskInfo taskInfo, 192 @NonNull SurfaceControl taskSurface, 193 Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier, 194 Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier, 195 Supplier<WindowContainerTransaction> windowContainerTransactionSupplier, 196 Supplier<SurfaceControl> surfaceControlSupplier, 197 SurfaceControlViewHostFactory surfaceControlViewHostFactory, 198 @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier, 199 @NonNull DesktopModeEventLogger desktopModeEventLogger 200 ) { 201 mContext = context; 202 mUserContext = userContext; 203 mDisplayController = displayController; 204 mTaskOrganizer = taskOrganizer; 205 mTaskInfo = taskInfo; 206 mTaskSurface = cloneSurfaceControl(taskSurface, surfaceControlSupplier); 207 mDesktopModeEventLogger = desktopModeEventLogger; 208 mSurfaceControlBuilderSupplier = surfaceControlBuilderSupplier; 209 mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier; 210 mWindowContainerTransactionSupplier = windowContainerTransactionSupplier; 211 mSurfaceControlViewHostFactory = surfaceControlViewHostFactory; 212 mWindowDecorViewHostSupplier = windowDecorViewHostSupplier; 213 mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId); 214 final InsetsState insetsState = mDisplayController.getInsetsState(mTaskInfo.displayId); 215 mIsStatusBarVisible = insetsState != null 216 && InsetsStateKt.isVisible(insetsState, statusBars()); 217 } 218 219 /** 220 * Gets the decoration's task leash. 221 * @return the decoration' task surface used to manipulate the task. 222 */ getLeash()223 public SurfaceControl getLeash() { 224 return mTaskSurface; 225 } 226 227 /** 228 * Used by {@link WindowDecoration} to trigger a new relayout because the requirements for a 229 * relayout weren't satisfied are satisfied now. 230 * 231 * @param taskInfo The previous {@link RunningTaskInfo} passed into {@link #relayout} or the 232 * constructor. 233 * @param hasGlobalFocus Whether the task is focused 234 */ relayout(RunningTaskInfo taskInfo, boolean hasGlobalFocus, @NonNull Region displayExclusionRegion)235 abstract void relayout(RunningTaskInfo taskInfo, boolean hasGlobalFocus, 236 @NonNull Region displayExclusionRegion); 237 238 /** 239 * Used by the {@link DragPositioningCallback} associated with the implementing class to 240 * enforce drags ending in a valid position. A null result means no restriction. 241 */ 242 @Nullable calculateValidDragArea()243 abstract Rect calculateValidDragArea(); 244 relayout(RelayoutParams params, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, WindowContainerTransaction wct, T rootView, RelayoutResult<T> outResult)245 void relayout(RelayoutParams params, SurfaceControl.Transaction startT, 246 SurfaceControl.Transaction finishT, WindowContainerTransaction wct, T rootView, 247 RelayoutResult<T> outResult) { 248 Trace.beginSection("WindowDecoration#relayout"); 249 outResult.reset(); 250 if (params.mRunningTaskInfo != null) { 251 mTaskInfo = params.mRunningTaskInfo; 252 } 253 mHasGlobalFocus = params.mHasGlobalFocus; 254 mExclusionRegion.set(params.mDisplayExclusionRegion); 255 final int oldLayoutResId = mLayoutResId; 256 mLayoutResId = params.mLayoutResId; 257 258 if (!mTaskInfo.isVisible) { 259 releaseViews(wct); 260 if (params.mSetTaskVisibilityPositionAndCrop) { 261 finishT.hide(mTaskSurface); 262 } 263 Trace.endSection(); // WindowDecoration#relayout 264 return; 265 } 266 Trace.beginSection("WindowDecoration#relayout-inflateIfNeeded"); 267 inflateIfNeeded(params, wct, rootView, oldLayoutResId, outResult); 268 Trace.endSection(); 269 final boolean hasCaptionView = outResult.mRootView != null; 270 if (!hasCaptionView) { 271 Trace.endSection(); // WindowDecoration#relayout 272 return; 273 } 274 275 Trace.beginSection("WindowDecoration#relayout-updateCaptionVisibility"); 276 updateCaptionVisibility(outResult.mRootView, params); 277 Trace.endSection(); 278 279 final Rect taskBounds = mTaskInfo.getConfiguration().windowConfiguration.getBounds(); 280 outResult.mWidth = taskBounds.width(); 281 outResult.mHeight = taskBounds.height(); 282 outResult.mRootView.setTaskFocusState(mHasGlobalFocus); 283 final Resources resources = mDecorWindowContext.getResources(); 284 outResult.mCaptionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId) 285 + params.mCaptionTopPadding; 286 outResult.mCaptionWidth = params.mCaptionWidthId != Resources.ID_NULL 287 ? loadDimensionPixelSize(resources, params.mCaptionWidthId) : taskBounds.width(); 288 outResult.mCaptionX = (outResult.mWidth - outResult.mCaptionWidth) / 2; 289 outResult.mCaptionY = 0; 290 outResult.mCaptionTopPadding = params.mCaptionTopPadding; 291 if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) { 292 outResult.mCornerRadius = params.mCornerRadiusId == Resources.ID_NULL 293 ? INVALID_CORNER_RADIUS : loadDimensionPixelSize(resources, 294 params.mCornerRadiusId); 295 outResult.mShadowRadius = params.mShadowRadiusId == Resources.ID_NULL 296 ? INVALID_SHADOW_RADIUS : loadDimensionPixelSize(resources, 297 params.mShadowRadiusId); 298 } 299 300 Trace.beginSection("relayout-createViewHostIfNeeded"); 301 createViewHostIfNeeded(mDecorWindowContext, mDisplay); 302 Trace.endSection(); 303 304 Trace.beginSection("WindowDecoration#relayout-updateSurfacesAndInsets"); 305 final SurfaceControl captionSurface = mViewHost.getSurfaceControl(); 306 updateDecorationContainerSurface(startT, outResult); 307 updateCaptionContainerSurface(captionSurface, startT, outResult); 308 updateCaptionInsets(params, wct, outResult, taskBounds); 309 updateTaskSurface(params, startT, finishT, outResult); 310 Trace.endSection(); 311 312 Trace.beginSection("WindowDecoration#relayout-updateViewHost"); 313 outResult.mRootView.setPadding( 314 outResult.mRootView.getPaddingLeft(), 315 params.mCaptionTopPadding, 316 outResult.mRootView.getPaddingRight(), 317 outResult.mRootView.getPaddingBottom()); 318 final Rect localCaptionBounds = new Rect( 319 outResult.mCaptionX, 320 outResult.mCaptionY, 321 outResult.mCaptionX + outResult.mCaptionWidth, 322 outResult.mCaptionY + outResult.mCaptionHeight); 323 final Region touchableRegion = params.mLimitTouchRegionToSystemAreas 324 ? calculateLimitedTouchableRegion(params, localCaptionBounds) 325 : null; 326 updateViewHierarchy(params, outResult, startT, touchableRegion); 327 Trace.endSection(); 328 329 Trace.endSection(); // WindowDecoration#relayout 330 } 331 createViewHostIfNeeded(@onNull Context context, @NonNull Display display)332 private void createViewHostIfNeeded(@NonNull Context context, @NonNull Display display) { 333 if (mViewHost == null) { 334 mViewHost = mWindowDecorViewHostSupplier.acquire(context, display); 335 } 336 } 337 updateViewHierarchy(@onNull RelayoutParams params, @NonNull RelayoutResult<T> outResult, @NonNull SurfaceControl.Transaction startT, @Nullable Region touchableRegion)338 private void updateViewHierarchy(@NonNull RelayoutParams params, 339 @NonNull RelayoutResult<T> outResult, @NonNull SurfaceControl.Transaction startT, 340 @Nullable Region touchableRegion) { 341 Trace.beginSection("WindowDecoration#updateViewHierarchy"); 342 final WindowManager.LayoutParams lp = 343 new WindowManager.LayoutParams( 344 outResult.mCaptionWidth, 345 outResult.mCaptionHeight, 346 TYPE_APPLICATION, 347 FLAG_NOT_FOCUSABLE, 348 PixelFormat.TRANSPARENT); 349 lp.setTitle("Caption of Task=" + mTaskInfo.taskId); 350 lp.setTrustedOverlay(); 351 lp.inputFeatures = params.mInputFeatures; 352 if (params.mAsyncViewHost) { 353 if (params.mApplyStartTransactionOnDraw) { 354 throw new IllegalArgumentException("Cannot use sync draw tx with async relayout"); 355 } 356 mViewHost.updateViewAsync(outResult.mRootView, lp, mTaskInfo.configuration, 357 touchableRegion); 358 } else { 359 mViewHost.updateView(outResult.mRootView, lp, mTaskInfo.configuration, 360 touchableRegion, params.mApplyStartTransactionOnDraw ? startT : null); 361 } 362 Trace.endSection(); 363 } 364 inflateIfNeeded(RelayoutParams params, WindowContainerTransaction wct, T rootView, int oldLayoutResId, RelayoutResult<T> outResult)365 private void inflateIfNeeded(RelayoutParams params, WindowContainerTransaction wct, 366 T rootView, int oldLayoutResId, RelayoutResult<T> outResult) { 367 if (rootView == null && params.mLayoutResId == 0) { 368 throw new IllegalArgumentException("layoutResId and rootView can't both be invalid."); 369 } 370 371 outResult.mRootView = rootView; 372 final boolean fontScaleChanged = mWindowDecorConfig != null 373 && mWindowDecorConfig.fontScale != mTaskInfo.configuration.fontScale; 374 final boolean localeListChanged = mWindowDecorConfig != null 375 && !mWindowDecorConfig.getLocales() 376 .equals(mTaskInfo.getConfiguration().getLocales()); 377 final int oldDensityDpi = mWindowDecorConfig != null 378 ? mWindowDecorConfig.densityDpi : DENSITY_DPI_UNDEFINED; 379 final int oldNightMode = mWindowDecorConfig != null 380 ? (mWindowDecorConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK) 381 : Configuration.UI_MODE_NIGHT_UNDEFINED; 382 mWindowDecorConfig = params.mWindowDecorConfig != null ? params.mWindowDecorConfig 383 : mTaskInfo.getConfiguration(); 384 final int newDensityDpi = mWindowDecorConfig.densityDpi; 385 final int newNightMode = mWindowDecorConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK; 386 if (oldDensityDpi != newDensityDpi 387 || mDisplay == null 388 || mDisplay.getDisplayId() != mTaskInfo.displayId 389 || oldLayoutResId != mLayoutResId 390 || oldNightMode != newNightMode 391 || mDecorWindowContext == null 392 || fontScaleChanged 393 || localeListChanged) { 394 releaseViews(wct); 395 396 if (!obtainDisplayOrRegisterListener()) { 397 outResult.mRootView = null; 398 return; 399 } 400 mDecorWindowContext = mContext.createConfigurationContext(mWindowDecorConfig); 401 mDecorWindowContext.setTheme(mContext.getThemeResId()); 402 if (params.mLayoutResId != 0) { 403 outResult.mRootView = inflateLayout(mDecorWindowContext, params.mLayoutResId); 404 } 405 } 406 407 if (outResult.mRootView == null) { 408 outResult.mRootView = inflateLayout(mDecorWindowContext, params.mLayoutResId); 409 } 410 } 411 412 @VisibleForTesting inflateLayout(Context context, int layoutResId)413 T inflateLayout(Context context, int layoutResId) { 414 return (T) LayoutInflater.from(context).inflate(layoutResId, null); 415 } 416 updateDecorationContainerSurface( SurfaceControl.Transaction startT, RelayoutResult<T> outResult)417 private void updateDecorationContainerSurface( 418 SurfaceControl.Transaction startT, RelayoutResult<T> outResult) { 419 if (mDecorationContainerSurface == null) { 420 final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get(); 421 mDecorationContainerSurface = builder 422 .setName("Decor container of Task=" + mTaskInfo.taskId) 423 .setContainerLayer() 424 .setParent(mTaskSurface) 425 .setCallsite("WindowDecoration.updateDecorationContainerSurface") 426 .build(); 427 428 startT.setTrustedOverlay(mDecorationContainerSurface, true) 429 .setLayer(mDecorationContainerSurface, 430 TaskConstants.TASK_CHILD_LAYER_WINDOW_DECORATIONS); 431 } 432 433 startT.setWindowCrop(mDecorationContainerSurface, outResult.mWidth, outResult.mHeight) 434 .show(mDecorationContainerSurface); 435 } 436 updateCaptionContainerSurface(@onNull SurfaceControl captionSurface, SurfaceControl.Transaction startT, RelayoutResult<T> outResult)437 private void updateCaptionContainerSurface(@NonNull SurfaceControl captionSurface, 438 SurfaceControl.Transaction startT, RelayoutResult<T> outResult) { 439 startT.reparent(captionSurface, mDecorationContainerSurface) 440 .setWindowCrop(captionSurface, outResult.mCaptionWidth, outResult.mCaptionHeight) 441 .setPosition(captionSurface, outResult.mCaptionX, 0 /* y */) 442 .setLayer(captionSurface, CAPTION_LAYER_Z_ORDER) 443 .show(captionSurface); 444 } 445 updateCaptionInsets(RelayoutParams params, WindowContainerTransaction wct, RelayoutResult<T> outResult, Rect taskBounds)446 private void updateCaptionInsets(RelayoutParams params, WindowContainerTransaction wct, 447 RelayoutResult<T> outResult, Rect taskBounds) { 448 if (!mIsCaptionVisible || !params.mIsInsetSource) { 449 if (mWindowDecorationInsets != null) { 450 mWindowDecorationInsets.remove(wct); 451 mWindowDecorationInsets = null; 452 } 453 return; 454 } 455 // Caption inset is the full width of the task with the |captionHeight| and 456 // positioned at the top of the task bounds, also in absolute coordinates. 457 // So just reuse the task bounds and adjust the bottom coordinate. 458 final Rect captionInsetsRect = new Rect(taskBounds); 459 captionInsetsRect.bottom = captionInsetsRect.top + outResult.mCaptionHeight; 460 461 // Caption bounding rectangles: these are optional, and are used to present finer 462 // insets than traditional |Insets| to apps about where their content is occluded. 463 // These are also in absolute coordinates. 464 final Rect[] boundingRects; 465 final int numOfElements = params.mOccludingCaptionElements.size(); 466 if (numOfElements == 0) { 467 boundingRects = null; 468 } else { 469 // The customizable region can at most be equal to the caption bar. 470 if (params.hasInputFeatureSpy()) { 471 outResult.mCustomizableCaptionRegion.set(captionInsetsRect); 472 } 473 final Resources resources = mDecorWindowContext.getResources(); 474 boundingRects = new Rect[numOfElements]; 475 for (int i = 0; i < numOfElements; i++) { 476 final OccludingCaptionElement element = 477 params.mOccludingCaptionElements.get(i); 478 final int elementWidthPx = 479 resources.getDimensionPixelSize(element.mWidthResId); 480 boundingRects[i] = 481 calculateBoundingRectLocal(element, elementWidthPx, captionInsetsRect); 482 // Subtract the regions used by the caption elements, the rest is 483 // customizable. 484 if (params.hasInputFeatureSpy()) { 485 outResult.mCustomizableCaptionRegion.op(boundingRects[i], 486 Region.Op.DIFFERENCE); 487 } 488 } 489 } 490 491 final WindowDecorationInsets newInsets = new WindowDecorationInsets( 492 mTaskInfo.token, mOwner, captionInsetsRect, taskBounds, boundingRects, 493 params.mInsetSourceFlags, params.mIsInsetSource, params.mShouldSetAppBounds); 494 if (!newInsets.equals(mWindowDecorationInsets)) { 495 // Add or update this caption as an insets source. 496 mWindowDecorationInsets = newInsets; 497 mWindowDecorationInsets.update(wct); 498 } 499 } 500 updateTaskSurface(RelayoutParams params, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, RelayoutResult<T> outResult)501 private void updateTaskSurface(RelayoutParams params, SurfaceControl.Transaction startT, 502 SurfaceControl.Transaction finishT, RelayoutResult<T> outResult) { 503 if (params.mSetTaskVisibilityPositionAndCrop) { 504 final Point taskPosition = mTaskInfo.positionInParent; 505 startT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight); 506 finishT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight) 507 .setPosition(mTaskSurface, taskPosition.x, taskPosition.y); 508 } 509 510 if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) { 511 if (outResult.mShadowRadius != INVALID_SHADOW_RADIUS) { 512 startT.setShadowRadius(mTaskSurface, outResult.mShadowRadius); 513 finishT.setShadowRadius(mTaskSurface, outResult.mShadowRadius); 514 } 515 } else { 516 if (params.mShadowRadius != INVALID_SHADOW_RADIUS) { 517 startT.setShadowRadius(mTaskSurface, params.mShadowRadius); 518 finishT.setShadowRadius(mTaskSurface, params.mShadowRadius); 519 } 520 } 521 522 if (params.mSetTaskVisibilityPositionAndCrop) { 523 startT.show(mTaskSurface); 524 } 525 526 if (params.mShouldSetBackground) { 527 final int backgroundColorInt = mTaskInfo.taskDescription != null 528 ? mTaskInfo.taskDescription.getBackgroundColor() : Color.BLACK; 529 mTmpColor[0] = (float) Color.red(backgroundColorInt) / 255.f; 530 mTmpColor[1] = (float) Color.green(backgroundColorInt) / 255.f; 531 mTmpColor[2] = (float) Color.blue(backgroundColorInt) / 255.f; 532 startT.setColor(mTaskSurface, mTmpColor); 533 } else { 534 startT.unsetColor(mTaskSurface); 535 } 536 537 if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) { 538 if (outResult.mCornerRadius != INVALID_CORNER_RADIUS) { 539 startT.setCornerRadius(mTaskSurface, outResult.mCornerRadius); 540 finishT.setCornerRadius(mTaskSurface, outResult.mCornerRadius); 541 } 542 } else { 543 if (params.mCornerRadius != INVALID_CORNER_RADIUS) { 544 startT.setCornerRadius(mTaskSurface, params.mCornerRadius); 545 finishT.setCornerRadius(mTaskSurface, params.mCornerRadius); 546 } 547 } 548 } 549 550 @NonNull calculateLimitedTouchableRegion( RelayoutParams params, @NonNull Rect localCaptionBounds)551 private Region calculateLimitedTouchableRegion( 552 RelayoutParams params, 553 @NonNull Rect localCaptionBounds) { 554 // Make caption bounds relative to display to align with exclusion region. 555 final Point positionInParent = params.mRunningTaskInfo.positionInParent; 556 final Rect captionBoundsInDisplay = new Rect(localCaptionBounds); 557 captionBoundsInDisplay.offsetTo(positionInParent.x, positionInParent.y); 558 559 final Region boundingRects = calculateBoundingRectsRegion(params, captionBoundsInDisplay); 560 561 final Region customizedRegion = Region.obtain(); 562 customizedRegion.set(captionBoundsInDisplay); 563 customizedRegion.op(boundingRects, Region.Op.DIFFERENCE); 564 customizedRegion.op(params.mDisplayExclusionRegion, Region.Op.INTERSECT); 565 566 final Region touchableRegion = Region.obtain(); 567 touchableRegion.set(captionBoundsInDisplay); 568 touchableRegion.op(customizedRegion, Region.Op.DIFFERENCE); 569 // Return resulting region back to window coordinates. 570 touchableRegion.translate(-positionInParent.x, -positionInParent.y); 571 572 boundingRects.recycle(); 573 customizedRegion.recycle(); 574 return touchableRegion; 575 } 576 577 @NonNull calculateBoundingRectsRegion( @onNull RelayoutParams params, @NonNull Rect captionBoundsInDisplay)578 private Region calculateBoundingRectsRegion( 579 @NonNull RelayoutParams params, 580 @NonNull Rect captionBoundsInDisplay) { 581 final int numOfElements = params.mOccludingCaptionElements.size(); 582 final Region region = Region.obtain(); 583 if (numOfElements == 0) { 584 // The entire caption is a bounding rect. 585 region.set(captionBoundsInDisplay); 586 return region; 587 } 588 final Resources resources = mDecorWindowContext.getResources(); 589 for (int i = 0; i < numOfElements; i++) { 590 final OccludingCaptionElement element = params.mOccludingCaptionElements.get(i); 591 final int elementWidthPx = resources.getDimensionPixelSize(element.mWidthResId); 592 final Rect boundingRect = calculateBoundingRectLocal(element, elementWidthPx, 593 captionBoundsInDisplay); 594 // Bounding rect is initially calculated relative to the caption, so offset it to make 595 // it relative to the display. 596 boundingRect.offset(captionBoundsInDisplay.left, captionBoundsInDisplay.top); 597 region.union(boundingRect); 598 } 599 return region; 600 } 601 calculateBoundingRectLocal(@onNull OccludingCaptionElement element, int elementWidthPx, @NonNull Rect captionRect)602 private Rect calculateBoundingRectLocal(@NonNull OccludingCaptionElement element, 603 int elementWidthPx, @NonNull Rect captionRect) { 604 final boolean isRtl = 605 mDecorWindowContext.getResources().getConfiguration().getLayoutDirection() 606 == View.LAYOUT_DIRECTION_RTL; 607 switch (element.mAlignment) { 608 case START -> { 609 if (isRtl) { 610 return new Rect(captionRect.width() - elementWidthPx, 0, 611 captionRect.width(), captionRect.height()); 612 } else { 613 return new Rect(0, 0, elementWidthPx, captionRect.height()); 614 } 615 } 616 case END -> { 617 if (isRtl) { 618 return new Rect(0, 0, elementWidthPx, captionRect.height()); 619 } else { 620 return new Rect(captionRect.width() - elementWidthPx, 0, 621 captionRect.width(), captionRect.height()); 622 } 623 } 624 } 625 throw new IllegalArgumentException("Unexpected alignment " + element.mAlignment); 626 } 627 onKeyguardStateChanged(boolean visible, boolean occluded)628 void onKeyguardStateChanged(boolean visible, boolean occluded) { 629 final boolean prevVisAndOccluded = mIsKeyguardVisibleAndOccluded; 630 mIsKeyguardVisibleAndOccluded = visible && occluded; 631 final boolean changed = prevVisAndOccluded != mIsKeyguardVisibleAndOccluded; 632 if (changed) { 633 relayout(mTaskInfo, mHasGlobalFocus, mExclusionRegion); 634 } 635 } 636 onInsetsStateChanged(@onNull InsetsState insetsState)637 void onInsetsStateChanged(@NonNull InsetsState insetsState) { 638 final boolean prevStatusBarVisibility = mIsStatusBarVisible; 639 mIsStatusBarVisible = InsetsStateKt.isVisible(insetsState, statusBars()); 640 final boolean changed = prevStatusBarVisibility != mIsStatusBarVisible; 641 642 if (changed) { 643 relayout(mTaskInfo, mHasGlobalFocus, mExclusionRegion); 644 } 645 } 646 onExclusionRegionChanged(@onNull Region exclusionRegion)647 void onExclusionRegionChanged(@NonNull Region exclusionRegion) { 648 relayout(mTaskInfo, mHasGlobalFocus, exclusionRegion); 649 } 650 651 /** 652 * Update caption visibility state and views. 653 */ updateCaptionVisibility(View rootView, @NonNull RelayoutParams params)654 private void updateCaptionVisibility(View rootView, @NonNull RelayoutParams params) { 655 mIsCaptionVisible = params.mIsCaptionVisible; 656 if (!DesktopModeFlags.ENABLE_DESKTOP_APP_HANDLE_ANIMATION.isTrue()) { 657 setCaptionVisibility(rootView, mIsCaptionVisible); 658 } 659 } 660 setTaskDragResizer(TaskDragResizer taskDragResizer)661 void setTaskDragResizer(TaskDragResizer taskDragResizer) { 662 mTaskDragResizer = taskDragResizer; 663 } 664 665 // TODO(b/346441962): Move these three methods closer to implementing or View-level classes to 666 // keep implementation details more encapsulated. setCaptionVisibility(View rootView, boolean visible)667 private void setCaptionVisibility(View rootView, boolean visible) { 668 if (rootView == null) { 669 return; 670 } 671 final int v = visible ? View.VISIBLE : View.GONE; 672 final View captionView = rootView.findViewById(getCaptionViewId()); 673 captionView.setVisibility(v); 674 } 675 getCaptionHeightId(@indowingMode int windowingMode)676 int getCaptionHeightId(@WindowingMode int windowingMode) { 677 return Resources.ID_NULL; 678 } 679 getCaptionViewId()680 int getCaptionViewId() { 681 return Resources.ID_NULL; 682 } 683 684 /** 685 * Obtains the {@link Display} instance for the display ID in {@link #mTaskInfo} if it exists or 686 * registers {@link #mOnDisplaysChangedListener} if it doesn't. 687 * 688 * @return {@code true} if the {@link Display} instance exists; or {@code false} otherwise 689 */ obtainDisplayOrRegisterListener()690 private boolean obtainDisplayOrRegisterListener() { 691 mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId); 692 if (mDisplay == null) { 693 mDisplayController.addDisplayWindowListener(mOnDisplaysChangedListener); 694 return false; 695 } 696 return true; 697 } 698 releaseViews(WindowContainerTransaction wct)699 void releaseViews(WindowContainerTransaction wct) { 700 final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get(); 701 boolean released = false; 702 if (mViewHost != null) { 703 mWindowDecorViewHostSupplier.release(mViewHost, t); 704 mViewHost = null; 705 released = true; 706 } 707 708 if (mDecorationContainerSurface != null) { 709 t.remove(mDecorationContainerSurface); 710 mDecorationContainerSurface = null; 711 released = true; 712 } 713 714 if (released) { 715 t.apply(); 716 } 717 718 if (mWindowDecorationInsets != null) { 719 mWindowDecorationInsets.remove(wct); 720 mWindowDecorationInsets = null; 721 } 722 } 723 724 @Override close()725 public void close() { 726 Trace.beginSection("WindowDecoration#close"); 727 mDisplayController.removeDisplayWindowListener(mOnDisplaysChangedListener); 728 if (mTaskDragResizer != null) { 729 mTaskDragResizer.close(); 730 } 731 final WindowContainerTransaction wct = mWindowContainerTransactionSupplier.get(); 732 releaseViews(wct); 733 mTaskOrganizer.applyTransaction(wct); 734 mTaskSurface.release(); 735 Trace.endSection(); 736 } 737 loadDimensionPixelSize(Resources resources, int resourceId)738 static int loadDimensionPixelSize(Resources resources, int resourceId) { 739 if (resourceId == Resources.ID_NULL) { 740 return 0; 741 } 742 return resources.getDimensionPixelSize(resourceId); 743 } 744 loadDimension(Resources resources, int resourceId)745 static float loadDimension(Resources resources, int resourceId) { 746 if (resourceId == Resources.ID_NULL) { 747 return 0; 748 } 749 return resources.getDimension(resourceId); 750 } 751 cloneSurfaceControl(SurfaceControl sc, Supplier<SurfaceControl> surfaceControlSupplier)752 private static SurfaceControl cloneSurfaceControl(SurfaceControl sc, 753 Supplier<SurfaceControl> surfaceControlSupplier) { 754 final SurfaceControl copy = surfaceControlSupplier.get(); 755 copy.copyFrom(sc, "WindowDecoration"); 756 return copy; 757 } 758 759 /** 760 * Create a window associated with this WindowDecoration. 761 * Note that subclass must dispose of this when the task is hidden/closed. 762 * 763 * @param v View to attach to the window 764 * @param t the transaction to apply 765 * @param xPos x position of new window 766 * @param yPos y position of new window 767 * @param width width of new window 768 * @param height height of new window 769 * @return the {@link AdditionalViewHostViewContainer} that was added. 770 */ addWindow(@onNull View v, @NonNull String namePrefix, @NonNull SurfaceControl.Transaction t, @NonNull SurfaceSyncGroup ssg, int xPos, int yPos, int width, int height)771 AdditionalViewHostViewContainer addWindow(@NonNull View v, @NonNull String namePrefix, 772 @NonNull SurfaceControl.Transaction t, @NonNull SurfaceSyncGroup ssg, 773 int xPos, int yPos, int width, int height) { 774 final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get(); 775 SurfaceControl windowSurfaceControl = builder 776 .setName(namePrefix + " of Task=" + mTaskInfo.taskId) 777 .setContainerLayer() 778 .setParent(mDecorationContainerSurface) 779 .setCallsite("WindowDecoration.addWindow") 780 .build(); 781 t.setPosition(windowSurfaceControl, xPos, yPos) 782 .setWindowCrop(windowSurfaceControl, width, height) 783 .show(windowSurfaceControl); 784 final WindowManager.LayoutParams lp = 785 new WindowManager.LayoutParams( 786 width, 787 height, 788 TYPE_APPLICATION, 789 FLAG_NOT_FOCUSABLE | FLAG_WATCH_OUTSIDE_TOUCH, 790 PixelFormat.TRANSPARENT); 791 lp.setTitle("Additional window of Task=" + mTaskInfo.taskId); 792 lp.setTrustedOverlay(); 793 WindowlessWindowManager windowManager = new WindowlessWindowManager(mTaskInfo.configuration, 794 windowSurfaceControl, null /* hostInputToken */); 795 SurfaceControlViewHost viewHost = mSurfaceControlViewHostFactory 796 .create(mDecorWindowContext, mDisplay, windowManager); 797 ssg.add(viewHost.getSurfacePackage(), () -> viewHost.setView(v, lp)); 798 return new AdditionalViewHostViewContainer(windowSurfaceControl, viewHost, 799 mSurfaceControlTransactionSupplier); 800 } 801 802 /** 803 * Create a window associated with this WindowDecoration. 804 * Note that subclass must dispose of this when the task is hidden/closed. 805 * 806 * @param layoutId layout to make the window from 807 * @param t the transaction to apply 808 * @param xPos x position of new window 809 * @param yPos y position of new window 810 * @param width width of new window 811 * @param height height of new window 812 * @return the {@link AdditionalViewHostViewContainer} that was added. 813 */ addWindow(int layoutId, String namePrefix, SurfaceControl.Transaction t, SurfaceSyncGroup ssg, int xPos, int yPos, int width, int height)814 AdditionalViewHostViewContainer addWindow(int layoutId, String namePrefix, 815 SurfaceControl.Transaction t, SurfaceSyncGroup ssg, int xPos, int yPos, 816 int width, int height) { 817 final View v = LayoutInflater.from(mDecorWindowContext).inflate(layoutId, null); 818 return addWindow(v, namePrefix, t, ssg, xPos, yPos, width, height); 819 } 820 821 /** 822 * Adds caption inset source to a WCT 823 */ addCaptionInset(WindowContainerTransaction wct)824 public void addCaptionInset(WindowContainerTransaction wct) { 825 final int captionHeightId = getCaptionHeightId(mTaskInfo.getWindowingMode()); 826 if (captionHeightId == Resources.ID_NULL || !mIsCaptionVisible) { 827 return; 828 } 829 830 final int captionHeight = loadDimensionPixelSize(mContext.getResources(), captionHeightId); 831 final Rect captionInsets = new Rect(0, 0, 0, captionHeight); 832 final WindowDecorationInsets newInsets = new WindowDecorationInsets(mTaskInfo.token, 833 mOwner, captionInsets, null /* taskFrame */, null /* boundingRects */, 834 0 /* flags */, true /* shouldAddCaptionInset */, false /* excludedFromAppBounds */); 835 if (!newInsets.equals(mWindowDecorationInsets)) { 836 mWindowDecorationInsets = newInsets; 837 mWindowDecorationInsets.update(wct); 838 } 839 } 840 841 static class RelayoutParams { 842 RunningTaskInfo mRunningTaskInfo; 843 int mLayoutResId; 844 int mCaptionHeightId; 845 int mCaptionWidthId; 846 final List<OccludingCaptionElement> mOccludingCaptionElements = new ArrayList<>(); 847 boolean mLimitTouchRegionToSystemAreas; 848 int mInputFeatures; 849 boolean mIsInsetSource = true; 850 @InsetsSource.Flags int mInsetSourceFlags; 851 final Region mDisplayExclusionRegion = Region.obtain(); 852 853 @Deprecated 854 int mShadowRadius = INVALID_SHADOW_RADIUS; 855 @Deprecated 856 int mCornerRadius = INVALID_CORNER_RADIUS; 857 858 int mShadowRadiusId = Resources.ID_NULL; 859 int mCornerRadiusId = Resources.ID_NULL; 860 861 int mCaptionTopPadding; 862 boolean mIsCaptionVisible; 863 864 Configuration mWindowDecorConfig; 865 boolean mAsyncViewHost; 866 867 boolean mApplyStartTransactionOnDraw; 868 boolean mSetTaskVisibilityPositionAndCrop; 869 boolean mHasGlobalFocus; 870 boolean mShouldSetAppBounds; 871 boolean mShouldSetBackground; 872 reset()873 void reset() { 874 mLayoutResId = Resources.ID_NULL; 875 mCaptionHeightId = Resources.ID_NULL; 876 mCaptionWidthId = Resources.ID_NULL; 877 mOccludingCaptionElements.clear(); 878 mLimitTouchRegionToSystemAreas = false; 879 mInputFeatures = 0; 880 mIsInsetSource = true; 881 mInsetSourceFlags = 0; 882 mDisplayExclusionRegion.setEmpty(); 883 if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) { 884 mShadowRadiusId = Resources.ID_NULL; 885 mCornerRadiusId = Resources.ID_NULL; 886 } else { 887 mShadowRadius = INVALID_SHADOW_RADIUS; 888 mCornerRadius = INVALID_SHADOW_RADIUS; 889 } 890 891 mCaptionTopPadding = 0; 892 mIsCaptionVisible = false; 893 894 mApplyStartTransactionOnDraw = false; 895 mSetTaskVisibilityPositionAndCrop = false; 896 mWindowDecorConfig = null; 897 mAsyncViewHost = false; 898 mHasGlobalFocus = false; 899 mShouldSetAppBounds = false; 900 mShouldSetBackground = false; 901 } 902 hasInputFeatureSpy()903 boolean hasInputFeatureSpy() { 904 return (mInputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_SPY) != 0; 905 } 906 907 /** 908 * Describes elements within the caption bar that could occlude app content, and should be 909 * sent as bounding rectangles to the insets system. 910 */ 911 static class OccludingCaptionElement { 912 int mWidthResId; 913 Alignment mAlignment; 914 915 enum Alignment { 916 START, END 917 } 918 } 919 } 920 921 static class RelayoutResult<T extends View & TaskFocusStateConsumer> { 922 int mCaptionHeight; 923 int mCaptionWidth; 924 int mCaptionX; 925 int mCaptionY; 926 int mCaptionTopPadding; 927 final Region mCustomizableCaptionRegion = Region.obtain(); 928 int mWidth; 929 int mHeight; 930 T mRootView; 931 int mCornerRadius; 932 int mShadowRadius; 933 reset()934 void reset() { 935 mWidth = 0; 936 mHeight = 0; 937 mCaptionHeight = 0; 938 mCaptionWidth = 0; 939 mCaptionX = 0; 940 mCaptionY = 0; 941 mCaptionTopPadding = 0; 942 mCustomizableCaptionRegion.setEmpty(); 943 mRootView = null; 944 if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) { 945 mCornerRadius = INVALID_CORNER_RADIUS; 946 mShadowRadius = INVALID_SHADOW_RADIUS; 947 } 948 } 949 } 950 951 private static class CaptionWindowlessWindowManager extends WindowlessWindowManager { CaptionWindowlessWindowManager( @onNull Configuration configuration, @NonNull SurfaceControl rootSurface)952 CaptionWindowlessWindowManager( 953 @NonNull Configuration configuration, 954 @NonNull SurfaceControl rootSurface) { 955 super(configuration, rootSurface, /* hostInputToken= */ null); 956 } 957 958 /** Set the view host's touchable region. */ setTouchRegion(@onNull SurfaceControlViewHost viewHost, @NonNull Region region)959 void setTouchRegion(@NonNull SurfaceControlViewHost viewHost, @NonNull Region region) { 960 setTouchRegion(viewHost.getWindowToken().asBinder(), region); 961 } 962 } 963 964 public interface SurfaceControlViewHostFactory { create(Context c, Display d, WindowlessWindowManager wmm)965 default SurfaceControlViewHost create(Context c, Display d, WindowlessWindowManager wmm) { 966 return new SurfaceControlViewHost(c, d, wmm, "WindowDecoration"); 967 } create(Context c, Display d, WindowlessWindowManager wmm, String callsite)968 default SurfaceControlViewHost create(Context c, Display d, 969 WindowlessWindowManager wmm, String callsite) { 970 return new SurfaceControlViewHost(c, d, wmm, callsite); 971 } 972 } 973 974 private static class WindowDecorationInsets { 975 private static final int INDEX = 0; 976 private final WindowContainerToken mToken; 977 private final Binder mOwner; 978 private final Rect mFrame; 979 private final Rect mTaskFrame; 980 private final Rect[] mBoundingRects; 981 private final @InsetsSource.Flags int mFlags; 982 private final boolean mShouldAddCaptionInset; 983 private final boolean mExcludedFromAppBounds; 984 WindowDecorationInsets(WindowContainerToken token, Binder owner, Rect frame, Rect taskFrame, Rect[] boundingRects, @InsetsSource.Flags int flags, boolean shouldAddCaptionInset, boolean excludedFromAppBounds)985 private WindowDecorationInsets(WindowContainerToken token, Binder owner, Rect frame, 986 Rect taskFrame, Rect[] boundingRects, @InsetsSource.Flags int flags, 987 boolean shouldAddCaptionInset, boolean excludedFromAppBounds) { 988 mToken = token; 989 mOwner = owner; 990 mFrame = frame; 991 mTaskFrame = taskFrame; 992 mBoundingRects = boundingRects; 993 mFlags = flags; 994 mShouldAddCaptionInset = shouldAddCaptionInset; 995 mExcludedFromAppBounds = excludedFromAppBounds; 996 } 997 update(WindowContainerTransaction wct)998 void update(WindowContainerTransaction wct) { 999 if (mShouldAddCaptionInset) { 1000 wct.addInsetsSource(mToken, mOwner, INDEX, captionBar(), mFrame, mBoundingRects, 1001 mFlags); 1002 wct.addInsetsSource(mToken, mOwner, INDEX, mandatorySystemGestures(), mFrame, 1003 mBoundingRects, 0 /* flags */); 1004 if (mExcludedFromAppBounds) { 1005 final Rect appBounds = new Rect(mTaskFrame); 1006 appBounds.top += mFrame.height(); 1007 wct.setAppBounds(mToken, appBounds); 1008 } 1009 } 1010 } 1011 remove(WindowContainerTransaction wct)1012 void remove(WindowContainerTransaction wct) { 1013 wct.removeInsetsSource(mToken, mOwner, INDEX, captionBar()); 1014 wct.removeInsetsSource(mToken, mOwner, INDEX, mandatorySystemGestures()); 1015 if (mExcludedFromAppBounds) { 1016 wct.setAppBounds(mToken, new Rect()); 1017 } 1018 } 1019 1020 @Override equals(Object o)1021 public boolean equals(Object o) { 1022 if (this == o) return true; 1023 if (!(o instanceof WindowDecoration.WindowDecorationInsets that)) return false; 1024 return Objects.equals(mToken, that.mToken) && Objects.equals(mOwner, 1025 that.mOwner) && Objects.equals(mFrame, that.mFrame) 1026 && Objects.equals(mTaskFrame, that.mTaskFrame) 1027 && Objects.deepEquals(mBoundingRects, that.mBoundingRects) 1028 && mFlags == that.mFlags 1029 && mShouldAddCaptionInset == that.mShouldAddCaptionInset 1030 && mExcludedFromAppBounds == that.mExcludedFromAppBounds; 1031 } 1032 1033 @Override hashCode()1034 public int hashCode() { 1035 return Objects.hash(mToken, mOwner, mFrame, Arrays.hashCode(mBoundingRects), mFlags); 1036 } 1037 } 1038 } 1039