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.IBinder; 27 import android.os.Parcel; 28 import android.os.Parcelable; 29 import android.util.ArrayMap; 30 import android.view.SurfaceControl; 31 32 import java.util.ArrayList; 33 import java.util.List; 34 import java.util.Map; 35 36 /** 37 * Represents a collection of operations on some WindowContainers that should be applied all at 38 * once. 39 * 40 * @hide 41 */ 42 @TestApi 43 public final class WindowContainerTransaction implements Parcelable { 44 private final ArrayMap<IBinder, Change> mChanges = new ArrayMap<>(); 45 46 // Flat list because re-order operations are order-dependent 47 private final ArrayList<HierarchyOp> mHierarchyOps = new ArrayList<>(); 48 WindowContainerTransaction()49 public WindowContainerTransaction() {} 50 WindowContainerTransaction(Parcel in)51 private WindowContainerTransaction(Parcel in) { 52 in.readMap(mChanges, null /* loader */); 53 in.readList(mHierarchyOps, null /* loader */); 54 } 55 getOrCreateChange(IBinder token)56 private Change getOrCreateChange(IBinder token) { 57 Change out = mChanges.get(token); 58 if (out == null) { 59 out = new Change(); 60 mChanges.put(token, out); 61 } 62 return out; 63 } 64 65 /** 66 * Resize a container. 67 */ 68 @NonNull setBounds( @onNull WindowContainerToken container,@NonNull Rect bounds)69 public WindowContainerTransaction setBounds( 70 @NonNull WindowContainerToken container,@NonNull Rect bounds) { 71 Change chg = getOrCreateChange(container.asBinder()); 72 chg.mConfiguration.windowConfiguration.setBounds(bounds); 73 chg.mConfigSetMask |= ActivityInfo.CONFIG_WINDOW_CONFIGURATION; 74 chg.mWindowSetMask |= WindowConfiguration.WINDOW_CONFIG_BOUNDS; 75 return this; 76 } 77 78 /** 79 * Resize a container's app bounds. This is the bounds used to report appWidth/Height to an 80 * app's DisplayInfo. It is derived by subtracting the overlapping portion of the navbar from 81 * the full bounds. 82 */ 83 @NonNull setAppBounds( @onNull WindowContainerToken container,@NonNull Rect appBounds)84 public WindowContainerTransaction setAppBounds( 85 @NonNull WindowContainerToken container,@NonNull Rect appBounds) { 86 Change chg = getOrCreateChange(container.asBinder()); 87 chg.mConfiguration.windowConfiguration.setAppBounds(appBounds); 88 chg.mConfigSetMask |= ActivityInfo.CONFIG_WINDOW_CONFIGURATION; 89 chg.mWindowSetMask |= WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS; 90 return this; 91 } 92 93 /** 94 * Resize a container's configuration size. The configuration size is what gets reported to the 95 * app via screenWidth/HeightDp and influences which resources get loaded. This size is 96 * derived by subtracting the overlapping portions of both the statusbar and the navbar from 97 * the full bounds. 98 */ 99 @NonNull setScreenSizeDp( @onNull WindowContainerToken container, int w, int h)100 public WindowContainerTransaction setScreenSizeDp( 101 @NonNull WindowContainerToken container, int w, int h) { 102 Change chg = getOrCreateChange(container.asBinder()); 103 chg.mConfiguration.screenWidthDp = w; 104 chg.mConfiguration.screenHeightDp = h; 105 chg.mConfigSetMask |= ActivityInfo.CONFIG_SCREEN_SIZE; 106 return this; 107 } 108 109 /** 110 * Notify activities within the hierarchy of a container that they have entered picture-in-picture 111 * mode with the given bounds. 112 */ 113 @NonNull scheduleFinishEnterPip( @onNull WindowContainerToken container,@NonNull Rect bounds)114 public WindowContainerTransaction scheduleFinishEnterPip( 115 @NonNull WindowContainerToken container,@NonNull Rect bounds) { 116 Change chg = getOrCreateChange(container.asBinder()); 117 chg.mPinnedBounds = new Rect(bounds); 118 chg.mChangeMask |= Change.CHANGE_PIP_CALLBACK; 119 120 return this; 121 } 122 123 /** 124 * Send a SurfaceControl transaction to the server, which the server will apply in sync with 125 * the next bounds change. As this uses deferred transaction and not BLAST it is only 126 * able to sync with a single window, and the first visible window in this hierarchy of type 127 * BASE_APPLICATION to resize will be used. If there are bound changes included in this 128 * WindowContainer transaction (from setBounds or scheduleFinishEnterPip), the SurfaceControl 129 * transaction will be synced with those bounds. If there are no changes, then 130 * the SurfaceControl transaction will be synced with the next bounds change. This means 131 * that you can call this, apply the WindowContainer transaction, and then later call 132 * dismissPip() to achieve synchronization. 133 */ 134 @NonNull setBoundsChangeTransaction( @onNull WindowContainerToken container,@NonNull SurfaceControl.Transaction t)135 public WindowContainerTransaction setBoundsChangeTransaction( 136 @NonNull WindowContainerToken container,@NonNull SurfaceControl.Transaction t) { 137 Change chg = getOrCreateChange(container.asBinder()); 138 chg.mBoundsChangeTransaction = t; 139 chg.mChangeMask |= Change.CHANGE_BOUNDS_TRANSACTION; 140 return this; 141 } 142 143 /** 144 * Like {@link #setBoundsChangeTransaction} but instead queues up a setPosition/WindowCrop 145 * on a container's surface control. This is useful when a boundsChangeTransaction needs to be 146 * queued up on a Task that won't be organized until the end of this window-container 147 * transaction. 148 * 149 * This requires that, at the end of this transaction, `task` will be organized; otherwise 150 * the server will throw an IllegalArgumentException. 151 * 152 * WARNING: Use this carefully. Whatever is set here should match the expected bounds after 153 * the transaction completes since it will likely be replaced by it. This call is 154 * intended to pre-emptively set bounds on a surface in sync with a buffer when 155 * otherwise the new bounds and the new buffer would update on different frames. 156 * 157 * TODO(b/134365562): remove once TaskOrg drives full-screen or BLAST is enabled. 158 * 159 * @hide 160 */ 161 @NonNull setBoundsChangeTransaction( @onNull WindowContainerToken task, @NonNull Rect surfaceBounds)162 public WindowContainerTransaction setBoundsChangeTransaction( 163 @NonNull WindowContainerToken task, @NonNull Rect surfaceBounds) { 164 Change chg = getOrCreateChange(task.asBinder()); 165 if (chg.mBoundsChangeSurfaceBounds == null) { 166 chg.mBoundsChangeSurfaceBounds = new Rect(); 167 } 168 chg.mBoundsChangeSurfaceBounds.set(surfaceBounds); 169 chg.mChangeMask |= Change.CHANGE_BOUNDS_TRANSACTION_RECT; 170 return this; 171 } 172 173 /** 174 * Set the windowing mode of children of a given root task, without changing 175 * the windowing mode of the Task itself. This can be used during transitions 176 * for example to make the activity render it's fullscreen configuration 177 * while the Task is still in PIP, so you can complete the animation. 178 * 179 * TODO(b/134365562): Can be removed once TaskOrg drives full-screen 180 */ 181 @NonNull setActivityWindowingMode( @onNull WindowContainerToken container, int windowingMode)182 public WindowContainerTransaction setActivityWindowingMode( 183 @NonNull WindowContainerToken container, int windowingMode) { 184 Change chg = getOrCreateChange(container.asBinder()); 185 chg.mActivityWindowingMode = windowingMode; 186 return this; 187 } 188 189 /** 190 * Sets the windowing mode of the given container. 191 */ 192 @NonNull setWindowingMode( @onNull WindowContainerToken container, int windowingMode)193 public WindowContainerTransaction setWindowingMode( 194 @NonNull WindowContainerToken container, int windowingMode) { 195 Change chg = getOrCreateChange(container.asBinder()); 196 chg.mWindowingMode = windowingMode; 197 return this; 198 } 199 200 /** 201 * Sets whether a container or any of its children can be focusable. When {@code false}, no 202 * child can be focused; however, when {@code true}, it is still possible for children to be 203 * non-focusable due to WM policy. 204 */ 205 @NonNull setFocusable( @onNull WindowContainerToken container, boolean focusable)206 public WindowContainerTransaction setFocusable( 207 @NonNull WindowContainerToken container, boolean focusable) { 208 Change chg = getOrCreateChange(container.asBinder()); 209 chg.mFocusable = focusable; 210 chg.mChangeMask |= Change.CHANGE_FOCUSABLE; 211 return this; 212 } 213 214 /** 215 * Sets whether a container or its children should be hidden. When {@code false}, the existing 216 * visibility of the container applies, but when {@code true} the container will be forced 217 * to be hidden. 218 */ 219 @NonNull setHidden( @onNull WindowContainerToken container, boolean hidden)220 public WindowContainerTransaction setHidden( 221 @NonNull WindowContainerToken container, boolean hidden) { 222 Change chg = getOrCreateChange(container.asBinder()); 223 chg.mHidden = hidden; 224 chg.mChangeMask |= Change.CHANGE_HIDDEN; 225 return this; 226 } 227 228 /** 229 * Set the smallestScreenWidth of a container. 230 */ 231 @NonNull setSmallestScreenWidthDp( @onNull WindowContainerToken container, int widthDp)232 public WindowContainerTransaction setSmallestScreenWidthDp( 233 @NonNull WindowContainerToken container, int widthDp) { 234 Change cfg = getOrCreateChange(container.asBinder()); 235 cfg.mConfiguration.smallestScreenWidthDp = widthDp; 236 cfg.mConfigSetMask |= ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE; 237 return this; 238 } 239 240 /** 241 * Reparents a container into another one. The effect of a {@code null} parent can vary. For 242 * example, reparenting a stack to {@code null} will reparent it to its display. 243 * 244 * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to 245 * the bottom. 246 */ 247 @NonNull reparent(@onNull WindowContainerToken child, @Nullable WindowContainerToken parent, boolean onTop)248 public WindowContainerTransaction reparent(@NonNull WindowContainerToken child, 249 @Nullable WindowContainerToken parent, boolean onTop) { 250 mHierarchyOps.add(new HierarchyOp(child.asBinder(), 251 parent == null ? null : parent.asBinder(), onTop)); 252 return this; 253 } 254 255 /** 256 * Reorders a container within its parent. 257 * 258 * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to 259 * the bottom. 260 */ 261 @NonNull reorder(@onNull WindowContainerToken child, boolean onTop)262 public WindowContainerTransaction reorder(@NonNull WindowContainerToken child, boolean onTop) { 263 mHierarchyOps.add(new HierarchyOp(child.asBinder(), onTop)); 264 return this; 265 } 266 267 /** 268 * Merges another WCT into this one. 269 * @param transfer When true, this will transfer everything from other potentially leaving 270 * other in an unusable state. When false, other is left alone, but 271 * SurfaceFlinger Transactions will not be merged. 272 * @hide 273 */ merge(WindowContainerTransaction other, boolean transfer)274 public void merge(WindowContainerTransaction other, boolean transfer) { 275 for (int i = 0, n = other.mChanges.size(); i < n; ++i) { 276 final IBinder key = other.mChanges.keyAt(i); 277 Change existing = mChanges.get(key); 278 if (existing == null) { 279 existing = new Change(); 280 mChanges.put(key, existing); 281 } 282 existing.merge(other.mChanges.valueAt(i), transfer); 283 } 284 for (int i = 0, n = other.mHierarchyOps.size(); i < n; ++i) { 285 mHierarchyOps.add(transfer ? other.mHierarchyOps.get(i) 286 : new HierarchyOp(other.mHierarchyOps.get(i))); 287 } 288 } 289 290 /** @hide */ getChanges()291 public Map<IBinder, Change> getChanges() { 292 return mChanges; 293 } 294 295 /** @hide */ getHierarchyOps()296 public List<HierarchyOp> getHierarchyOps() { 297 return mHierarchyOps; 298 } 299 300 @Override 301 @NonNull toString()302 public String toString() { 303 return "WindowContainerTransaction { changes = " + mChanges + " hops = " + mHierarchyOps 304 + " }"; 305 } 306 307 @Override 308 /** @hide */ writeToParcel(@onNull Parcel dest, int flags)309 public void writeToParcel(@NonNull Parcel dest, int flags) { 310 dest.writeMap(mChanges); 311 dest.writeList(mHierarchyOps); 312 } 313 314 @Override 315 /** @hide */ describeContents()316 public int describeContents() { 317 return 0; 318 } 319 320 @NonNull 321 public static final Creator<WindowContainerTransaction> CREATOR = 322 new Creator<WindowContainerTransaction>() { 323 @Override 324 public WindowContainerTransaction createFromParcel(Parcel in) { 325 return new WindowContainerTransaction(in); 326 } 327 328 @Override 329 public WindowContainerTransaction[] newArray(int size) { 330 return new WindowContainerTransaction[size]; 331 } 332 }; 333 334 /** 335 * Holds changes on a single WindowContainer including Configuration changes. 336 * @hide 337 */ 338 public static class Change implements Parcelable { 339 public static final int CHANGE_FOCUSABLE = 1; 340 public static final int CHANGE_BOUNDS_TRANSACTION = 1 << 1; 341 public static final int CHANGE_PIP_CALLBACK = 1 << 2; 342 public static final int CHANGE_HIDDEN = 1 << 3; 343 public static final int CHANGE_BOUNDS_TRANSACTION_RECT = 1 << 4; 344 345 private final Configuration mConfiguration = new Configuration(); 346 private boolean mFocusable = true; 347 private boolean mHidden = false; 348 private int mChangeMask = 0; 349 private @ActivityInfo.Config int mConfigSetMask = 0; 350 private @WindowConfiguration.WindowConfig int mWindowSetMask = 0; 351 352 private Rect mPinnedBounds = null; 353 private SurfaceControl.Transaction mBoundsChangeTransaction = null; 354 private Rect mBoundsChangeSurfaceBounds = null; 355 356 private int mActivityWindowingMode = -1; 357 private int mWindowingMode = -1; 358 Change()359 public Change() {} 360 Change(Parcel in)361 protected Change(Parcel in) { 362 mConfiguration.readFromParcel(in); 363 mFocusable = in.readBoolean(); 364 mHidden = in.readBoolean(); 365 mChangeMask = in.readInt(); 366 mConfigSetMask = in.readInt(); 367 mWindowSetMask = in.readInt(); 368 if ((mChangeMask & Change.CHANGE_PIP_CALLBACK) != 0) { 369 mPinnedBounds = new Rect(); 370 mPinnedBounds.readFromParcel(in); 371 } 372 if ((mChangeMask & Change.CHANGE_BOUNDS_TRANSACTION) != 0) { 373 mBoundsChangeTransaction = 374 SurfaceControl.Transaction.CREATOR.createFromParcel(in); 375 } 376 if ((mChangeMask & Change.CHANGE_BOUNDS_TRANSACTION_RECT) != 0) { 377 mBoundsChangeSurfaceBounds = new Rect(); 378 mBoundsChangeSurfaceBounds.readFromParcel(in); 379 } 380 381 mWindowingMode = in.readInt(); 382 mActivityWindowingMode = in.readInt(); 383 } 384 385 /** 386 * @param transfer When true, this will transfer other into this leaving other in an 387 * undefined state. Use this if you don't intend to use other. When false, 388 * SurfaceFlinger Transactions will not merge. 389 */ merge(Change other, boolean transfer)390 public void merge(Change other, boolean transfer) { 391 mConfiguration.setTo(other.mConfiguration, other.mConfigSetMask, other.mWindowSetMask); 392 mConfigSetMask |= other.mConfigSetMask; 393 mWindowSetMask |= other.mWindowSetMask; 394 if ((other.mChangeMask & CHANGE_FOCUSABLE) != 0) { 395 mFocusable = other.mFocusable; 396 } 397 if (transfer && (other.mChangeMask & CHANGE_BOUNDS_TRANSACTION) != 0) { 398 mBoundsChangeTransaction = other.mBoundsChangeTransaction; 399 other.mBoundsChangeTransaction = null; 400 } 401 if ((other.mChangeMask & CHANGE_PIP_CALLBACK) != 0) { 402 mPinnedBounds = transfer ? other.mPinnedBounds : new Rect(other.mPinnedBounds); 403 } 404 if ((other.mChangeMask & CHANGE_HIDDEN) != 0) { 405 mHidden = other.mHidden; 406 } 407 mChangeMask |= other.mChangeMask; 408 if (other.mActivityWindowingMode >= 0) { 409 mActivityWindowingMode = other.mActivityWindowingMode; 410 } 411 if (other.mWindowingMode >= 0) { 412 mWindowingMode = other.mWindowingMode; 413 } 414 if (other.mBoundsChangeSurfaceBounds != null) { 415 mBoundsChangeSurfaceBounds = transfer ? other.mBoundsChangeSurfaceBounds 416 : new Rect(other.mBoundsChangeSurfaceBounds); 417 } 418 } 419 getWindowingMode()420 public int getWindowingMode() { 421 return mWindowingMode; 422 } 423 getActivityWindowingMode()424 public int getActivityWindowingMode() { 425 return mActivityWindowingMode; 426 } 427 getConfiguration()428 public Configuration getConfiguration() { 429 return mConfiguration; 430 } 431 432 /** Gets the requested focusable state */ getFocusable()433 public boolean getFocusable() { 434 if ((mChangeMask & CHANGE_FOCUSABLE) == 0) { 435 throw new RuntimeException("Focusable not set. check CHANGE_FOCUSABLE first"); 436 } 437 return mFocusable; 438 } 439 440 /** Gets the requested hidden state */ getHidden()441 public boolean getHidden() { 442 if ((mChangeMask & CHANGE_HIDDEN) == 0) { 443 throw new RuntimeException("Hidden not set. check CHANGE_HIDDEN first"); 444 } 445 return mHidden; 446 } 447 getChangeMask()448 public int getChangeMask() { 449 return mChangeMask; 450 } 451 452 @ActivityInfo.Config getConfigSetMask()453 public int getConfigSetMask() { 454 return mConfigSetMask; 455 } 456 457 @WindowConfiguration.WindowConfig getWindowSetMask()458 public int getWindowSetMask() { 459 return mWindowSetMask; 460 } 461 462 /** 463 * Returns the bounds to be used for scheduling the enter pip callback 464 * or null if no callback is to be scheduled. 465 */ getEnterPipBounds()466 public Rect getEnterPipBounds() { 467 return mPinnedBounds; 468 } 469 getBoundsChangeTransaction()470 public SurfaceControl.Transaction getBoundsChangeTransaction() { 471 return mBoundsChangeTransaction; 472 } 473 getBoundsChangeSurfaceBounds()474 public Rect getBoundsChangeSurfaceBounds() { 475 return mBoundsChangeSurfaceBounds; 476 } 477 478 @Override toString()479 public String toString() { 480 final boolean changesBounds = 481 (mConfigSetMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0 482 && ((mWindowSetMask & WindowConfiguration.WINDOW_CONFIG_BOUNDS) 483 != 0); 484 final boolean changesAppBounds = 485 (mConfigSetMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0 486 && ((mWindowSetMask & WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS) 487 != 0); 488 final boolean changesSs = (mConfigSetMask & ActivityInfo.CONFIG_SCREEN_SIZE) != 0; 489 final boolean changesSss = 490 (mConfigSetMask & ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE) != 0; 491 StringBuilder sb = new StringBuilder(); 492 sb.append('{'); 493 if (changesBounds) { 494 sb.append("bounds:" + mConfiguration.windowConfiguration.getBounds() + ","); 495 } 496 if (changesAppBounds) { 497 sb.append("appbounds:" + mConfiguration.windowConfiguration.getAppBounds() + ","); 498 } 499 if (changesSss) { 500 sb.append("ssw:" + mConfiguration.smallestScreenWidthDp + ","); 501 } 502 if (changesSs) { 503 sb.append("sw/h:" + mConfiguration.screenWidthDp + "x" 504 + mConfiguration.screenHeightDp + ","); 505 } 506 if ((mChangeMask & CHANGE_FOCUSABLE) != 0) { 507 sb.append("focusable:" + mFocusable + ","); 508 } 509 if (mBoundsChangeTransaction != null) { 510 sb.append("hasBoundsTransaction,"); 511 } 512 sb.append("}"); 513 return sb.toString(); 514 } 515 516 @Override writeToParcel(Parcel dest, int flags)517 public void writeToParcel(Parcel dest, int flags) { 518 mConfiguration.writeToParcel(dest, flags); 519 dest.writeBoolean(mFocusable); 520 dest.writeBoolean(mHidden); 521 dest.writeInt(mChangeMask); 522 dest.writeInt(mConfigSetMask); 523 dest.writeInt(mWindowSetMask); 524 525 if (mPinnedBounds != null) { 526 mPinnedBounds.writeToParcel(dest, flags); 527 } 528 if (mBoundsChangeTransaction != null) { 529 mBoundsChangeTransaction.writeToParcel(dest, flags); 530 } 531 if (mBoundsChangeSurfaceBounds != null) { 532 mBoundsChangeSurfaceBounds.writeToParcel(dest, flags); 533 } 534 535 dest.writeInt(mWindowingMode); 536 dest.writeInt(mActivityWindowingMode); 537 } 538 539 @Override describeContents()540 public int describeContents() { 541 return 0; 542 } 543 544 public static final Creator<Change> CREATOR = new Creator<Change>() { 545 @Override 546 public Change createFromParcel(Parcel in) { 547 return new Change(in); 548 } 549 550 @Override 551 public Change[] newArray(int size) { 552 return new Change[size]; 553 } 554 }; 555 } 556 557 /** 558 * Holds information about a reparent/reorder operation in the hierarchy. This is separate from 559 * Changes because they must be executed in the same order that they are added. 560 * @hide 561 */ 562 public static class HierarchyOp implements Parcelable { 563 private final IBinder mContainer; 564 565 // If this is same as mContainer, then only change position, don't reparent. 566 private final IBinder mReparent; 567 568 // Moves/reparents to top of parent when {@code true}, otherwise moves/reparents to bottom. 569 private final boolean mToTop; 570 HierarchyOp(@onNull IBinder container, @Nullable IBinder reparent, boolean toTop)571 public HierarchyOp(@NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) { 572 mContainer = container; 573 mReparent = reparent; 574 mToTop = toTop; 575 } 576 HierarchyOp(@onNull IBinder container, boolean toTop)577 public HierarchyOp(@NonNull IBinder container, boolean toTop) { 578 mContainer = container; 579 mReparent = container; 580 mToTop = toTop; 581 } 582 HierarchyOp(@onNull HierarchyOp copy)583 public HierarchyOp(@NonNull HierarchyOp copy) { 584 mContainer = copy.mContainer; 585 mReparent = copy.mReparent; 586 mToTop = copy.mToTop; 587 } 588 HierarchyOp(Parcel in)589 protected HierarchyOp(Parcel in) { 590 mContainer = in.readStrongBinder(); 591 mReparent = in.readStrongBinder(); 592 mToTop = in.readBoolean(); 593 } 594 isReparent()595 public boolean isReparent() { 596 return mContainer != mReparent; 597 } 598 599 @Nullable getNewParent()600 public IBinder getNewParent() { 601 return mReparent; 602 } 603 604 @NonNull getContainer()605 public IBinder getContainer() { 606 return mContainer; 607 } 608 getToTop()609 public boolean getToTop() { 610 return mToTop; 611 } 612 613 @Override toString()614 public String toString() { 615 if (isReparent()) { 616 return "{reparent: " + mContainer + " to " + (mToTop ? "top of " : "bottom of ") 617 + mReparent + "}"; 618 } else { 619 return "{reorder: " + mContainer + " to " + (mToTop ? "top" : "bottom") + "}"; 620 } 621 } 622 623 @Override writeToParcel(Parcel dest, int flags)624 public void writeToParcel(Parcel dest, int flags) { 625 dest.writeStrongBinder(mContainer); 626 dest.writeStrongBinder(mReparent); 627 dest.writeBoolean(mToTop); 628 } 629 630 @Override describeContents()631 public int describeContents() { 632 return 0; 633 } 634 635 public static final Creator<HierarchyOp> CREATOR = new Creator<HierarchyOp>() { 636 @Override 637 public HierarchyOp createFromParcel(Parcel in) { 638 return new HierarchyOp(in); 639 } 640 641 @Override 642 public HierarchyOp[] newArray(int size) { 643 return new HierarchyOp[size]; 644 } 645 }; 646 } 647 } 648