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.PendingIntent; 23 import android.app.WindowConfiguration; 24 import android.content.Intent; 25 import android.content.pm.ActivityInfo; 26 import android.content.res.Configuration; 27 import android.graphics.Rect; 28 import android.os.Bundle; 29 import android.os.IBinder; 30 import android.os.Parcel; 31 import android.os.Parcelable; 32 import android.util.ArrayMap; 33 import android.view.SurfaceControl; 34 35 import java.util.ArrayList; 36 import java.util.Arrays; 37 import java.util.List; 38 import java.util.Map; 39 import java.util.Objects; 40 41 /** 42 * Represents a collection of operations on some WindowContainers that should be applied all at 43 * once. 44 * 45 * @hide 46 */ 47 @TestApi 48 public final class WindowContainerTransaction implements Parcelable { 49 private final ArrayMap<IBinder, Change> mChanges = new ArrayMap<>(); 50 51 // Flat list because re-order operations are order-dependent 52 private final ArrayList<HierarchyOp> mHierarchyOps = new ArrayList<>(); 53 54 @Nullable 55 private IBinder mErrorCallbackToken; 56 57 @Nullable 58 private ITaskFragmentOrganizer mTaskFragmentOrganizer; 59 WindowContainerTransaction()60 public WindowContainerTransaction() {} 61 WindowContainerTransaction(Parcel in)62 private WindowContainerTransaction(Parcel in) { 63 in.readMap(mChanges, null /* loader */); 64 in.readList(mHierarchyOps, null /* loader */); 65 mErrorCallbackToken = in.readStrongBinder(); 66 mTaskFragmentOrganizer = ITaskFragmentOrganizer.Stub.asInterface(in.readStrongBinder()); 67 } 68 getOrCreateChange(IBinder token)69 private Change getOrCreateChange(IBinder token) { 70 Change out = mChanges.get(token); 71 if (out == null) { 72 out = new Change(); 73 mChanges.put(token, out); 74 } 75 return out; 76 } 77 78 /** 79 * Resize a container. 80 */ 81 @NonNull setBounds( @onNull WindowContainerToken container,@NonNull Rect bounds)82 public WindowContainerTransaction setBounds( 83 @NonNull WindowContainerToken container,@NonNull Rect bounds) { 84 Change chg = getOrCreateChange(container.asBinder()); 85 chg.mConfiguration.windowConfiguration.setBounds(bounds); 86 chg.mConfigSetMask |= ActivityInfo.CONFIG_WINDOW_CONFIGURATION; 87 chg.mWindowSetMask |= WindowConfiguration.WINDOW_CONFIG_BOUNDS; 88 return this; 89 } 90 91 /** 92 * Resize a container's app bounds. This is the bounds used to report appWidth/Height to an 93 * app's DisplayInfo. It is derived by subtracting the overlapping portion of the navbar from 94 * the full bounds. 95 */ 96 @NonNull setAppBounds( @onNull WindowContainerToken container,@NonNull Rect appBounds)97 public WindowContainerTransaction setAppBounds( 98 @NonNull WindowContainerToken container,@NonNull Rect appBounds) { 99 Change chg = getOrCreateChange(container.asBinder()); 100 chg.mConfiguration.windowConfiguration.setAppBounds(appBounds); 101 chg.mConfigSetMask |= ActivityInfo.CONFIG_WINDOW_CONFIGURATION; 102 chg.mWindowSetMask |= WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS; 103 return this; 104 } 105 106 /** 107 * Resize a container's configuration size. The configuration size is what gets reported to the 108 * app via screenWidth/HeightDp and influences which resources get loaded. This size is 109 * derived by subtracting the overlapping portions of both the statusbar and the navbar from 110 * the full bounds. 111 */ 112 @NonNull setScreenSizeDp( @onNull WindowContainerToken container, int w, int h)113 public WindowContainerTransaction setScreenSizeDp( 114 @NonNull WindowContainerToken container, int w, int h) { 115 Change chg = getOrCreateChange(container.asBinder()); 116 chg.mConfiguration.screenWidthDp = w; 117 chg.mConfiguration.screenHeightDp = h; 118 chg.mConfigSetMask |= ActivityInfo.CONFIG_SCREEN_SIZE; 119 return this; 120 } 121 122 /** 123 * Notify {@link com.android.server.wm.PinnedTaskController} that the picture-in-picture task 124 * has finished the enter animation with the given bounds. 125 */ 126 @NonNull scheduleFinishEnterPip( @onNull WindowContainerToken container,@NonNull Rect bounds)127 public WindowContainerTransaction scheduleFinishEnterPip( 128 @NonNull WindowContainerToken container,@NonNull Rect bounds) { 129 Change chg = getOrCreateChange(container.asBinder()); 130 chg.mPinnedBounds = new Rect(bounds); 131 chg.mChangeMask |= Change.CHANGE_PIP_CALLBACK; 132 133 return this; 134 } 135 136 /** 137 * Send a SurfaceControl transaction to the server, which the server will apply in sync with 138 * the next bounds change. As this uses deferred transaction and not BLAST it is only 139 * able to sync with a single window, and the first visible window in this hierarchy of type 140 * BASE_APPLICATION to resize will be used. If there are bound changes included in this 141 * WindowContainer transaction (from setBounds or scheduleFinishEnterPip), the SurfaceControl 142 * transaction will be synced with those bounds. If there are no changes, then 143 * the SurfaceControl transaction will be synced with the next bounds change. This means 144 * that you can call this, apply the WindowContainer transaction, and then later call 145 * dismissPip() to achieve synchronization. 146 */ 147 @NonNull setBoundsChangeTransaction( @onNull WindowContainerToken container,@NonNull SurfaceControl.Transaction t)148 public WindowContainerTransaction setBoundsChangeTransaction( 149 @NonNull WindowContainerToken container,@NonNull SurfaceControl.Transaction t) { 150 Change chg = getOrCreateChange(container.asBinder()); 151 chg.mBoundsChangeTransaction = t; 152 chg.mChangeMask |= Change.CHANGE_BOUNDS_TRANSACTION; 153 return this; 154 } 155 156 /** 157 * Like {@link #setBoundsChangeTransaction} but instead queues up a setPosition/WindowCrop 158 * on a container's surface control. This is useful when a boundsChangeTransaction needs to be 159 * queued up on a Task that won't be organized until the end of this window-container 160 * transaction. 161 * 162 * This requires that, at the end of this transaction, `task` will be organized; otherwise 163 * the server will throw an IllegalArgumentException. 164 * 165 * WARNING: Use this carefully. Whatever is set here should match the expected bounds after 166 * the transaction completes since it will likely be replaced by it. This call is 167 * intended to pre-emptively set bounds on a surface in sync with a buffer when 168 * otherwise the new bounds and the new buffer would update on different frames. 169 * 170 * TODO(b/134365562): remove once TaskOrg drives full-screen or BLAST is enabled. 171 * 172 * @hide 173 */ 174 @NonNull setBoundsChangeTransaction( @onNull WindowContainerToken task, @NonNull Rect surfaceBounds)175 public WindowContainerTransaction setBoundsChangeTransaction( 176 @NonNull WindowContainerToken task, @NonNull Rect surfaceBounds) { 177 Change chg = getOrCreateChange(task.asBinder()); 178 if (chg.mBoundsChangeSurfaceBounds == null) { 179 chg.mBoundsChangeSurfaceBounds = new Rect(); 180 } 181 chg.mBoundsChangeSurfaceBounds.set(surfaceBounds); 182 chg.mChangeMask |= Change.CHANGE_BOUNDS_TRANSACTION_RECT; 183 return this; 184 } 185 186 /** 187 * Set the windowing mode of children of a given root task, without changing 188 * the windowing mode of the Task itself. This can be used during transitions 189 * for example to make the activity render it's fullscreen configuration 190 * while the Task is still in PIP, so you can complete the animation. 191 * 192 * TODO(b/134365562): Can be removed once TaskOrg drives full-screen 193 */ 194 @NonNull setActivityWindowingMode( @onNull WindowContainerToken container, int windowingMode)195 public WindowContainerTransaction setActivityWindowingMode( 196 @NonNull WindowContainerToken container, int windowingMode) { 197 Change chg = getOrCreateChange(container.asBinder()); 198 chg.mActivityWindowingMode = windowingMode; 199 return this; 200 } 201 202 /** 203 * Sets the windowing mode of the given container. 204 */ 205 @NonNull setWindowingMode( @onNull WindowContainerToken container, int windowingMode)206 public WindowContainerTransaction setWindowingMode( 207 @NonNull WindowContainerToken container, int windowingMode) { 208 Change chg = getOrCreateChange(container.asBinder()); 209 chg.mWindowingMode = windowingMode; 210 return this; 211 } 212 213 /** 214 * Sets whether a container or any of its children can be focusable. When {@code false}, no 215 * child can be focused; however, when {@code true}, it is still possible for children to be 216 * non-focusable due to WM policy. 217 */ 218 @NonNull setFocusable( @onNull WindowContainerToken container, boolean focusable)219 public WindowContainerTransaction setFocusable( 220 @NonNull WindowContainerToken container, boolean focusable) { 221 Change chg = getOrCreateChange(container.asBinder()); 222 chg.mFocusable = focusable; 223 chg.mChangeMask |= Change.CHANGE_FOCUSABLE; 224 return this; 225 } 226 227 /** 228 * Sets whether a container or its children should be hidden. When {@code false}, the existing 229 * visibility of the container applies, but when {@code true} the container will be forced 230 * to be hidden. 231 */ 232 @NonNull setHidden( @onNull WindowContainerToken container, boolean hidden)233 public WindowContainerTransaction setHidden( 234 @NonNull WindowContainerToken container, boolean hidden) { 235 Change chg = getOrCreateChange(container.asBinder()); 236 chg.mHidden = hidden; 237 chg.mChangeMask |= Change.CHANGE_HIDDEN; 238 return this; 239 } 240 241 /** 242 * Set the smallestScreenWidth of a container. 243 */ 244 @NonNull setSmallestScreenWidthDp( @onNull WindowContainerToken container, int widthDp)245 public WindowContainerTransaction setSmallestScreenWidthDp( 246 @NonNull WindowContainerToken container, int widthDp) { 247 Change cfg = getOrCreateChange(container.asBinder()); 248 cfg.mConfiguration.smallestScreenWidthDp = widthDp; 249 cfg.mConfigSetMask |= ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE; 250 return this; 251 } 252 253 /** 254 * Sets whether a container should ignore the orientation request from apps and windows below 255 * it. It currently only applies to {@link com.android.server.wm.DisplayArea}. When 256 * {@code false}, it may rotate based on the orientation request; When {@code true}, it can 257 * never specify orientation, but shows the fixed-orientation apps below it in the letterbox. 258 * @hide 259 */ 260 @NonNull setIgnoreOrientationRequest( @onNull WindowContainerToken container, boolean ignoreOrientationRequest)261 public WindowContainerTransaction setIgnoreOrientationRequest( 262 @NonNull WindowContainerToken container, boolean ignoreOrientationRequest) { 263 Change chg = getOrCreateChange(container.asBinder()); 264 chg.mIgnoreOrientationRequest = ignoreOrientationRequest; 265 chg.mChangeMask |= Change.CHANGE_IGNORE_ORIENTATION_REQUEST; 266 return this; 267 } 268 269 /** 270 * Reparents a container into another one. The effect of a {@code null} parent can vary. For 271 * example, reparenting a stack to {@code null} will reparent it to its display. 272 * 273 * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to 274 * the bottom. 275 */ 276 @NonNull reparent(@onNull WindowContainerToken child, @Nullable WindowContainerToken parent, boolean onTop)277 public WindowContainerTransaction reparent(@NonNull WindowContainerToken child, 278 @Nullable WindowContainerToken parent, boolean onTop) { 279 mHierarchyOps.add(HierarchyOp.createForReparent(child.asBinder(), 280 parent == null ? null : parent.asBinder(), 281 onTop)); 282 return this; 283 } 284 285 /** 286 * Reorders a container within its parent. 287 * 288 * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to 289 * the bottom. 290 */ 291 @NonNull reorder(@onNull WindowContainerToken child, boolean onTop)292 public WindowContainerTransaction reorder(@NonNull WindowContainerToken child, boolean onTop) { 293 mHierarchyOps.add(HierarchyOp.createForReorder(child.asBinder(), onTop)); 294 return this; 295 } 296 297 /** 298 * Reparent's all children tasks or the top task of {@param currentParent} in the specified 299 * {@param windowingMode} and {@param activityType} to {@param newParent} in their current 300 * z-order. 301 * 302 * @param currentParent of the tasks to perform the operation no. 303 * {@code null} will perform the operation on the display. 304 * @param newParent for the tasks. {@code null} will perform the operation on the display. 305 * @param windowingModes of the tasks to reparent. 306 * @param activityTypes of the tasks to reparent. 307 * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to 308 * the bottom. 309 * @param reparentTopOnly When {@code true}, only reparent the top task which fit windowingModes 310 * and activityTypes. 311 * @hide 312 */ 313 @NonNull reparentTasks(@ullable WindowContainerToken currentParent, @Nullable WindowContainerToken newParent, @Nullable int[] windowingModes, @Nullable int[] activityTypes, boolean onTop, boolean reparentTopOnly)314 public WindowContainerTransaction reparentTasks(@Nullable WindowContainerToken currentParent, 315 @Nullable WindowContainerToken newParent, @Nullable int[] windowingModes, 316 @Nullable int[] activityTypes, boolean onTop, boolean reparentTopOnly) { 317 mHierarchyOps.add(HierarchyOp.createForChildrenTasksReparent( 318 currentParent != null ? currentParent.asBinder() : null, 319 newParent != null ? newParent.asBinder() : null, 320 windowingModes, 321 activityTypes, 322 onTop, 323 reparentTopOnly)); 324 return this; 325 } 326 327 /** 328 * Reparent's all children tasks of {@param currentParent} in the specified 329 * {@param windowingMode} and {@param activityType} to {@param newParent} in their current 330 * z-order. 331 * 332 * @param currentParent of the tasks to perform the operation no. 333 * {@code null} will perform the operation on the display. 334 * @param newParent for the tasks. {@code null} will perform the operation on the display. 335 * @param windowingModes of the tasks to reparent. 336 * @param activityTypes of the tasks to reparent. 337 * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to 338 * the bottom. 339 */ 340 @NonNull reparentTasks(@ullable WindowContainerToken currentParent, @Nullable WindowContainerToken newParent, @Nullable int[] windowingModes, @Nullable int[] activityTypes, boolean onTop)341 public WindowContainerTransaction reparentTasks(@Nullable WindowContainerToken currentParent, 342 @Nullable WindowContainerToken newParent, @Nullable int[] windowingModes, 343 @Nullable int[] activityTypes, boolean onTop) { 344 return reparentTasks(currentParent, newParent, windowingModes, activityTypes, onTop, 345 false /* reparentTopOnly */); 346 } 347 348 /** 349 * Sets whether a container should be the launch root for the specified windowing mode and 350 * activity type. This currently only applies to Task containers created by organizer. 351 */ 352 @NonNull setLaunchRoot(@onNull WindowContainerToken container, @Nullable int[] windowingModes, @Nullable int[] activityTypes)353 public WindowContainerTransaction setLaunchRoot(@NonNull WindowContainerToken container, 354 @Nullable int[] windowingModes, @Nullable int[] activityTypes) { 355 mHierarchyOps.add(HierarchyOp.createForSetLaunchRoot( 356 container.asBinder(), 357 windowingModes, 358 activityTypes)); 359 return this; 360 } 361 362 /** 363 * Sets to containers adjacent to each other. Containers below two visible adjacent roots will 364 * be made invisible. This currently only applies to TaskFragment containers created by 365 * organizer. 366 * @param root1 the first root. 367 * @param root2 the second root. 368 */ 369 @NonNull setAdjacentRoots( @onNull WindowContainerToken root1, @NonNull WindowContainerToken root2, boolean moveTogether)370 public WindowContainerTransaction setAdjacentRoots( 371 @NonNull WindowContainerToken root1, @NonNull WindowContainerToken root2, 372 boolean moveTogether) { 373 mHierarchyOps.add(HierarchyOp.createForAdjacentRoots( 374 root1.asBinder(), 375 root2.asBinder(), 376 moveTogether)); 377 return this; 378 } 379 380 /** 381 * Sets the container as launch adjacent flag root. Task starting with 382 * {@link FLAG_ACTIVITY_LAUNCH_ADJACENT} will be launching to. 383 * 384 * @hide 385 */ 386 @NonNull setLaunchAdjacentFlagRoot( @onNull WindowContainerToken container)387 public WindowContainerTransaction setLaunchAdjacentFlagRoot( 388 @NonNull WindowContainerToken container) { 389 mHierarchyOps.add(HierarchyOp.createForSetLaunchAdjacentFlagRoot(container.asBinder(), 390 false /* clearRoot */)); 391 return this; 392 } 393 394 /** 395 * Clears launch adjacent flag root for the display area of passing container. 396 * 397 * @hide 398 */ 399 @NonNull clearLaunchAdjacentFlagRoot( @onNull WindowContainerToken container)400 public WindowContainerTransaction clearLaunchAdjacentFlagRoot( 401 @NonNull WindowContainerToken container) { 402 mHierarchyOps.add(HierarchyOp.createForSetLaunchAdjacentFlagRoot(container.asBinder(), 403 true /* clearRoot */)); 404 return this; 405 } 406 407 /** 408 * Starts a task by id. The task is expected to already exist (eg. as a recent task). 409 * @param taskId Id of task to start. 410 * @param options bundle containing ActivityOptions for the task's top activity. 411 * @hide 412 */ 413 @NonNull startTask(int taskId, @Nullable Bundle options)414 public WindowContainerTransaction startTask(int taskId, @Nullable Bundle options) { 415 mHierarchyOps.add(HierarchyOp.createForTaskLaunch(taskId, options)); 416 return this; 417 } 418 419 /** 420 * Sends a pending intent in sync. 421 * @param sender The PendingIntent sender. 422 * @param intent The fillIn intent to patch over the sender's base intent. 423 * @param options bundle containing ActivityOptions for the task's top activity. 424 * @hide 425 */ 426 @NonNull sendPendingIntent(PendingIntent sender, Intent intent, @Nullable Bundle options)427 public WindowContainerTransaction sendPendingIntent(PendingIntent sender, Intent intent, 428 @Nullable Bundle options) { 429 mHierarchyOps.add(new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT) 430 .setLaunchOptions(options) 431 .setPendingIntent(sender) 432 .setActivityIntent(intent) 433 .build()); 434 return this; 435 } 436 437 /** 438 * Creates a new TaskFragment with the given options. 439 * @param taskFragmentOptions the options used to create the TaskFragment. 440 */ 441 @NonNull createTaskFragment( @onNull TaskFragmentCreationParams taskFragmentOptions)442 public WindowContainerTransaction createTaskFragment( 443 @NonNull TaskFragmentCreationParams taskFragmentOptions) { 444 final HierarchyOp hierarchyOp = 445 new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT) 446 .setTaskFragmentCreationOptions(taskFragmentOptions) 447 .build(); 448 mHierarchyOps.add(hierarchyOp); 449 return this; 450 } 451 452 /** 453 * Deletes an existing TaskFragment. Any remaining activities below it will be destroyed. 454 * @param taskFragment the TaskFragment to be removed. 455 */ 456 @NonNull deleteTaskFragment( @onNull WindowContainerToken taskFragment)457 public WindowContainerTransaction deleteTaskFragment( 458 @NonNull WindowContainerToken taskFragment) { 459 final HierarchyOp hierarchyOp = 460 new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT) 461 .setContainer(taskFragment.asBinder()) 462 .build(); 463 mHierarchyOps.add(hierarchyOp); 464 return this; 465 } 466 467 /** 468 * Starts an activity in the TaskFragment. 469 * @param fragmentToken client assigned unique token to create TaskFragment with specified in 470 * {@link TaskFragmentCreationParams#getFragmentToken()}. 471 * @param callerToken the activity token that initialized the activity launch. 472 * @param activityIntent intent to start the activity. 473 * @param activityOptions ActivityOptions to start the activity with. 474 * @see android.content.Context#startActivity(Intent, Bundle). 475 */ 476 @NonNull startActivityInTaskFragment( @onNull IBinder fragmentToken, @NonNull IBinder callerToken, @NonNull Intent activityIntent, @Nullable Bundle activityOptions)477 public WindowContainerTransaction startActivityInTaskFragment( 478 @NonNull IBinder fragmentToken, @NonNull IBinder callerToken, 479 @NonNull Intent activityIntent, @Nullable Bundle activityOptions) { 480 final HierarchyOp hierarchyOp = 481 new HierarchyOp.Builder( 482 HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT) 483 .setContainer(fragmentToken) 484 .setReparentContainer(callerToken) 485 .setActivityIntent(activityIntent) 486 .setLaunchOptions(activityOptions) 487 .build(); 488 mHierarchyOps.add(hierarchyOp); 489 return this; 490 } 491 492 /** 493 * Moves an activity into the TaskFragment. 494 * @param fragmentToken client assigned unique token to create TaskFragment with specified in 495 * {@link TaskFragmentCreationParams#getFragmentToken()}. 496 * @param activityToken activity to be reparented. 497 */ 498 @NonNull reparentActivityToTaskFragment( @onNull IBinder fragmentToken, @NonNull IBinder activityToken)499 public WindowContainerTransaction reparentActivityToTaskFragment( 500 @NonNull IBinder fragmentToken, @NonNull IBinder activityToken) { 501 final HierarchyOp hierarchyOp = 502 new HierarchyOp.Builder( 503 HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT) 504 .setReparentContainer(fragmentToken) 505 .setContainer(activityToken) 506 .build(); 507 mHierarchyOps.add(hierarchyOp); 508 return this; 509 } 510 511 /** 512 * Reparents all children of one TaskFragment to another. 513 * @param oldParent children of this TaskFragment will be reparented. 514 * @param newParent the new parent TaskFragment to move the children to. If {@code null}, the 515 * children will be moved to the leaf Task. 516 */ 517 @NonNull reparentChildren( @onNull WindowContainerToken oldParent, @Nullable WindowContainerToken newParent)518 public WindowContainerTransaction reparentChildren( 519 @NonNull WindowContainerToken oldParent, 520 @Nullable WindowContainerToken newParent) { 521 final HierarchyOp hierarchyOp = 522 new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_CHILDREN) 523 .setContainer(oldParent.asBinder()) 524 .setReparentContainer(newParent != null ? newParent.asBinder() : null) 525 .build(); 526 mHierarchyOps.add(hierarchyOp); 527 return this; 528 } 529 530 /** 531 * Sets to TaskFragments adjacent to each other. Containers below two visible adjacent 532 * TaskFragments will be made invisible. This is similar to 533 * {@link #setAdjacentRoots(WindowContainerToken, WindowContainerToken)}, but can be used with 534 * fragmentTokens when that TaskFragments haven't been created (but will be created in the same 535 * {@link WindowContainerTransaction}). 536 * To reset it, pass {@code null} for {@code fragmentToken2}. 537 * @param fragmentToken1 client assigned unique token to create TaskFragment with specified 538 * in {@link TaskFragmentCreationParams#getFragmentToken()}. 539 * @param fragmentToken2 client assigned unique token to create TaskFragment with specified 540 * in {@link TaskFragmentCreationParams#getFragmentToken()}. If it is 541 * {@code null}, the transaction will reset the adjacent TaskFragment. 542 */ 543 @NonNull setAdjacentTaskFragments( @onNull IBinder fragmentToken1, @Nullable IBinder fragmentToken2, @Nullable TaskFragmentAdjacentParams params)544 public WindowContainerTransaction setAdjacentTaskFragments( 545 @NonNull IBinder fragmentToken1, @Nullable IBinder fragmentToken2, 546 @Nullable TaskFragmentAdjacentParams params) { 547 final HierarchyOp hierarchyOp = 548 new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS) 549 .setContainer(fragmentToken1) 550 .setReparentContainer(fragmentToken2) 551 .setLaunchOptions(params != null ? params.toBundle() : null) 552 .build(); 553 mHierarchyOps.add(hierarchyOp); 554 return this; 555 } 556 557 /** 558 * When this {@link WindowContainerTransaction} failed to finish on the server side, it will 559 * trigger callback with this {@param errorCallbackToken}. 560 * @param errorCallbackToken client provided token that will be passed back as parameter in 561 * the callback if there is an error on the server side. 562 * @see ITaskFragmentOrganizer#onTaskFragmentError 563 */ 564 @NonNull setErrorCallbackToken(@onNull IBinder errorCallbackToken)565 public WindowContainerTransaction setErrorCallbackToken(@NonNull IBinder errorCallbackToken) { 566 if (mErrorCallbackToken != null) { 567 throw new IllegalStateException("Can't set multiple error token for one transaction."); 568 } 569 mErrorCallbackToken = errorCallbackToken; 570 return this; 571 } 572 573 /** 574 * Sets the {@link TaskFragmentOrganizer} that applies this {@link WindowContainerTransaction}. 575 * When this is set, the server side will not check for the permission of 576 * {@link android.Manifest.permission#MANAGE_ACTIVITY_TASKS}, but will ensure this WCT only 577 * contains operations that are allowed for this organizer, such as modifying TaskFragments that 578 * are organized by this organizer. 579 * @hide 580 */ 581 @NonNull setTaskFragmentOrganizer(@onNull ITaskFragmentOrganizer organizer)582 WindowContainerTransaction setTaskFragmentOrganizer(@NonNull ITaskFragmentOrganizer organizer) { 583 if (mTaskFragmentOrganizer != null) { 584 throw new IllegalStateException("Can't set multiple organizers for one transaction."); 585 } 586 mTaskFragmentOrganizer = organizer; 587 return this; 588 } 589 590 /** 591 * Merges another WCT into this one. 592 * @param transfer When true, this will transfer everything from other potentially leaving 593 * other in an unusable state. When false, other is left alone, but 594 * SurfaceFlinger Transactions will not be merged. 595 * @hide 596 */ merge(WindowContainerTransaction other, boolean transfer)597 public void merge(WindowContainerTransaction other, boolean transfer) { 598 for (int i = 0, n = other.mChanges.size(); i < n; ++i) { 599 final IBinder key = other.mChanges.keyAt(i); 600 Change existing = mChanges.get(key); 601 if (existing == null) { 602 existing = new Change(); 603 mChanges.put(key, existing); 604 } 605 existing.merge(other.mChanges.valueAt(i), transfer); 606 } 607 for (int i = 0, n = other.mHierarchyOps.size(); i < n; ++i) { 608 mHierarchyOps.add(transfer ? other.mHierarchyOps.get(i) 609 : new HierarchyOp(other.mHierarchyOps.get(i))); 610 } 611 if (mErrorCallbackToken != null && other.mErrorCallbackToken != null && mErrorCallbackToken 612 != other.mErrorCallbackToken) { 613 throw new IllegalArgumentException("Can't merge two WCTs with different error token"); 614 } 615 final IBinder taskFragmentOrganizerAsBinder = mTaskFragmentOrganizer != null 616 ? mTaskFragmentOrganizer.asBinder() 617 : null; 618 final IBinder otherTaskFragmentOrganizerAsBinder = other.mTaskFragmentOrganizer != null 619 ? other.mTaskFragmentOrganizer.asBinder() 620 : null; 621 if (!Objects.equals(taskFragmentOrganizerAsBinder, otherTaskFragmentOrganizerAsBinder)) { 622 throw new IllegalArgumentException( 623 "Can't merge two WCTs from different TaskFragmentOrganizers"); 624 } 625 mErrorCallbackToken = mErrorCallbackToken != null 626 ? mErrorCallbackToken 627 : other.mErrorCallbackToken; 628 } 629 630 /** @hide */ isEmpty()631 public boolean isEmpty() { 632 return mChanges.isEmpty() && mHierarchyOps.isEmpty(); 633 } 634 635 /** @hide */ getChanges()636 public Map<IBinder, Change> getChanges() { 637 return mChanges; 638 } 639 640 /** @hide */ getHierarchyOps()641 public List<HierarchyOp> getHierarchyOps() { 642 return mHierarchyOps; 643 } 644 645 /** @hide */ 646 @Nullable getErrorCallbackToken()647 public IBinder getErrorCallbackToken() { 648 return mErrorCallbackToken; 649 } 650 651 /** @hide */ 652 @Nullable getTaskFragmentOrganizer()653 public ITaskFragmentOrganizer getTaskFragmentOrganizer() { 654 return mTaskFragmentOrganizer; 655 } 656 657 @Override 658 @NonNull toString()659 public String toString() { 660 return "WindowContainerTransaction {" 661 + " changes = " + mChanges 662 + " hops = " + mHierarchyOps 663 + " errorCallbackToken=" + mErrorCallbackToken 664 + " taskFragmentOrganizer=" + mTaskFragmentOrganizer 665 + " }"; 666 } 667 668 @Override 669 /** @hide */ writeToParcel(@onNull Parcel dest, int flags)670 public void writeToParcel(@NonNull Parcel dest, int flags) { 671 dest.writeMap(mChanges); 672 dest.writeList(mHierarchyOps); 673 dest.writeStrongBinder(mErrorCallbackToken); 674 dest.writeStrongInterface(mTaskFragmentOrganizer); 675 } 676 677 @Override 678 /** @hide */ describeContents()679 public int describeContents() { 680 return 0; 681 } 682 683 @NonNull 684 public static final Creator<WindowContainerTransaction> CREATOR = 685 new Creator<WindowContainerTransaction>() { 686 @Override 687 public WindowContainerTransaction createFromParcel(Parcel in) { 688 return new WindowContainerTransaction(in); 689 } 690 691 @Override 692 public WindowContainerTransaction[] newArray(int size) { 693 return new WindowContainerTransaction[size]; 694 } 695 }; 696 697 /** 698 * Holds changes on a single WindowContainer including Configuration changes. 699 * @hide 700 */ 701 public static class Change implements Parcelable { 702 public static final int CHANGE_FOCUSABLE = 1; 703 public static final int CHANGE_BOUNDS_TRANSACTION = 1 << 1; 704 public static final int CHANGE_PIP_CALLBACK = 1 << 2; 705 public static final int CHANGE_HIDDEN = 1 << 3; 706 public static final int CHANGE_BOUNDS_TRANSACTION_RECT = 1 << 4; 707 public static final int CHANGE_IGNORE_ORIENTATION_REQUEST = 1 << 5; 708 709 private final Configuration mConfiguration = new Configuration(); 710 private boolean mFocusable = true; 711 private boolean mHidden = false; 712 private boolean mIgnoreOrientationRequest = false; 713 714 private int mChangeMask = 0; 715 private @ActivityInfo.Config int mConfigSetMask = 0; 716 private @WindowConfiguration.WindowConfig int mWindowSetMask = 0; 717 718 private Rect mPinnedBounds = null; 719 private SurfaceControl.Transaction mBoundsChangeTransaction = null; 720 private Rect mBoundsChangeSurfaceBounds = null; 721 722 private int mActivityWindowingMode = -1; 723 private int mWindowingMode = -1; 724 Change()725 public Change() {} 726 Change(Parcel in)727 protected Change(Parcel in) { 728 mConfiguration.readFromParcel(in); 729 mFocusable = in.readBoolean(); 730 mHidden = in.readBoolean(); 731 mIgnoreOrientationRequest = in.readBoolean(); 732 mChangeMask = in.readInt(); 733 mConfigSetMask = in.readInt(); 734 mWindowSetMask = in.readInt(); 735 if ((mChangeMask & Change.CHANGE_PIP_CALLBACK) != 0) { 736 mPinnedBounds = new Rect(); 737 mPinnedBounds.readFromParcel(in); 738 } 739 if ((mChangeMask & Change.CHANGE_BOUNDS_TRANSACTION) != 0) { 740 mBoundsChangeTransaction = 741 SurfaceControl.Transaction.CREATOR.createFromParcel(in); 742 } 743 if ((mChangeMask & Change.CHANGE_BOUNDS_TRANSACTION_RECT) != 0) { 744 mBoundsChangeSurfaceBounds = new Rect(); 745 mBoundsChangeSurfaceBounds.readFromParcel(in); 746 } 747 748 mWindowingMode = in.readInt(); 749 mActivityWindowingMode = in.readInt(); 750 } 751 752 /** 753 * @param transfer When true, this will transfer other into this leaving other in an 754 * undefined state. Use this if you don't intend to use other. When false, 755 * SurfaceFlinger Transactions will not merge. 756 */ merge(Change other, boolean transfer)757 public void merge(Change other, boolean transfer) { 758 mConfiguration.setTo(other.mConfiguration, other.mConfigSetMask, other.mWindowSetMask); 759 mConfigSetMask |= other.mConfigSetMask; 760 mWindowSetMask |= other.mWindowSetMask; 761 if ((other.mChangeMask & CHANGE_FOCUSABLE) != 0) { 762 mFocusable = other.mFocusable; 763 } 764 if (transfer && (other.mChangeMask & CHANGE_BOUNDS_TRANSACTION) != 0) { 765 mBoundsChangeTransaction = other.mBoundsChangeTransaction; 766 other.mBoundsChangeTransaction = null; 767 } 768 if ((other.mChangeMask & CHANGE_PIP_CALLBACK) != 0) { 769 mPinnedBounds = transfer ? other.mPinnedBounds : new Rect(other.mPinnedBounds); 770 } 771 if ((other.mChangeMask & CHANGE_HIDDEN) != 0) { 772 mHidden = other.mHidden; 773 } 774 if ((other.mChangeMask & CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) { 775 mIgnoreOrientationRequest = other.mIgnoreOrientationRequest; 776 } 777 mChangeMask |= other.mChangeMask; 778 if (other.mActivityWindowingMode >= 0) { 779 mActivityWindowingMode = other.mActivityWindowingMode; 780 } 781 if (other.mWindowingMode >= 0) { 782 mWindowingMode = other.mWindowingMode; 783 } 784 if (other.mBoundsChangeSurfaceBounds != null) { 785 mBoundsChangeSurfaceBounds = transfer ? other.mBoundsChangeSurfaceBounds 786 : new Rect(other.mBoundsChangeSurfaceBounds); 787 } 788 } 789 getWindowingMode()790 public int getWindowingMode() { 791 return mWindowingMode; 792 } 793 getActivityWindowingMode()794 public int getActivityWindowingMode() { 795 return mActivityWindowingMode; 796 } 797 getConfiguration()798 public Configuration getConfiguration() { 799 return mConfiguration; 800 } 801 802 /** Gets the requested focusable state */ getFocusable()803 public boolean getFocusable() { 804 if ((mChangeMask & CHANGE_FOCUSABLE) == 0) { 805 throw new RuntimeException("Focusable not set. check CHANGE_FOCUSABLE first"); 806 } 807 return mFocusable; 808 } 809 810 /** Gets the requested hidden state */ getHidden()811 public boolean getHidden() { 812 if ((mChangeMask & CHANGE_HIDDEN) == 0) { 813 throw new RuntimeException("Hidden not set. check CHANGE_HIDDEN first"); 814 } 815 return mHidden; 816 } 817 818 /** Gets the requested state of whether to ignore orientation request. */ getIgnoreOrientationRequest()819 public boolean getIgnoreOrientationRequest() { 820 if ((mChangeMask & CHANGE_IGNORE_ORIENTATION_REQUEST) == 0) { 821 throw new RuntimeException("IgnoreOrientationRequest not set. " 822 + "Check CHANGE_IGNORE_ORIENTATION_REQUEST first"); 823 } 824 return mIgnoreOrientationRequest; 825 } 826 getChangeMask()827 public int getChangeMask() { 828 return mChangeMask; 829 } 830 831 @ActivityInfo.Config getConfigSetMask()832 public int getConfigSetMask() { 833 return mConfigSetMask; 834 } 835 836 @WindowConfiguration.WindowConfig getWindowSetMask()837 public int getWindowSetMask() { 838 return mWindowSetMask; 839 } 840 841 /** 842 * Returns the bounds to be used for scheduling the enter pip callback 843 * or null if no callback is to be scheduled. 844 */ getEnterPipBounds()845 public Rect getEnterPipBounds() { 846 return mPinnedBounds; 847 } 848 getBoundsChangeTransaction()849 public SurfaceControl.Transaction getBoundsChangeTransaction() { 850 return mBoundsChangeTransaction; 851 } 852 getBoundsChangeSurfaceBounds()853 public Rect getBoundsChangeSurfaceBounds() { 854 return mBoundsChangeSurfaceBounds; 855 } 856 857 @Override toString()858 public String toString() { 859 final boolean changesBounds = 860 (mConfigSetMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0 861 && ((mWindowSetMask & WindowConfiguration.WINDOW_CONFIG_BOUNDS) 862 != 0); 863 final boolean changesAppBounds = 864 (mConfigSetMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0 865 && ((mWindowSetMask & WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS) 866 != 0); 867 final boolean changesSs = (mConfigSetMask & ActivityInfo.CONFIG_SCREEN_SIZE) != 0; 868 final boolean changesSss = 869 (mConfigSetMask & ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE) != 0; 870 StringBuilder sb = new StringBuilder(); 871 sb.append('{'); 872 if (changesBounds) { 873 sb.append("bounds:" + mConfiguration.windowConfiguration.getBounds() + ","); 874 } 875 if (changesAppBounds) { 876 sb.append("appbounds:" + mConfiguration.windowConfiguration.getAppBounds() + ","); 877 } 878 if (changesSss) { 879 sb.append("ssw:" + mConfiguration.smallestScreenWidthDp + ","); 880 } 881 if (changesSs) { 882 sb.append("sw/h:" + mConfiguration.screenWidthDp + "x" 883 + mConfiguration.screenHeightDp + ","); 884 } 885 if ((mChangeMask & CHANGE_FOCUSABLE) != 0) { 886 sb.append("focusable:" + mFocusable + ","); 887 } 888 if (mBoundsChangeTransaction != null) { 889 sb.append("hasBoundsTransaction,"); 890 } 891 if ((mChangeMask & CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) { 892 sb.append("ignoreOrientationRequest:" + mIgnoreOrientationRequest + ","); 893 } 894 sb.append("}"); 895 return sb.toString(); 896 } 897 898 @Override writeToParcel(Parcel dest, int flags)899 public void writeToParcel(Parcel dest, int flags) { 900 mConfiguration.writeToParcel(dest, flags); 901 dest.writeBoolean(mFocusable); 902 dest.writeBoolean(mHidden); 903 dest.writeBoolean(mIgnoreOrientationRequest); 904 dest.writeInt(mChangeMask); 905 dest.writeInt(mConfigSetMask); 906 dest.writeInt(mWindowSetMask); 907 908 if (mPinnedBounds != null) { 909 mPinnedBounds.writeToParcel(dest, flags); 910 } 911 if (mBoundsChangeTransaction != null) { 912 mBoundsChangeTransaction.writeToParcel(dest, flags); 913 } 914 if (mBoundsChangeSurfaceBounds != null) { 915 mBoundsChangeSurfaceBounds.writeToParcel(dest, flags); 916 } 917 918 dest.writeInt(mWindowingMode); 919 dest.writeInt(mActivityWindowingMode); 920 } 921 922 @Override describeContents()923 public int describeContents() { 924 return 0; 925 } 926 927 public static final Creator<Change> CREATOR = new Creator<Change>() { 928 @Override 929 public Change createFromParcel(Parcel in) { 930 return new Change(in); 931 } 932 933 @Override 934 public Change[] newArray(int size) { 935 return new Change[size]; 936 } 937 }; 938 } 939 940 /** 941 * Holds information about a reparent/reorder operation in the hierarchy. This is separate from 942 * Changes because they must be executed in the same order that they are added. 943 * @hide 944 */ 945 public static class HierarchyOp implements Parcelable { 946 public static final int HIERARCHY_OP_TYPE_REPARENT = 0; 947 public static final int HIERARCHY_OP_TYPE_REORDER = 1; 948 public static final int HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT = 2; 949 public static final int HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT = 3; 950 public static final int HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS = 4; 951 public static final int HIERARCHY_OP_TYPE_LAUNCH_TASK = 5; 952 public static final int HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT = 6; 953 public static final int HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT = 7; 954 public static final int HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT = 8; 955 public static final int HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT = 9; 956 public static final int HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT = 10; 957 public static final int HIERARCHY_OP_TYPE_REPARENT_CHILDREN = 11; 958 public static final int HIERARCHY_OP_TYPE_PENDING_INTENT = 12; 959 public static final int HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS = 13; 960 961 // The following key(s) are for use with mLaunchOptions: 962 // When launching a task (eg. from recents), this is the taskId to be launched. 963 public static final String LAUNCH_KEY_TASK_ID = "android:transaction.hop.taskId"; 964 965 private final int mType; 966 967 // Container we are performing the operation on. 968 @Nullable 969 private IBinder mContainer; 970 971 // If this is same as mContainer, then only change position, don't reparent. 972 @Nullable 973 private IBinder mReparent; 974 975 // Moves/reparents to top of parent when {@code true}, otherwise moves/reparents to bottom. 976 private boolean mToTop; 977 978 private boolean mReparentTopOnly; 979 980 // TODO(b/207185041): Remove this once having a single-top root for split screen. 981 private boolean mMoveAdjacentTogether; 982 983 @Nullable 984 private int[] mWindowingModes; 985 986 @Nullable 987 private int[] mActivityTypes; 988 989 @Nullable 990 private Bundle mLaunchOptions; 991 992 @Nullable 993 private Intent mActivityIntent; 994 995 // Used as options for WindowContainerTransaction#createTaskFragment(). 996 @Nullable 997 private TaskFragmentCreationParams mTaskFragmentCreationOptions; 998 999 @Nullable 1000 private PendingIntent mPendingIntent; 1001 createForReparent( @onNull IBinder container, @Nullable IBinder reparent, boolean toTop)1002 public static HierarchyOp createForReparent( 1003 @NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) { 1004 return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REPARENT) 1005 .setContainer(container) 1006 .setReparentContainer(reparent) 1007 .setToTop(toTop) 1008 .build(); 1009 } 1010 createForReorder(@onNull IBinder container, boolean toTop)1011 public static HierarchyOp createForReorder(@NonNull IBinder container, boolean toTop) { 1012 return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REORDER) 1013 .setContainer(container) 1014 .setReparentContainer(container) 1015 .setToTop(toTop) 1016 .build(); 1017 } 1018 createForChildrenTasksReparent(IBinder currentParent, IBinder newParent, int[] windowingModes, int[] activityTypes, boolean onTop, boolean reparentTopOnly)1019 public static HierarchyOp createForChildrenTasksReparent(IBinder currentParent, 1020 IBinder newParent, int[] windowingModes, int[] activityTypes, boolean onTop, 1021 boolean reparentTopOnly) { 1022 return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT) 1023 .setContainer(currentParent) 1024 .setReparentContainer(newParent) 1025 .setWindowingModes(windowingModes) 1026 .setActivityTypes(activityTypes) 1027 .setToTop(onTop) 1028 .setReparentTopOnly(reparentTopOnly) 1029 .build(); 1030 } 1031 createForSetLaunchRoot(IBinder container, int[] windowingModes, int[] activityTypes)1032 public static HierarchyOp createForSetLaunchRoot(IBinder container, 1033 int[] windowingModes, int[] activityTypes) { 1034 return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT) 1035 .setContainer(container) 1036 .setWindowingModes(windowingModes) 1037 .setActivityTypes(activityTypes) 1038 .build(); 1039 } 1040 1041 /** Create a hierarchy op for setting adjacent root tasks. */ createForAdjacentRoots(IBinder root1, IBinder root2, boolean moveTogether)1042 public static HierarchyOp createForAdjacentRoots(IBinder root1, IBinder root2, 1043 boolean moveTogether) { 1044 return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS) 1045 .setContainer(root1) 1046 .setReparentContainer(root2) 1047 .setMoveAdjacentTogether(moveTogether) 1048 .build(); 1049 } 1050 1051 /** Create a hierarchy op for launching a task. */ createForTaskLaunch(int taskId, @Nullable Bundle options)1052 public static HierarchyOp createForTaskLaunch(int taskId, @Nullable Bundle options) { 1053 final Bundle fullOptions = options == null ? new Bundle() : options; 1054 fullOptions.putInt(LAUNCH_KEY_TASK_ID, taskId); 1055 return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_LAUNCH_TASK) 1056 .setToTop(true) 1057 .setLaunchOptions(fullOptions) 1058 .build(); 1059 } 1060 1061 /** Create a hierarchy op for setting launch adjacent flag root. */ createForSetLaunchAdjacentFlagRoot(IBinder container, boolean clearRoot)1062 public static HierarchyOp createForSetLaunchAdjacentFlagRoot(IBinder container, 1063 boolean clearRoot) { 1064 return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT) 1065 .setContainer(container) 1066 .setToTop(clearRoot) 1067 .build(); 1068 } 1069 1070 /** Only creates through {@link Builder}. */ HierarchyOp(int type)1071 private HierarchyOp(int type) { 1072 mType = type; 1073 } 1074 HierarchyOp(@onNull HierarchyOp copy)1075 public HierarchyOp(@NonNull HierarchyOp copy) { 1076 mType = copy.mType; 1077 mContainer = copy.mContainer; 1078 mReparent = copy.mReparent; 1079 mToTop = copy.mToTop; 1080 mReparentTopOnly = copy.mReparentTopOnly; 1081 mMoveAdjacentTogether = copy.mMoveAdjacentTogether; 1082 mWindowingModes = copy.mWindowingModes; 1083 mActivityTypes = copy.mActivityTypes; 1084 mLaunchOptions = copy.mLaunchOptions; 1085 mActivityIntent = copy.mActivityIntent; 1086 mTaskFragmentCreationOptions = copy.mTaskFragmentCreationOptions; 1087 mPendingIntent = copy.mPendingIntent; 1088 } 1089 HierarchyOp(Parcel in)1090 protected HierarchyOp(Parcel in) { 1091 mType = in.readInt(); 1092 mContainer = in.readStrongBinder(); 1093 mReparent = in.readStrongBinder(); 1094 mToTop = in.readBoolean(); 1095 mReparentTopOnly = in.readBoolean(); 1096 mMoveAdjacentTogether = in.readBoolean(); 1097 mWindowingModes = in.createIntArray(); 1098 mActivityTypes = in.createIntArray(); 1099 mLaunchOptions = in.readBundle(); 1100 mActivityIntent = in.readTypedObject(Intent.CREATOR); 1101 mTaskFragmentCreationOptions = in.readTypedObject(TaskFragmentCreationParams.CREATOR); 1102 mPendingIntent = in.readTypedObject(PendingIntent.CREATOR); 1103 } 1104 getType()1105 public int getType() { 1106 return mType; 1107 } 1108 isReparent()1109 public boolean isReparent() { 1110 return mType == HIERARCHY_OP_TYPE_REPARENT; 1111 } 1112 1113 @Nullable getNewParent()1114 public IBinder getNewParent() { 1115 return mReparent; 1116 } 1117 1118 @NonNull getContainer()1119 public IBinder getContainer() { 1120 return mContainer; 1121 } 1122 1123 @NonNull getAdjacentRoot()1124 public IBinder getAdjacentRoot() { 1125 return mReparent; 1126 } 1127 1128 @NonNull getCallingActivity()1129 public IBinder getCallingActivity() { 1130 return mReparent; 1131 } 1132 getToTop()1133 public boolean getToTop() { 1134 return mToTop; 1135 } 1136 getReparentTopOnly()1137 public boolean getReparentTopOnly() { 1138 return mReparentTopOnly; 1139 } 1140 getMoveAdjacentTogether()1141 public boolean getMoveAdjacentTogether() { 1142 return mMoveAdjacentTogether; 1143 } 1144 getWindowingModes()1145 public int[] getWindowingModes() { 1146 return mWindowingModes; 1147 } 1148 getActivityTypes()1149 public int[] getActivityTypes() { 1150 return mActivityTypes; 1151 } 1152 1153 @Nullable getLaunchOptions()1154 public Bundle getLaunchOptions() { 1155 return mLaunchOptions; 1156 } 1157 1158 @Nullable getActivityIntent()1159 public Intent getActivityIntent() { 1160 return mActivityIntent; 1161 } 1162 1163 @Nullable getTaskFragmentCreationOptions()1164 public TaskFragmentCreationParams getTaskFragmentCreationOptions() { 1165 return mTaskFragmentCreationOptions; 1166 } 1167 1168 @Nullable getPendingIntent()1169 public PendingIntent getPendingIntent() { 1170 return mPendingIntent; 1171 } 1172 1173 @Override toString()1174 public String toString() { 1175 switch (mType) { 1176 case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: 1177 return "{ChildrenTasksReparent: from=" + mContainer + " to=" + mReparent 1178 + " mToTop=" + mToTop + " mReparentTopOnly=" + mReparentTopOnly 1179 + " mWindowingMode=" + Arrays.toString(mWindowingModes) 1180 + " mActivityType=" + Arrays.toString(mActivityTypes) + "}"; 1181 case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: 1182 return "{SetLaunchRoot: container=" + mContainer 1183 + " mWindowingMode=" + Arrays.toString(mWindowingModes) 1184 + " mActivityType=" + Arrays.toString(mActivityTypes) + "}"; 1185 case HIERARCHY_OP_TYPE_REPARENT: 1186 return "{reparent: " + mContainer + " to " + (mToTop ? "top of " : "bottom of ") 1187 + mReparent + "}"; 1188 case HIERARCHY_OP_TYPE_REORDER: 1189 return "{reorder: " + mContainer + " to " + (mToTop ? "top" : "bottom") + "}"; 1190 case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: 1191 return "{SetAdjacentRoot: container=" + mContainer 1192 + " adjacentRoot=" + mReparent + " mMoveAdjacentTogether=" 1193 + mMoveAdjacentTogether + "}"; 1194 case HIERARCHY_OP_TYPE_LAUNCH_TASK: 1195 return "{LaunchTask: " + mLaunchOptions + "}"; 1196 case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: 1197 return "{SetAdjacentFlagRoot: container=" + mContainer + " clearRoot=" + mToTop 1198 + "}"; 1199 case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT: 1200 return "{CreateTaskFragment: options=" + mTaskFragmentCreationOptions + "}"; 1201 case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT: 1202 return "{DeleteTaskFragment: taskFragment=" + mContainer + "}"; 1203 case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: 1204 return "{StartActivityInTaskFragment: fragmentToken=" + mContainer + " intent=" 1205 + mActivityIntent + " options=" + mLaunchOptions + "}"; 1206 case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT: 1207 return "{ReparentActivityToTaskFragment: fragmentToken=" + mReparent 1208 + " activity=" + mContainer + "}"; 1209 case HIERARCHY_OP_TYPE_REPARENT_CHILDREN: 1210 return "{ReparentChildren: oldParent=" + mContainer + " newParent=" + mReparent 1211 + "}"; 1212 case HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS: 1213 return "{SetAdjacentTaskFragments: container=" + mContainer 1214 + " adjacentContainer=" + mReparent + "}"; 1215 default: 1216 return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent 1217 + " mToTop=" + mToTop 1218 + " mWindowingMode=" + Arrays.toString(mWindowingModes) 1219 + " mActivityType=" + Arrays.toString(mActivityTypes) + "}"; 1220 } 1221 } 1222 1223 @Override writeToParcel(Parcel dest, int flags)1224 public void writeToParcel(Parcel dest, int flags) { 1225 dest.writeInt(mType); 1226 dest.writeStrongBinder(mContainer); 1227 dest.writeStrongBinder(mReparent); 1228 dest.writeBoolean(mToTop); 1229 dest.writeBoolean(mReparentTopOnly); 1230 dest.writeBoolean(mMoveAdjacentTogether); 1231 dest.writeIntArray(mWindowingModes); 1232 dest.writeIntArray(mActivityTypes); 1233 dest.writeBundle(mLaunchOptions); 1234 dest.writeTypedObject(mActivityIntent, flags); 1235 dest.writeTypedObject(mTaskFragmentCreationOptions, flags); 1236 dest.writeTypedObject(mPendingIntent, flags); 1237 } 1238 1239 @Override describeContents()1240 public int describeContents() { 1241 return 0; 1242 } 1243 1244 public static final Creator<HierarchyOp> CREATOR = new Creator<HierarchyOp>() { 1245 @Override 1246 public HierarchyOp createFromParcel(Parcel in) { 1247 return new HierarchyOp(in); 1248 } 1249 1250 @Override 1251 public HierarchyOp[] newArray(int size) { 1252 return new HierarchyOp[size]; 1253 } 1254 }; 1255 1256 private static class Builder { 1257 1258 private final int mType; 1259 1260 @Nullable 1261 private IBinder mContainer; 1262 1263 @Nullable 1264 private IBinder mReparent; 1265 1266 private boolean mToTop; 1267 1268 private boolean mReparentTopOnly; 1269 1270 private boolean mMoveAdjacentTogether; 1271 1272 @Nullable 1273 private int[] mWindowingModes; 1274 1275 @Nullable 1276 private int[] mActivityTypes; 1277 1278 @Nullable 1279 private Bundle mLaunchOptions; 1280 1281 @Nullable 1282 private Intent mActivityIntent; 1283 1284 @Nullable 1285 private TaskFragmentCreationParams mTaskFragmentCreationOptions; 1286 1287 @Nullable 1288 private PendingIntent mPendingIntent; 1289 Builder(int type)1290 Builder(int type) { 1291 mType = type; 1292 } 1293 setContainer(@ullable IBinder container)1294 Builder setContainer(@Nullable IBinder container) { 1295 mContainer = container; 1296 return this; 1297 } 1298 setReparentContainer(@ullable IBinder reparentContainer)1299 Builder setReparentContainer(@Nullable IBinder reparentContainer) { 1300 mReparent = reparentContainer; 1301 return this; 1302 } 1303 setToTop(boolean toTop)1304 Builder setToTop(boolean toTop) { 1305 mToTop = toTop; 1306 return this; 1307 } 1308 setReparentTopOnly(boolean reparentTopOnly)1309 Builder setReparentTopOnly(boolean reparentTopOnly) { 1310 mReparentTopOnly = reparentTopOnly; 1311 return this; 1312 } 1313 setMoveAdjacentTogether(boolean moveAdjacentTogether)1314 Builder setMoveAdjacentTogether(boolean moveAdjacentTogether) { 1315 mMoveAdjacentTogether = moveAdjacentTogether; 1316 return this; 1317 } 1318 setWindowingModes(@ullable int[] windowingModes)1319 Builder setWindowingModes(@Nullable int[] windowingModes) { 1320 mWindowingModes = windowingModes; 1321 return this; 1322 } 1323 setActivityTypes(@ullable int[] activityTypes)1324 Builder setActivityTypes(@Nullable int[] activityTypes) { 1325 mActivityTypes = activityTypes; 1326 return this; 1327 } 1328 setLaunchOptions(@ullable Bundle launchOptions)1329 Builder setLaunchOptions(@Nullable Bundle launchOptions) { 1330 mLaunchOptions = launchOptions; 1331 return this; 1332 } 1333 setActivityIntent(@ullable Intent activityIntent)1334 Builder setActivityIntent(@Nullable Intent activityIntent) { 1335 mActivityIntent = activityIntent; 1336 return this; 1337 } 1338 setPendingIntent(@ullable PendingIntent sender)1339 Builder setPendingIntent(@Nullable PendingIntent sender) { 1340 mPendingIntent = sender; 1341 return this; 1342 } 1343 setTaskFragmentCreationOptions( @ullable TaskFragmentCreationParams taskFragmentCreationOptions)1344 Builder setTaskFragmentCreationOptions( 1345 @Nullable TaskFragmentCreationParams taskFragmentCreationOptions) { 1346 mTaskFragmentCreationOptions = taskFragmentCreationOptions; 1347 return this; 1348 } 1349 build()1350 HierarchyOp build() { 1351 final HierarchyOp hierarchyOp = new HierarchyOp(mType); 1352 hierarchyOp.mContainer = mContainer; 1353 hierarchyOp.mReparent = mReparent; 1354 hierarchyOp.mWindowingModes = mWindowingModes != null 1355 ? Arrays.copyOf(mWindowingModes, mWindowingModes.length) 1356 : null; 1357 hierarchyOp.mActivityTypes = mActivityTypes != null 1358 ? Arrays.copyOf(mActivityTypes, mActivityTypes.length) 1359 : null; 1360 hierarchyOp.mToTop = mToTop; 1361 hierarchyOp.mReparentTopOnly = mReparentTopOnly; 1362 hierarchyOp.mMoveAdjacentTogether = mMoveAdjacentTogether; 1363 hierarchyOp.mLaunchOptions = mLaunchOptions; 1364 hierarchyOp.mActivityIntent = mActivityIntent; 1365 hierarchyOp.mPendingIntent = mPendingIntent; 1366 hierarchyOp.mTaskFragmentCreationOptions = mTaskFragmentCreationOptions; 1367 1368 return hierarchyOp; 1369 } 1370 } 1371 } 1372 1373 /** 1374 * Helper class for building an options Bundle that can be used to set adjacent rules of 1375 * TaskFragments. 1376 */ 1377 public static class TaskFragmentAdjacentParams { 1378 private static final String DELAY_PRIMARY_LAST_ACTIVITY_REMOVAL = 1379 "android:transaction.adjacent.option.delay_primary_removal"; 1380 private static final String DELAY_SECONDARY_LAST_ACTIVITY_REMOVAL = 1381 "android:transaction.adjacent.option.delay_secondary_removal"; 1382 1383 private boolean mDelayPrimaryLastActivityRemoval; 1384 private boolean mDelaySecondaryLastActivityRemoval; 1385 TaskFragmentAdjacentParams()1386 public TaskFragmentAdjacentParams() { 1387 } 1388 TaskFragmentAdjacentParams(@onNull Bundle bundle)1389 public TaskFragmentAdjacentParams(@NonNull Bundle bundle) { 1390 mDelayPrimaryLastActivityRemoval = bundle.getBoolean( 1391 DELAY_PRIMARY_LAST_ACTIVITY_REMOVAL); 1392 mDelaySecondaryLastActivityRemoval = bundle.getBoolean( 1393 DELAY_SECONDARY_LAST_ACTIVITY_REMOVAL); 1394 } 1395 1396 /** @see #shouldDelayPrimaryLastActivityRemoval() */ setShouldDelayPrimaryLastActivityRemoval(boolean delay)1397 public void setShouldDelayPrimaryLastActivityRemoval(boolean delay) { 1398 mDelayPrimaryLastActivityRemoval = delay; 1399 } 1400 1401 /** @see #shouldDelaySecondaryLastActivityRemoval() */ setShouldDelaySecondaryLastActivityRemoval(boolean delay)1402 public void setShouldDelaySecondaryLastActivityRemoval(boolean delay) { 1403 mDelaySecondaryLastActivityRemoval = delay; 1404 } 1405 1406 /** 1407 * Whether to delay the last activity of the primary adjacent TaskFragment being immediately 1408 * removed while finishing. 1409 * <p> 1410 * It is usually set to {@code true} to give organizer an opportunity to perform other 1411 * actions or animations. An example is to finish together with the adjacent TaskFragment. 1412 * </p> 1413 */ shouldDelayPrimaryLastActivityRemoval()1414 public boolean shouldDelayPrimaryLastActivityRemoval() { 1415 return mDelayPrimaryLastActivityRemoval; 1416 } 1417 1418 /** 1419 * Similar to {@link #shouldDelayPrimaryLastActivityRemoval()}, but for the secondary 1420 * TaskFragment. 1421 */ shouldDelaySecondaryLastActivityRemoval()1422 public boolean shouldDelaySecondaryLastActivityRemoval() { 1423 return mDelaySecondaryLastActivityRemoval; 1424 } 1425 toBundle()1426 Bundle toBundle() { 1427 final Bundle b = new Bundle(); 1428 b.putBoolean(DELAY_PRIMARY_LAST_ACTIVITY_REMOVAL, mDelayPrimaryLastActivityRemoval); 1429 b.putBoolean(DELAY_SECONDARY_LAST_ACTIVITY_REMOVAL, mDelaySecondaryLastActivityRemoval); 1430 return b; 1431 } 1432 } 1433 } 1434