1 /* 2 * Copyright (C) 2020 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 android.window; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.TestApi; 22 import android.app.WindowConfiguration; 23 import android.content.pm.ActivityInfo; 24 import android.content.res.Configuration; 25 import android.graphics.Rect; 26 import android.os.Bundle; 27 import android.os.IBinder; 28 import android.os.Parcel; 29 import android.os.Parcelable; 30 import android.util.ArrayMap; 31 import android.view.SurfaceControl; 32 33 import java.util.ArrayList; 34 import java.util.Arrays; 35 import java.util.List; 36 import java.util.Map; 37 38 /** 39 * Represents a collection of operations on some WindowContainers that should be applied all at 40 * once. 41 * 42 * @hide 43 */ 44 @TestApi 45 public final class WindowContainerTransaction implements Parcelable { 46 private final ArrayMap<IBinder, Change> mChanges = new ArrayMap<>(); 47 48 // Flat list because re-order operations are order-dependent 49 private final ArrayList<HierarchyOp> mHierarchyOps = new ArrayList<>(); 50 51 public WindowContainerTransaction() {} 52 53 private WindowContainerTransaction(Parcel in) { 54 in.readMap(mChanges, null /* loader */); 55 in.readList(mHierarchyOps, null /* loader */); 56 } 57 58 private Change getOrCreateChange(IBinder token) { 59 Change out = mChanges.get(token); 60 if (out == null) { 61 out = new Change(); 62 mChanges.put(token, out); 63 } 64 return out; 65 } 66 67 /** 68 * Resize a container. 69 */ 70 @NonNull 71 public WindowContainerTransaction setBounds( 72 @NonNull WindowContainerToken container,@NonNull Rect bounds) { 73 Change chg = getOrCreateChange(container.asBinder()); 74 chg.mConfiguration.windowConfiguration.setBounds(bounds); 75 chg.mConfigSetMask |= ActivityInfo.CONFIG_WINDOW_CONFIGURATION; 76 chg.mWindowSetMask |= WindowConfiguration.WINDOW_CONFIG_BOUNDS; 77 return this; 78 } 79 80 /** 81 * Resize a container's app bounds. This is the bounds used to report appWidth/Height to an 82 * app's DisplayInfo. It is derived by subtracting the overlapping portion of the navbar from 83 * the full bounds. 84 */ 85 @NonNull 86 public WindowContainerTransaction setAppBounds( 87 @NonNull WindowContainerToken container,@NonNull Rect appBounds) { 88 Change chg = getOrCreateChange(container.asBinder()); 89 chg.mConfiguration.windowConfiguration.setAppBounds(appBounds); 90 chg.mConfigSetMask |= ActivityInfo.CONFIG_WINDOW_CONFIGURATION; 91 chg.mWindowSetMask |= WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS; 92 return this; 93 } 94 95 /** 96 * Resize a container's configuration size. The configuration size is what gets reported to the 97 * app via screenWidth/HeightDp and influences which resources get loaded. This size is 98 * derived by subtracting the overlapping portions of both the statusbar and the navbar from 99 * the full bounds. 100 */ 101 @NonNull 102 public WindowContainerTransaction setScreenSizeDp( 103 @NonNull WindowContainerToken container, int w, int h) { 104 Change chg = getOrCreateChange(container.asBinder()); 105 chg.mConfiguration.screenWidthDp = w; 106 chg.mConfiguration.screenHeightDp = h; 107 chg.mConfigSetMask |= ActivityInfo.CONFIG_SCREEN_SIZE; 108 return this; 109 } 110 111 /** 112 * Notify {@link com.android.server.wm.PinnedTaskController} that the picture-in-picture task 113 * has finished the enter animation with the given bounds. 114 */ 115 @NonNull 116 public WindowContainerTransaction scheduleFinishEnterPip( 117 @NonNull WindowContainerToken container,@NonNull Rect bounds) { 118 Change chg = getOrCreateChange(container.asBinder()); 119 chg.mPinnedBounds = new Rect(bounds); 120 chg.mChangeMask |= Change.CHANGE_PIP_CALLBACK; 121 122 return this; 123 } 124 125 /** 126 * Send a SurfaceControl transaction to the server, which the server will apply in sync with 127 * the next bounds change. As this uses deferred transaction and not BLAST it is only 128 * able to sync with a single window, and the first visible window in this hierarchy of type 129 * BASE_APPLICATION to resize will be used. If there are bound changes included in this 130 * WindowContainer transaction (from setBounds or scheduleFinishEnterPip), the SurfaceControl 131 * transaction will be synced with those bounds. If there are no changes, then 132 * the SurfaceControl transaction will be synced with the next bounds change. This means 133 * that you can call this, apply the WindowContainer transaction, and then later call 134 * dismissPip() to achieve synchronization. 135 */ 136 @NonNull 137 public WindowContainerTransaction setBoundsChangeTransaction( 138 @NonNull WindowContainerToken container,@NonNull SurfaceControl.Transaction t) { 139 Change chg = getOrCreateChange(container.asBinder()); 140 chg.mBoundsChangeTransaction = t; 141 chg.mChangeMask |= Change.CHANGE_BOUNDS_TRANSACTION; 142 return this; 143 } 144 145 /** 146 * Like {@link #setBoundsChangeTransaction} but instead queues up a setPosition/WindowCrop 147 * on a container's surface control. This is useful when a boundsChangeTransaction needs to be 148 * queued up on a Task that won't be organized until the end of this window-container 149 * transaction. 150 * 151 * This requires that, at the end of this transaction, `task` will be organized; otherwise 152 * the server will throw an IllegalArgumentException. 153 * 154 * WARNING: Use this carefully. Whatever is set here should match the expected bounds after 155 * the transaction completes since it will likely be replaced by it. This call is 156 * intended to pre-emptively set bounds on a surface in sync with a buffer when 157 * otherwise the new bounds and the new buffer would update on different frames. 158 * 159 * TODO(b/134365562): remove once TaskOrg drives full-screen or BLAST is enabled. 160 * 161 * @hide 162 */ 163 @NonNull 164 public WindowContainerTransaction setBoundsChangeTransaction( 165 @NonNull WindowContainerToken task, @NonNull Rect surfaceBounds) { 166 Change chg = getOrCreateChange(task.asBinder()); 167 if (chg.mBoundsChangeSurfaceBounds == null) { 168 chg.mBoundsChangeSurfaceBounds = new Rect(); 169 } 170 chg.mBoundsChangeSurfaceBounds.set(surfaceBounds); 171 chg.mChangeMask |= Change.CHANGE_BOUNDS_TRANSACTION_RECT; 172 return this; 173 } 174 175 /** 176 * Set the windowing mode of children of a given root task, without changing 177 * the windowing mode of the Task itself. This can be used during transitions 178 * for example to make the activity render it's fullscreen configuration 179 * while the Task is still in PIP, so you can complete the animation. 180 * 181 * TODO(b/134365562): Can be removed once TaskOrg drives full-screen 182 */ 183 @NonNull 184 public WindowContainerTransaction setActivityWindowingMode( 185 @NonNull WindowContainerToken container, int windowingMode) { 186 Change chg = getOrCreateChange(container.asBinder()); 187 chg.mActivityWindowingMode = windowingMode; 188 return this; 189 } 190 191 /** 192 * Sets the windowing mode of the given container. 193 */ 194 @NonNull 195 public WindowContainerTransaction setWindowingMode( 196 @NonNull WindowContainerToken container, int windowingMode) { 197 Change chg = getOrCreateChange(container.asBinder()); 198 chg.mWindowingMode = windowingMode; 199 return this; 200 } 201 202 /** 203 * Sets whether a container or any of its children can be focusable. When {@code false}, no 204 * child can be focused; however, when {@code true}, it is still possible for children to be 205 * non-focusable due to WM policy. 206 */ 207 @NonNull 208 public WindowContainerTransaction setFocusable( 209 @NonNull WindowContainerToken container, boolean focusable) { 210 Change chg = getOrCreateChange(container.asBinder()); 211 chg.mFocusable = focusable; 212 chg.mChangeMask |= Change.CHANGE_FOCUSABLE; 213 return this; 214 } 215 216 /** 217 * Sets whether a container or its children should be hidden. When {@code false}, the existing 218 * visibility of the container applies, but when {@code true} the container will be forced 219 * to be hidden. 220 */ 221 @NonNull 222 public WindowContainerTransaction setHidden( 223 @NonNull WindowContainerToken container, boolean hidden) { 224 Change chg = getOrCreateChange(container.asBinder()); 225 chg.mHidden = hidden; 226 chg.mChangeMask |= Change.CHANGE_HIDDEN; 227 return this; 228 } 229 230 /** 231 * Set the smallestScreenWidth of a container. 232 */ 233 @NonNull 234 public WindowContainerTransaction setSmallestScreenWidthDp( 235 @NonNull WindowContainerToken container, int widthDp) { 236 Change cfg = getOrCreateChange(container.asBinder()); 237 cfg.mConfiguration.smallestScreenWidthDp = widthDp; 238 cfg.mConfigSetMask |= ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE; 239 return this; 240 } 241 242 /** 243 * Sets whether a container should ignore the orientation request from apps and windows below 244 * it. It currently only applies to {@link com.android.server.wm.DisplayArea}. When 245 * {@code false}, it may rotate based on the orientation request; When {@code true}, it can 246 * never specify orientation, but shows the fixed-orientation apps below it in the letterbox. 247 * @hide 248 */ 249 @NonNull 250 public WindowContainerTransaction setIgnoreOrientationRequest( 251 @NonNull WindowContainerToken container, boolean ignoreOrientationRequest) { 252 Change chg = getOrCreateChange(container.asBinder()); 253 chg.mIgnoreOrientationRequest = ignoreOrientationRequest; 254 chg.mChangeMask |= Change.CHANGE_IGNORE_ORIENTATION_REQUEST; 255 return this; 256 } 257 258 /** 259 * Reparents a container into another one. The effect of a {@code null} parent can vary. For 260 * example, reparenting a stack to {@code null} will reparent it to its display. 261 * 262 * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to 263 * the bottom. 264 */ 265 @NonNull 266 public WindowContainerTransaction reparent(@NonNull WindowContainerToken child, 267 @Nullable WindowContainerToken parent, boolean onTop) { 268 mHierarchyOps.add(HierarchyOp.createForReparent(child.asBinder(), 269 parent == null ? null : parent.asBinder(), 270 onTop)); 271 return this; 272 } 273 274 /** 275 * Reorders a container within its parent. 276 * 277 * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to 278 * the bottom. 279 */ 280 @NonNull 281 public WindowContainerTransaction reorder(@NonNull WindowContainerToken child, boolean onTop) { 282 mHierarchyOps.add(HierarchyOp.createForReorder(child.asBinder(), onTop)); 283 return this; 284 } 285 286 /** 287 * Reparent's all children tasks of {@param currentParent} in the specified 288 * {@param windowingMode} and {@param activityType} to {@param newParent} in their current 289 * z-order. 290 * 291 * @param currentParent of the tasks to perform the operation no. 292 * {@code null} will perform the operation on the display. 293 * @param newParent for the tasks. {@code null} will perform the operation on the display. 294 * @param windowingModes of the tasks to reparent. 295 * @param activityTypes of the tasks to reparent. 296 * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to 297 * the bottom. 298 */ 299 @NonNull 300 public WindowContainerTransaction reparentTasks(@Nullable WindowContainerToken currentParent, 301 @Nullable WindowContainerToken newParent, @Nullable int[] windowingModes, 302 @Nullable int[] activityTypes, boolean onTop) { 303 mHierarchyOps.add(HierarchyOp.createForChildrenTasksReparent( 304 currentParent != null ? currentParent.asBinder() : null, 305 newParent != null ? newParent.asBinder() : null, 306 windowingModes, 307 activityTypes, 308 onTop)); 309 return this; 310 } 311 312 /** 313 * Sets whether a container should be the launch root for the specified windowing mode and 314 * activity type. This currently only applies to Task containers created by organizer. 315 */ 316 @NonNull 317 public WindowContainerTransaction setLaunchRoot(@NonNull WindowContainerToken container, 318 @Nullable int[] windowingModes, @Nullable int[] activityTypes) { 319 mHierarchyOps.add(HierarchyOp.createForSetLaunchRoot( 320 container.asBinder(), 321 windowingModes, 322 activityTypes)); 323 return this; 324 } 325 326 /** 327 * Sets to containers adjacent to each other. Containers below two visible adjacent roots will 328 * be made invisible. This currently only applies to Task containers created by organizer. 329 * @param root1 the first root. 330 * @param root2 the second root. 331 */ 332 @NonNull 333 public WindowContainerTransaction setAdjacentRoots( 334 @NonNull WindowContainerToken root1, @NonNull WindowContainerToken root2) { 335 mHierarchyOps.add(HierarchyOp.createForAdjacentRoots( 336 root1.asBinder(), 337 root2.asBinder())); 338 return this; 339 } 340 341 /** 342 * Sets the container as launch adjacent flag root. Task starting with 343 * {@link FLAG_ACTIVITY_LAUNCH_ADJACENT} will be launching to. 344 * 345 * @hide 346 */ 347 @NonNull 348 public WindowContainerTransaction setLaunchAdjacentFlagRoot( 349 @NonNull WindowContainerToken container) { 350 mHierarchyOps.add(HierarchyOp.createForSetLaunchAdjacentFlagRoot(container.asBinder(), 351 false /* clearRoot */)); 352 return this; 353 } 354 355 /** 356 * Clears launch adjacent flag root for the display area of passing container. 357 * 358 * @hide 359 */ 360 @NonNull 361 public WindowContainerTransaction clearLaunchAdjacentFlagRoot( 362 @NonNull WindowContainerToken container) { 363 mHierarchyOps.add(HierarchyOp.createForSetLaunchAdjacentFlagRoot(container.asBinder(), 364 true /* clearRoot */)); 365 return this; 366 } 367 368 /** 369 * Starts a task by id. The task is expected to already exist (eg. as a recent task). 370 * @param taskId Id of task to start. 371 * @param options bundle containing ActivityOptions for the task's top activity. 372 * @hide 373 */ 374 @NonNull 375 public WindowContainerTransaction startTask(int taskId, @Nullable Bundle options) { 376 mHierarchyOps.add(HierarchyOp.createForTaskLaunch(taskId, options)); 377 return this; 378 } 379 380 /** 381 * Merges another WCT into this one. 382 * @param transfer When true, this will transfer everything from other potentially leaving 383 * other in an unusable state. When false, other is left alone, but 384 * SurfaceFlinger Transactions will not be merged. 385 * @hide 386 */ 387 public void merge(WindowContainerTransaction other, boolean transfer) { 388 for (int i = 0, n = other.mChanges.size(); i < n; ++i) { 389 final IBinder key = other.mChanges.keyAt(i); 390 Change existing = mChanges.get(key); 391 if (existing == null) { 392 existing = new Change(); 393 mChanges.put(key, existing); 394 } 395 existing.merge(other.mChanges.valueAt(i), transfer); 396 } 397 for (int i = 0, n = other.mHierarchyOps.size(); i < n; ++i) { 398 mHierarchyOps.add(transfer ? other.mHierarchyOps.get(i) 399 : new HierarchyOp(other.mHierarchyOps.get(i))); 400 } 401 } 402 403 /** @hide */ 404 public boolean isEmpty() { 405 return mChanges.isEmpty() && mHierarchyOps.isEmpty(); 406 } 407 408 /** @hide */ 409 public Map<IBinder, Change> getChanges() { 410 return mChanges; 411 } 412 413 /** @hide */ 414 public List<HierarchyOp> getHierarchyOps() { 415 return mHierarchyOps; 416 } 417 418 @Override 419 @NonNull 420 public String toString() { 421 return "WindowContainerTransaction { changes = " + mChanges + " hops = " + mHierarchyOps 422 + " }"; 423 } 424 425 @Override 426 /** @hide */ 427 public void writeToParcel(@NonNull Parcel dest, int flags) { 428 dest.writeMap(mChanges); 429 dest.writeList(mHierarchyOps); 430 } 431 432 @Override 433 /** @hide */ 434 public int describeContents() { 435 return 0; 436 } 437 438 @NonNull 439 public static final Creator<WindowContainerTransaction> CREATOR = 440 new Creator<WindowContainerTransaction>() { 441 @Override 442 public WindowContainerTransaction createFromParcel(Parcel in) { 443 return new WindowContainerTransaction(in); 444 } 445 446 @Override 447 public WindowContainerTransaction[] newArray(int size) { 448 return new WindowContainerTransaction[size]; 449 } 450 }; 451 452 /** 453 * Holds changes on a single WindowContainer including Configuration changes. 454 * @hide 455 */ 456 public static class Change implements Parcelable { 457 public static final int CHANGE_FOCUSABLE = 1; 458 public static final int CHANGE_BOUNDS_TRANSACTION = 1 << 1; 459 public static final int CHANGE_PIP_CALLBACK = 1 << 2; 460 public static final int CHANGE_HIDDEN = 1 << 3; 461 public static final int CHANGE_BOUNDS_TRANSACTION_RECT = 1 << 4; 462 public static final int CHANGE_IGNORE_ORIENTATION_REQUEST = 1 << 5; 463 464 private final Configuration mConfiguration = new Configuration(); 465 private boolean mFocusable = true; 466 private boolean mHidden = false; 467 private boolean mIgnoreOrientationRequest = false; 468 469 private int mChangeMask = 0; 470 private @ActivityInfo.Config int mConfigSetMask = 0; 471 private @WindowConfiguration.WindowConfig int mWindowSetMask = 0; 472 473 private Rect mPinnedBounds = null; 474 private SurfaceControl.Transaction mBoundsChangeTransaction = null; 475 private Rect mBoundsChangeSurfaceBounds = null; 476 477 private int mActivityWindowingMode = -1; 478 private int mWindowingMode = -1; 479 480 public Change() {} 481 482 protected Change(Parcel in) { 483 mConfiguration.readFromParcel(in); 484 mFocusable = in.readBoolean(); 485 mHidden = in.readBoolean(); 486 mIgnoreOrientationRequest = in.readBoolean(); 487 mChangeMask = in.readInt(); 488 mConfigSetMask = in.readInt(); 489 mWindowSetMask = in.readInt(); 490 if ((mChangeMask & Change.CHANGE_PIP_CALLBACK) != 0) { 491 mPinnedBounds = new Rect(); 492 mPinnedBounds.readFromParcel(in); 493 } 494 if ((mChangeMask & Change.CHANGE_BOUNDS_TRANSACTION) != 0) { 495 mBoundsChangeTransaction = 496 SurfaceControl.Transaction.CREATOR.createFromParcel(in); 497 } 498 if ((mChangeMask & Change.CHANGE_BOUNDS_TRANSACTION_RECT) != 0) { 499 mBoundsChangeSurfaceBounds = new Rect(); 500 mBoundsChangeSurfaceBounds.readFromParcel(in); 501 } 502 503 mWindowingMode = in.readInt(); 504 mActivityWindowingMode = in.readInt(); 505 } 506 507 /** 508 * @param transfer When true, this will transfer other into this leaving other in an 509 * undefined state. Use this if you don't intend to use other. When false, 510 * SurfaceFlinger Transactions will not merge. 511 */ 512 public void merge(Change other, boolean transfer) { 513 mConfiguration.setTo(other.mConfiguration, other.mConfigSetMask, other.mWindowSetMask); 514 mConfigSetMask |= other.mConfigSetMask; 515 mWindowSetMask |= other.mWindowSetMask; 516 if ((other.mChangeMask & CHANGE_FOCUSABLE) != 0) { 517 mFocusable = other.mFocusable; 518 } 519 if (transfer && (other.mChangeMask & CHANGE_BOUNDS_TRANSACTION) != 0) { 520 mBoundsChangeTransaction = other.mBoundsChangeTransaction; 521 other.mBoundsChangeTransaction = null; 522 } 523 if ((other.mChangeMask & CHANGE_PIP_CALLBACK) != 0) { 524 mPinnedBounds = transfer ? other.mPinnedBounds : new Rect(other.mPinnedBounds); 525 } 526 if ((other.mChangeMask & CHANGE_HIDDEN) != 0) { 527 mHidden = other.mHidden; 528 } 529 if ((other.mChangeMask & CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) { 530 mIgnoreOrientationRequest = other.mIgnoreOrientationRequest; 531 } 532 mChangeMask |= other.mChangeMask; 533 if (other.mActivityWindowingMode >= 0) { 534 mActivityWindowingMode = other.mActivityWindowingMode; 535 } 536 if (other.mWindowingMode >= 0) { 537 mWindowingMode = other.mWindowingMode; 538 } 539 if (other.mBoundsChangeSurfaceBounds != null) { 540 mBoundsChangeSurfaceBounds = transfer ? other.mBoundsChangeSurfaceBounds 541 : new Rect(other.mBoundsChangeSurfaceBounds); 542 } 543 } 544 545 public int getWindowingMode() { 546 return mWindowingMode; 547 } 548 549 public int getActivityWindowingMode() { 550 return mActivityWindowingMode; 551 } 552 553 public Configuration getConfiguration() { 554 return mConfiguration; 555 } 556 557 /** Gets the requested focusable state */ 558 public boolean getFocusable() { 559 if ((mChangeMask & CHANGE_FOCUSABLE) == 0) { 560 throw new RuntimeException("Focusable not set. check CHANGE_FOCUSABLE first"); 561 } 562 return mFocusable; 563 } 564 565 /** Gets the requested hidden state */ 566 public boolean getHidden() { 567 if ((mChangeMask & CHANGE_HIDDEN) == 0) { 568 throw new RuntimeException("Hidden not set. check CHANGE_HIDDEN first"); 569 } 570 return mHidden; 571 } 572 573 /** Gets the requested state of whether to ignore orientation request. */ 574 public boolean getIgnoreOrientationRequest() { 575 if ((mChangeMask & CHANGE_IGNORE_ORIENTATION_REQUEST) == 0) { 576 throw new RuntimeException("IgnoreOrientationRequest not set. " 577 + "Check CHANGE_IGNORE_ORIENTATION_REQUEST first"); 578 } 579 return mIgnoreOrientationRequest; 580 } 581 582 public int getChangeMask() { 583 return mChangeMask; 584 } 585 586 @ActivityInfo.Config 587 public int getConfigSetMask() { 588 return mConfigSetMask; 589 } 590 591 @WindowConfiguration.WindowConfig 592 public int getWindowSetMask() { 593 return mWindowSetMask; 594 } 595 596 /** 597 * Returns the bounds to be used for scheduling the enter pip callback 598 * or null if no callback is to be scheduled. 599 */ 600 public Rect getEnterPipBounds() { 601 return mPinnedBounds; 602 } 603 604 public SurfaceControl.Transaction getBoundsChangeTransaction() { 605 return mBoundsChangeTransaction; 606 } 607 608 public Rect getBoundsChangeSurfaceBounds() { 609 return mBoundsChangeSurfaceBounds; 610 } 611 612 @Override 613 public String toString() { 614 final boolean changesBounds = 615 (mConfigSetMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0 616 && ((mWindowSetMask & WindowConfiguration.WINDOW_CONFIG_BOUNDS) 617 != 0); 618 final boolean changesAppBounds = 619 (mConfigSetMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0 620 && ((mWindowSetMask & WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS) 621 != 0); 622 final boolean changesSs = (mConfigSetMask & ActivityInfo.CONFIG_SCREEN_SIZE) != 0; 623 final boolean changesSss = 624 (mConfigSetMask & ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE) != 0; 625 StringBuilder sb = new StringBuilder(); 626 sb.append('{'); 627 if (changesBounds) { 628 sb.append("bounds:" + mConfiguration.windowConfiguration.getBounds() + ","); 629 } 630 if (changesAppBounds) { 631 sb.append("appbounds:" + mConfiguration.windowConfiguration.getAppBounds() + ","); 632 } 633 if (changesSss) { 634 sb.append("ssw:" + mConfiguration.smallestScreenWidthDp + ","); 635 } 636 if (changesSs) { 637 sb.append("sw/h:" + mConfiguration.screenWidthDp + "x" 638 + mConfiguration.screenHeightDp + ","); 639 } 640 if ((mChangeMask & CHANGE_FOCUSABLE) != 0) { 641 sb.append("focusable:" + mFocusable + ","); 642 } 643 if (mBoundsChangeTransaction != null) { 644 sb.append("hasBoundsTransaction,"); 645 } 646 if ((mChangeMask & CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) { 647 sb.append("ignoreOrientationRequest:" + mIgnoreOrientationRequest + ","); 648 } 649 sb.append("}"); 650 return sb.toString(); 651 } 652 653 @Override 654 public void writeToParcel(Parcel dest, int flags) { 655 mConfiguration.writeToParcel(dest, flags); 656 dest.writeBoolean(mFocusable); 657 dest.writeBoolean(mHidden); 658 dest.writeBoolean(mIgnoreOrientationRequest); 659 dest.writeInt(mChangeMask); 660 dest.writeInt(mConfigSetMask); 661 dest.writeInt(mWindowSetMask); 662 663 if (mPinnedBounds != null) { 664 mPinnedBounds.writeToParcel(dest, flags); 665 } 666 if (mBoundsChangeTransaction != null) { 667 mBoundsChangeTransaction.writeToParcel(dest, flags); 668 } 669 if (mBoundsChangeSurfaceBounds != null) { 670 mBoundsChangeSurfaceBounds.writeToParcel(dest, flags); 671 } 672 673 dest.writeInt(mWindowingMode); 674 dest.writeInt(mActivityWindowingMode); 675 } 676 677 @Override 678 public int describeContents() { 679 return 0; 680 } 681 682 public static final Creator<Change> CREATOR = new Creator<Change>() { 683 @Override 684 public Change createFromParcel(Parcel in) { 685 return new Change(in); 686 } 687 688 @Override 689 public Change[] newArray(int size) { 690 return new Change[size]; 691 } 692 }; 693 } 694 695 /** 696 * Holds information about a reparent/reorder operation in the hierarchy. This is separate from 697 * Changes because they must be executed in the same order that they are added. 698 * @hide 699 */ 700 public static class HierarchyOp implements Parcelable { 701 public static final int HIERARCHY_OP_TYPE_REPARENT = 0; 702 public static final int HIERARCHY_OP_TYPE_REORDER = 1; 703 public static final int HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT = 2; 704 public static final int HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT = 3; 705 public static final int HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS = 4; 706 public static final int HIERARCHY_OP_TYPE_LAUNCH_TASK = 5; 707 public static final int HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT = 6; 708 709 // The following key(s) are for use with mLaunchOptions: 710 // When launching a task (eg. from recents), this is the taskId to be launched. 711 public static final String LAUNCH_KEY_TASK_ID = "android:transaction.hop.taskId"; 712 713 private final int mType; 714 715 // Container we are performing the operation on. 716 private final IBinder mContainer; 717 718 // If this is same as mContainer, then only change position, don't reparent. 719 private final IBinder mReparent; 720 721 // Moves/reparents to top of parent when {@code true}, otherwise moves/reparents to bottom. 722 private final boolean mToTop; 723 724 final private int[] mWindowingModes; 725 final private int[] mActivityTypes; 726 727 private final Bundle mLaunchOptions; 728 729 public static HierarchyOp createForReparent( 730 @NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) { 731 return new HierarchyOp(HIERARCHY_OP_TYPE_REPARENT, 732 container, reparent, null, null, toTop, null); 733 } 734 735 public static HierarchyOp createForReorder(@NonNull IBinder container, boolean toTop) { 736 return new HierarchyOp(HIERARCHY_OP_TYPE_REORDER, 737 container, container, null, null, toTop, null); 738 } 739 740 public static HierarchyOp createForChildrenTasksReparent(IBinder currentParent, 741 IBinder newParent, int[] windowingModes, int[] activityTypes, boolean onTop) { 742 return new HierarchyOp(HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT, 743 currentParent, newParent, windowingModes, activityTypes, onTop, null); 744 } 745 746 public static HierarchyOp createForSetLaunchRoot(IBinder container, 747 int[] windowingModes, int[] activityTypes) { 748 return new HierarchyOp(HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT, 749 container, null, windowingModes, activityTypes, false, null); 750 } 751 752 public static HierarchyOp createForAdjacentRoots(IBinder root1, IBinder root2) { 753 return new HierarchyOp(HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS, 754 root1, root2, null, null, false, null); 755 } 756 757 /** Create a hierarchy op for launching a task. */ 758 public static HierarchyOp createForTaskLaunch(int taskId, @Nullable Bundle options) { 759 final Bundle fullOptions = options == null ? new Bundle() : options; 760 fullOptions.putInt(LAUNCH_KEY_TASK_ID, taskId); 761 return new HierarchyOp(HIERARCHY_OP_TYPE_LAUNCH_TASK, null, null, null, null, true, 762 fullOptions); 763 } 764 765 /** Create a hierarchy op for setting launch adjacent flag root. */ 766 public static HierarchyOp createForSetLaunchAdjacentFlagRoot(IBinder container, 767 boolean clearRoot) { 768 return new HierarchyOp(HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT, container, null, 769 null, null, clearRoot, null); 770 } 771 772 773 private HierarchyOp(int type, @Nullable IBinder container, @Nullable IBinder reparent, 774 int[] windowingModes, int[] activityTypes, boolean toTop, 775 @Nullable Bundle launchOptions) { 776 mType = type; 777 mContainer = container; 778 mReparent = reparent; 779 mWindowingModes = windowingModes != null ? 780 Arrays.copyOf(windowingModes, windowingModes.length) : null; 781 mActivityTypes = activityTypes != null ? 782 Arrays.copyOf(activityTypes, activityTypes.length) : null; 783 mToTop = toTop; 784 mLaunchOptions = launchOptions; 785 } 786 787 public HierarchyOp(@NonNull HierarchyOp copy) { 788 mType = copy.mType; 789 mContainer = copy.mContainer; 790 mReparent = copy.mReparent; 791 mToTop = copy.mToTop; 792 mWindowingModes = copy.mWindowingModes; 793 mActivityTypes = copy.mActivityTypes; 794 mLaunchOptions = copy.mLaunchOptions; 795 } 796 797 protected HierarchyOp(Parcel in) { 798 mType = in.readInt(); 799 mContainer = in.readStrongBinder(); 800 mReparent = in.readStrongBinder(); 801 mToTop = in.readBoolean(); 802 mWindowingModes = in.createIntArray(); 803 mActivityTypes = in.createIntArray(); 804 mLaunchOptions = in.readBundle(); 805 } 806 807 public int getType() { 808 return mType; 809 } 810 811 public boolean isReparent() { 812 return mType == HIERARCHY_OP_TYPE_REPARENT; 813 } 814 815 @Nullable 816 public IBinder getNewParent() { 817 return mReparent; 818 } 819 820 @NonNull 821 public IBinder getContainer() { 822 return mContainer; 823 } 824 825 @NonNull 826 public IBinder getAdjacentRoot() { 827 return mReparent; 828 } 829 830 public boolean getToTop() { 831 return mToTop; 832 } 833 834 public int[] getWindowingModes() { 835 return mWindowingModes; 836 } 837 838 public int[] getActivityTypes() { 839 return mActivityTypes; 840 } 841 842 @Nullable 843 public Bundle getLaunchOptions() { 844 return mLaunchOptions; 845 } 846 847 @Override 848 public String toString() { 849 switch (mType) { 850 case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: 851 return "{ChildrenTasksReparent: from=" + mContainer + " to=" + mReparent 852 + " mToTop=" + mToTop + " mWindowingMode=" + mWindowingModes 853 + " mActivityType=" + mActivityTypes + "}"; 854 case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: 855 return "{SetLaunchRoot: container=" + mContainer 856 + " mWindowingMode=" + mWindowingModes 857 + " mActivityType=" + mActivityTypes + "}"; 858 case HIERARCHY_OP_TYPE_REPARENT: 859 return "{reparent: " + mContainer + " to " + (mToTop ? "top of " : "bottom of ") 860 + mReparent + "}"; 861 case HIERARCHY_OP_TYPE_REORDER: 862 return "{reorder: " + mContainer + " to " + (mToTop ? "top" : "bottom") + "}"; 863 case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: 864 return "{SetAdjacentRoot: container=" + mContainer 865 + " adjacentRoot=" + mReparent + "}"; 866 case HIERARCHY_OP_TYPE_LAUNCH_TASK: 867 return "{LaunchTask: " + mLaunchOptions + "}"; 868 case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: 869 return "{SetAdjacentFlagRoot: container=" + mContainer + " clearRoot=" + mToTop 870 + "}"; 871 default: 872 return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent 873 + " mToTop=" + mToTop + " mWindowingMode=" + mWindowingModes 874 + " mActivityType=" + mActivityTypes + "}"; 875 } 876 } 877 878 @Override 879 public void writeToParcel(Parcel dest, int flags) { 880 dest.writeInt(mType); 881 dest.writeStrongBinder(mContainer); 882 dest.writeStrongBinder(mReparent); 883 dest.writeBoolean(mToTop); 884 dest.writeIntArray(mWindowingModes); 885 dest.writeIntArray(mActivityTypes); 886 dest.writeBundle(mLaunchOptions); 887 } 888 889 @Override 890 public int describeContents() { 891 return 0; 892 } 893 894 public static final Creator<HierarchyOp> CREATOR = new Creator<HierarchyOp>() { 895 @Override 896 public HierarchyOp createFromParcel(Parcel in) { 897 return new HierarchyOp(in); 898 } 899 900 @Override 901 public HierarchyOp[] newArray(int size) { 902 return new HierarchyOp[size]; 903 } 904 }; 905 } 906 } 907