1 /* 2 * Copyright (C) 2017 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.app; 18 19 import static android.app.ActivityThread.isSystem; 20 import static android.app.WindowConfigurationProto.ACTIVITY_TYPE; 21 import static android.app.WindowConfigurationProto.APP_BOUNDS; 22 import static android.app.WindowConfigurationProto.BOUNDS; 23 import static android.app.WindowConfigurationProto.MAX_BOUNDS; 24 import static android.app.WindowConfigurationProto.WINDOWING_MODE; 25 import static android.view.Surface.rotationToString; 26 27 import android.annotation.IntDef; 28 import android.annotation.NonNull; 29 import android.annotation.Nullable; 30 import android.annotation.TestApi; 31 import android.compat.annotation.UnsupportedAppUsage; 32 import android.content.res.Configuration; 33 import android.graphics.Rect; 34 import android.os.Parcel; 35 import android.os.Parcelable; 36 import android.util.proto.ProtoInputStream; 37 import android.util.proto.ProtoOutputStream; 38 import android.util.proto.WireTypeMismatchException; 39 import android.view.DisplayInfo; 40 import android.view.WindowManager; 41 42 import java.io.IOException; 43 import java.util.Objects; 44 45 /** 46 * Class that contains windowing configuration/state for other objects that contain windows directly 47 * or indirectly. E.g. Activities, Task, Displays, ... 48 * The test class is {@link com.android.server.wm.WindowConfigurationTests} which must be kept 49 * up-to-date and ran anytime changes are made to this class. 50 * @hide 51 */ 52 @TestApi 53 public class WindowConfiguration implements Parcelable, Comparable<WindowConfiguration> { 54 /** 55 * bounds that can differ from app bounds, which may include things such as insets. 56 * 57 * TODO: Investigate combining with {@link #mAppBounds}. Can the latter be a product of the 58 * former? 59 */ 60 private final Rect mBounds = new Rect(); 61 62 /** 63 * {@link android.graphics.Rect} defining app bounds. The dimensions override usages of 64 * {@link DisplayInfo#appHeight} and {@link DisplayInfo#appWidth} and mirrors these values at 65 * the display level. Lower levels can override these values to provide custom bounds to enforce 66 * features such as a max aspect ratio. 67 */ 68 private Rect mAppBounds; 69 70 /** 71 * The maximum {@link Rect} bounds that an app can expect. It is used to report value of 72 * {@link WindowManager#getMaximumWindowMetrics()}. 73 */ 74 private final Rect mMaxBounds = new Rect(); 75 76 /** 77 * The current rotation of this window container relative to the default 78 * orientation of the display it is on (regardless of how deep in the hierarchy 79 * it is). It is used by the configuration hierarchy to apply rotation-dependent 80 * policy during bounds calculation. 81 */ 82 private int mRotation = ROTATION_UNDEFINED; 83 84 /** Rotation is not defined, use the parent containers rotation. */ 85 public static final int ROTATION_UNDEFINED = -1; 86 87 /** The current windowing mode of the configuration. */ 88 private @WindowingMode int mWindowingMode; 89 90 /** The display windowing mode of the configuration */ 91 private @WindowingMode int mDisplayWindowingMode; 92 93 /** Windowing mode is currently not defined. */ 94 public static final int WINDOWING_MODE_UNDEFINED = 0; 95 /** Occupies the full area of the screen or the parent container. */ 96 public static final int WINDOWING_MODE_FULLSCREEN = 1; 97 /** Always on-top (always visible). of other siblings in its parent container. */ 98 public static final int WINDOWING_MODE_PINNED = 2; 99 /** The primary container driving the screen to be in split-screen mode. */ 100 // TODO: Remove once split-screen is migrated to wm-shell. 101 public static final int WINDOWING_MODE_SPLIT_SCREEN_PRIMARY = 3; 102 /** 103 * The containers adjacent to the {@link #WINDOWING_MODE_SPLIT_SCREEN_PRIMARY} container in 104 * split-screen mode. 105 * NOTE: Containers launched with the windowing mode with APIs like 106 * {@link ActivityOptions#setLaunchWindowingMode(int)} will be launched in 107 * {@link #WINDOWING_MODE_FULLSCREEN} if the display isn't currently in split-screen windowing 108 * mode 109 */ 110 // TODO: Remove once split-screen is migrated to wm-shell. 111 public static final int WINDOWING_MODE_SPLIT_SCREEN_SECONDARY = 4; 112 /** Can be freely resized within its parent container. */ 113 // TODO: Remove once freeform is migrated to wm-shell. 114 public static final int WINDOWING_MODE_FREEFORM = 5; 115 /** Generic multi-window with no presentation attribution from the window manager. */ 116 public static final int WINDOWING_MODE_MULTI_WINDOW = 6; 117 118 /** @hide */ 119 @IntDef(prefix = { "WINDOWING_MODE_" }, value = { 120 WINDOWING_MODE_UNDEFINED, 121 WINDOWING_MODE_FULLSCREEN, 122 WINDOWING_MODE_MULTI_WINDOW, 123 WINDOWING_MODE_PINNED, 124 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, 125 WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, 126 WINDOWING_MODE_FREEFORM, 127 }) 128 public @interface WindowingMode {} 129 130 /** The current activity type of the configuration. */ 131 private @ActivityType int mActivityType; 132 133 /** Activity type is currently not defined. */ 134 public static final int ACTIVITY_TYPE_UNDEFINED = 0; 135 /** Standard activity type. Nothing special about the activity... */ 136 public static final int ACTIVITY_TYPE_STANDARD = 1; 137 /** Home/Launcher activity type. */ 138 public static final int ACTIVITY_TYPE_HOME = 2; 139 /** Recents/Overview activity type. There is only one activity with this type in the system. */ 140 public static final int ACTIVITY_TYPE_RECENTS = 3; 141 /** Assistant activity type. */ 142 public static final int ACTIVITY_TYPE_ASSISTANT = 4; 143 /** Dream activity type. */ 144 public static final int ACTIVITY_TYPE_DREAM = 5; 145 146 /** @hide */ 147 @IntDef(prefix = { "ACTIVITY_TYPE_" }, value = { 148 ACTIVITY_TYPE_UNDEFINED, 149 ACTIVITY_TYPE_STANDARD, 150 ACTIVITY_TYPE_HOME, 151 ACTIVITY_TYPE_RECENTS, 152 ACTIVITY_TYPE_ASSISTANT, 153 ACTIVITY_TYPE_DREAM, 154 }) 155 public @interface ActivityType {} 156 157 /** The current always on top status of the configuration. */ 158 private @AlwaysOnTop int mAlwaysOnTop; 159 160 /** Always on top is currently not defined. */ 161 private static final int ALWAYS_ON_TOP_UNDEFINED = 0; 162 /** Always on top is currently on for this configuration. */ 163 private static final int ALWAYS_ON_TOP_ON = 1; 164 /** Always on top is currently off for this configuration. */ 165 private static final int ALWAYS_ON_TOP_OFF = 2; 166 167 /** @hide */ 168 @IntDef(prefix = { "ALWAYS_ON_TOP_" }, value = { 169 ALWAYS_ON_TOP_UNDEFINED, 170 ALWAYS_ON_TOP_ON, 171 ALWAYS_ON_TOP_OFF, 172 }) 173 private @interface AlwaysOnTop {} 174 175 /** Bit that indicates that the {@link #mBounds} changed. 176 * @hide */ 177 public static final int WINDOW_CONFIG_BOUNDS = 1 << 0; 178 /** Bit that indicates that the {@link #mAppBounds} changed. 179 * @hide */ 180 public static final int WINDOW_CONFIG_APP_BOUNDS = 1 << 1; 181 /** Bit that indicates that the {@link #mMaxBounds} changed. 182 * @hide */ 183 public static final int WINDOW_CONFIG_MAX_BOUNDS = 1 << 2; 184 /** Bit that indicates that the {@link #mWindowingMode} changed. 185 * @hide */ 186 public static final int WINDOW_CONFIG_WINDOWING_MODE = 1 << 3; 187 /** Bit that indicates that the {@link #mActivityType} changed. 188 * @hide */ 189 public static final int WINDOW_CONFIG_ACTIVITY_TYPE = 1 << 4; 190 /** Bit that indicates that the {@link #mAlwaysOnTop} changed. 191 * @hide */ 192 public static final int WINDOW_CONFIG_ALWAYS_ON_TOP = 1 << 5; 193 /** Bit that indicates that the {@link #mRotation} changed. 194 * @hide */ 195 public static final int WINDOW_CONFIG_ROTATION = 1 << 6; 196 /** Bit that indicates that the {@link #mDisplayWindowingMode} changed. 197 * @hide */ 198 public static final int WINDOW_CONFIG_DISPLAY_WINDOWING_MODE = 1 << 7; 199 200 /** @hide */ 201 @IntDef(flag = true, prefix = { "WINDOW_CONFIG_" }, value = { 202 WINDOW_CONFIG_BOUNDS, 203 WINDOW_CONFIG_APP_BOUNDS, 204 WINDOW_CONFIG_MAX_BOUNDS, 205 WINDOW_CONFIG_WINDOWING_MODE, 206 WINDOW_CONFIG_ACTIVITY_TYPE, 207 WINDOW_CONFIG_ALWAYS_ON_TOP, 208 WINDOW_CONFIG_ROTATION, 209 WINDOW_CONFIG_DISPLAY_WINDOWING_MODE, 210 }) 211 public @interface WindowConfig {} 212 213 /** @hide */ 214 public static final int PINNED_WINDOWING_MODE_ELEVATION_IN_DIP = 5; 215 216 @UnsupportedAppUsage WindowConfiguration()217 public WindowConfiguration() { 218 unset(); 219 } 220 221 /** @hide */ WindowConfiguration(WindowConfiguration configuration)222 public WindowConfiguration(WindowConfiguration configuration) { 223 setTo(configuration); 224 } 225 WindowConfiguration(Parcel in)226 private WindowConfiguration(Parcel in) { 227 readFromParcel(in); 228 } 229 230 @Override writeToParcel(Parcel dest, int flags)231 public void writeToParcel(Parcel dest, int flags) { 232 mBounds.writeToParcel(dest, flags); 233 dest.writeTypedObject(mAppBounds, flags); 234 mMaxBounds.writeToParcel(dest, flags); 235 dest.writeInt(mWindowingMode); 236 dest.writeInt(mActivityType); 237 dest.writeInt(mAlwaysOnTop); 238 dest.writeInt(mRotation); 239 dest.writeInt(mDisplayWindowingMode); 240 } 241 242 /** @hide */ readFromParcel(@onNull Parcel source)243 public void readFromParcel(@NonNull Parcel source) { 244 mBounds.readFromParcel(source); 245 mAppBounds = source.readTypedObject(Rect.CREATOR); 246 mMaxBounds.readFromParcel(source); 247 mWindowingMode = source.readInt(); 248 mActivityType = source.readInt(); 249 mAlwaysOnTop = source.readInt(); 250 mRotation = source.readInt(); 251 mDisplayWindowingMode = source.readInt(); 252 } 253 254 @Override describeContents()255 public int describeContents() { 256 return 0; 257 } 258 259 /** @hide */ 260 public static final @android.annotation.NonNull Creator<WindowConfiguration> CREATOR = new Creator<WindowConfiguration>() { 261 @Override 262 public WindowConfiguration createFromParcel(Parcel in) { 263 return new WindowConfiguration(in); 264 } 265 266 @Override 267 public WindowConfiguration[] newArray(int size) { 268 return new WindowConfiguration[size]; 269 } 270 }; 271 272 /** 273 * Sets the bounds to the provided {@link Rect}. 274 * @param rect the new bounds value. 275 */ setBounds(Rect rect)276 public void setBounds(Rect rect) { 277 if (rect == null) { 278 mBounds.setEmpty(); 279 return; 280 } 281 282 mBounds.set(rect); 283 } 284 285 /** 286 * Set {@link #mAppBounds} to the input Rect. 287 * @param rect The rect value to set {@link #mAppBounds} to. 288 * @see #getAppBounds() 289 */ setAppBounds(Rect rect)290 public void setAppBounds(Rect rect) { 291 if (rect == null) { 292 mAppBounds = null; 293 return; 294 } 295 296 setAppBounds(rect.left, rect.top, rect.right, rect.bottom); 297 } 298 299 /** 300 * Sets the maximum bounds to the provided {@link Rect}. 301 * @param rect the new bounds value. 302 * @see #getMaxBounds() 303 */ setMaxBounds(@ullable Rect rect)304 public void setMaxBounds(@Nullable Rect rect) { 305 if (rect == null) { 306 mMaxBounds.setEmpty(); 307 return; 308 } 309 mMaxBounds.set(rect); 310 } 311 312 /** 313 * @see #setMaxBounds(Rect) 314 * @hide 315 */ setMaxBounds(int left, int top, int right, int bottom)316 public void setMaxBounds(int left, int top, int right, int bottom) { 317 mMaxBounds.set(left, top, right, bottom); 318 } 319 320 /** 321 * Sets whether this window should be always on top. 322 * @param alwaysOnTop {@code true} to set window always on top, otherwise {@code false} 323 * @hide 324 */ setAlwaysOnTop(boolean alwaysOnTop)325 public void setAlwaysOnTop(boolean alwaysOnTop) { 326 mAlwaysOnTop = alwaysOnTop ? ALWAYS_ON_TOP_ON : ALWAYS_ON_TOP_OFF; 327 } 328 setAlwaysOnTop(@lwaysOnTop int alwaysOnTop)329 private void setAlwaysOnTop(@AlwaysOnTop int alwaysOnTop) { 330 mAlwaysOnTop = alwaysOnTop; 331 } 332 333 /** 334 * @see #setAppBounds(Rect) 335 * @see #getAppBounds() 336 * @hide 337 */ setAppBounds(int left, int top, int right, int bottom)338 public void setAppBounds(int left, int top, int right, int bottom) { 339 if (mAppBounds == null) { 340 mAppBounds = new Rect(); 341 } 342 343 mAppBounds.set(left, top, right, bottom); 344 } 345 346 /** @see #setAppBounds(Rect) */ getAppBounds()347 public Rect getAppBounds() { 348 return mAppBounds; 349 } 350 351 /** @see #setBounds(Rect) */ getBounds()352 public Rect getBounds() { 353 return mBounds; 354 } 355 356 /** @see #setMaxBounds(Rect) */ 357 @NonNull getMaxBounds()358 public Rect getMaxBounds() { 359 return mMaxBounds; 360 } 361 getRotation()362 public int getRotation() { 363 return mRotation; 364 } 365 setRotation(int rotation)366 public void setRotation(int rotation) { 367 mRotation = rotation; 368 } 369 setWindowingMode(@indowingMode int windowingMode)370 public void setWindowingMode(@WindowingMode int windowingMode) { 371 mWindowingMode = windowingMode; 372 } 373 374 @WindowingMode getWindowingMode()375 public int getWindowingMode() { 376 return mWindowingMode; 377 } 378 379 /** @hide */ setDisplayWindowingMode(@indowingMode int windowingMode)380 public void setDisplayWindowingMode(@WindowingMode int windowingMode) { 381 mDisplayWindowingMode = windowingMode; 382 } 383 384 /** @hide */ 385 @WindowingMode getDisplayWindowingMode()386 public int getDisplayWindowingMode() { 387 return mDisplayWindowingMode; 388 } 389 setActivityType(@ctivityType int activityType)390 public void setActivityType(@ActivityType int activityType) { 391 if (mActivityType == activityType) { 392 return; 393 } 394 395 // Error check within system server that we are not changing activity type which can be 396 // dangerous. It is okay for things to change in the application process as it doesn't 397 // affect how other things is the system is managed. 398 if (isSystem() 399 && mActivityType != ACTIVITY_TYPE_UNDEFINED 400 && activityType != ACTIVITY_TYPE_UNDEFINED) { 401 throw new IllegalStateException("Can't change activity type once set: " + this 402 + " activityType=" + activityTypeToString(activityType)); 403 } 404 mActivityType = activityType; 405 } 406 407 @ActivityType getActivityType()408 public int getActivityType() { 409 return mActivityType; 410 } 411 setTo(WindowConfiguration other)412 public void setTo(WindowConfiguration other) { 413 setBounds(other.mBounds); 414 setAppBounds(other.mAppBounds); 415 setMaxBounds(other.mMaxBounds); 416 setWindowingMode(other.mWindowingMode); 417 setActivityType(other.mActivityType); 418 setAlwaysOnTop(other.mAlwaysOnTop); 419 setRotation(other.mRotation); 420 setDisplayWindowingMode(other.mDisplayWindowingMode); 421 } 422 423 /** Set this object to completely undefined. 424 * @hide */ unset()425 public void unset() { 426 setToDefaults(); 427 } 428 429 /** @hide */ setToDefaults()430 public void setToDefaults() { 431 setAppBounds(null); 432 setBounds(null); 433 setMaxBounds(null); 434 setWindowingMode(WINDOWING_MODE_UNDEFINED); 435 setActivityType(ACTIVITY_TYPE_UNDEFINED); 436 setAlwaysOnTop(ALWAYS_ON_TOP_UNDEFINED); 437 setRotation(ROTATION_UNDEFINED); 438 setDisplayWindowingMode(WINDOWING_MODE_UNDEFINED); 439 } 440 441 /** 442 * Copies the fields from delta into this Configuration object, keeping 443 * track of which ones have changed. Any undefined fields in {@code delta} 444 * are ignored and not copied in to the current Configuration. 445 * 446 * @return a bit mask of the changed fields, as per {@link #diff} 447 * @hide 448 */ updateFrom(@onNull WindowConfiguration delta)449 public @WindowConfig int updateFrom(@NonNull WindowConfiguration delta) { 450 int changed = 0; 451 // Only allow override if bounds is not empty 452 if (!delta.mBounds.isEmpty() && !delta.mBounds.equals(mBounds)) { 453 changed |= WINDOW_CONFIG_BOUNDS; 454 setBounds(delta.mBounds); 455 } 456 if (delta.mAppBounds != null && !delta.mAppBounds.equals(mAppBounds)) { 457 changed |= WINDOW_CONFIG_APP_BOUNDS; 458 setAppBounds(delta.mAppBounds); 459 } 460 if (!delta.mMaxBounds.isEmpty() && !delta.mMaxBounds.equals(mMaxBounds)) { 461 changed |= WINDOW_CONFIG_MAX_BOUNDS; 462 setMaxBounds(delta.mMaxBounds); 463 } 464 if (delta.mWindowingMode != WINDOWING_MODE_UNDEFINED 465 && mWindowingMode != delta.mWindowingMode) { 466 changed |= WINDOW_CONFIG_WINDOWING_MODE; 467 setWindowingMode(delta.mWindowingMode); 468 } 469 if (delta.mActivityType != ACTIVITY_TYPE_UNDEFINED 470 && mActivityType != delta.mActivityType) { 471 changed |= WINDOW_CONFIG_ACTIVITY_TYPE; 472 setActivityType(delta.mActivityType); 473 } 474 if (delta.mAlwaysOnTop != ALWAYS_ON_TOP_UNDEFINED 475 && mAlwaysOnTop != delta.mAlwaysOnTop) { 476 changed |= WINDOW_CONFIG_ALWAYS_ON_TOP; 477 setAlwaysOnTop(delta.mAlwaysOnTop); 478 } 479 if (delta.mRotation != ROTATION_UNDEFINED && delta.mRotation != mRotation) { 480 changed |= WINDOW_CONFIG_ROTATION; 481 setRotation(delta.mRotation); 482 } 483 if (delta.mDisplayWindowingMode != WINDOWING_MODE_UNDEFINED 484 && mDisplayWindowingMode != delta.mDisplayWindowingMode) { 485 changed |= WINDOW_CONFIG_DISPLAY_WINDOWING_MODE; 486 setDisplayWindowingMode(delta.mDisplayWindowingMode); 487 } 488 return changed; 489 } 490 491 /** 492 * Copies the fields specified by mask from delta into this Configuration object. 493 * @hide 494 */ setTo(@onNull WindowConfiguration delta, @WindowConfig int mask)495 public void setTo(@NonNull WindowConfiguration delta, @WindowConfig int mask) { 496 if ((mask & WINDOW_CONFIG_BOUNDS) != 0) { 497 setBounds(delta.mBounds); 498 } 499 if ((mask & WINDOW_CONFIG_APP_BOUNDS) != 0) { 500 setAppBounds(delta.mAppBounds); 501 } 502 if ((mask & WINDOW_CONFIG_MAX_BOUNDS) != 0) { 503 setMaxBounds(delta.mMaxBounds); 504 } 505 if ((mask & WINDOW_CONFIG_WINDOWING_MODE) != 0) { 506 setWindowingMode(delta.mWindowingMode); 507 } 508 if ((mask & WINDOW_CONFIG_ACTIVITY_TYPE) != 0) { 509 setActivityType(delta.mActivityType); 510 } 511 if ((mask & WINDOW_CONFIG_ALWAYS_ON_TOP) != 0) { 512 setAlwaysOnTop(delta.mAlwaysOnTop); 513 } 514 if ((mask & WINDOW_CONFIG_ROTATION) != 0) { 515 setRotation(delta.mRotation); 516 } 517 if ((mask & WINDOW_CONFIG_DISPLAY_WINDOWING_MODE) != 0) { 518 setDisplayWindowingMode(delta.mDisplayWindowingMode); 519 } 520 } 521 522 /** 523 * Return a bit mask of the differences between this Configuration object and the given one. 524 * Does not change the values of either. Any undefined fields in <var>other</var> are ignored. 525 * @param other The configuration to diff against. 526 * @param compareUndefined If undefined values should be compared. 527 * @return Returns a bit mask indicating which configuration 528 * values has changed, containing any combination of {@link WindowConfig} flags. 529 * 530 * @see Configuration#diff(Configuration) 531 * @hide 532 */ diff(WindowConfiguration other, boolean compareUndefined)533 public @WindowConfig long diff(WindowConfiguration other, boolean compareUndefined) { 534 long changes = 0; 535 536 if (!mBounds.equals(other.mBounds)) { 537 changes |= WINDOW_CONFIG_BOUNDS; 538 } 539 540 // Make sure that one of the values is not null and that they are not equal. 541 if ((compareUndefined || other.mAppBounds != null) 542 && mAppBounds != other.mAppBounds 543 && (mAppBounds == null || !mAppBounds.equals(other.mAppBounds))) { 544 changes |= WINDOW_CONFIG_APP_BOUNDS; 545 } 546 547 if (!mMaxBounds.equals(other.mMaxBounds)) { 548 changes |= WINDOW_CONFIG_MAX_BOUNDS; 549 } 550 551 if ((compareUndefined || other.mWindowingMode != WINDOWING_MODE_UNDEFINED) 552 && mWindowingMode != other.mWindowingMode) { 553 changes |= WINDOW_CONFIG_WINDOWING_MODE; 554 } 555 556 if ((compareUndefined || other.mActivityType != ACTIVITY_TYPE_UNDEFINED) 557 && mActivityType != other.mActivityType) { 558 changes |= WINDOW_CONFIG_ACTIVITY_TYPE; 559 } 560 561 if ((compareUndefined || other.mAlwaysOnTop != ALWAYS_ON_TOP_UNDEFINED) 562 && mAlwaysOnTop != other.mAlwaysOnTop) { 563 changes |= WINDOW_CONFIG_ALWAYS_ON_TOP; 564 } 565 566 if ((compareUndefined || other.mRotation != ROTATION_UNDEFINED) 567 && mRotation != other.mRotation) { 568 changes |= WINDOW_CONFIG_ROTATION; 569 } 570 571 if ((compareUndefined || other.mDisplayWindowingMode != WINDOWING_MODE_UNDEFINED) 572 && mDisplayWindowingMode != other.mDisplayWindowingMode) { 573 changes |= WINDOW_CONFIG_DISPLAY_WINDOWING_MODE; 574 } 575 576 return changes; 577 } 578 579 @Override compareTo(WindowConfiguration that)580 public int compareTo(WindowConfiguration that) { 581 int n = 0; 582 if (mAppBounds == null && that.mAppBounds != null) { 583 return 1; 584 } else if (mAppBounds != null && that.mAppBounds == null) { 585 return -1; 586 } else if (mAppBounds != null && that.mAppBounds != null) { 587 n = mAppBounds.left - that.mAppBounds.left; 588 if (n != 0) return n; 589 n = mAppBounds.top - that.mAppBounds.top; 590 if (n != 0) return n; 591 n = mAppBounds.right - that.mAppBounds.right; 592 if (n != 0) return n; 593 n = mAppBounds.bottom - that.mAppBounds.bottom; 594 if (n != 0) return n; 595 } 596 597 n = mMaxBounds.left - that.mMaxBounds.left; 598 if (n != 0) return n; 599 n = mMaxBounds.top - that.mMaxBounds.top; 600 if (n != 0) return n; 601 n = mMaxBounds.right - that.mMaxBounds.right; 602 if (n != 0) return n; 603 n = mMaxBounds.bottom - that.mMaxBounds.bottom; 604 if (n != 0) return n; 605 606 n = mBounds.left - that.mBounds.left; 607 if (n != 0) return n; 608 n = mBounds.top - that.mBounds.top; 609 if (n != 0) return n; 610 n = mBounds.right - that.mBounds.right; 611 if (n != 0) return n; 612 n = mBounds.bottom - that.mBounds.bottom; 613 if (n != 0) return n; 614 615 n = mWindowingMode - that.mWindowingMode; 616 if (n != 0) return n; 617 n = mActivityType - that.mActivityType; 618 if (n != 0) return n; 619 n = mAlwaysOnTop - that.mAlwaysOnTop; 620 if (n != 0) return n; 621 n = mRotation - that.mRotation; 622 if (n != 0) return n; 623 n = mDisplayWindowingMode - that.mDisplayWindowingMode; 624 if (n != 0) return n; 625 626 // if (n != 0) return n; 627 return n; 628 } 629 630 /** @hide */ 631 @Override equals(@ullable Object that)632 public boolean equals(@Nullable Object that) { 633 if (that == null) return false; 634 if (that == this) return true; 635 if (!(that instanceof WindowConfiguration)) { 636 return false; 637 } 638 return this.compareTo((WindowConfiguration) that) == 0; 639 } 640 641 /** @hide */ 642 @Override hashCode()643 public int hashCode() { 644 int result = 0; 645 result = 31 * result + Objects.hashCode(mAppBounds); 646 result = 31 * result + Objects.hashCode(mBounds); 647 result = 31 * result + Objects.hashCode(mMaxBounds); 648 result = 31 * result + mWindowingMode; 649 result = 31 * result + mActivityType; 650 result = 31 * result + mAlwaysOnTop; 651 result = 31 * result + mRotation; 652 result = 31 * result + mDisplayWindowingMode; 653 return result; 654 } 655 656 /** @hide */ 657 @Override toString()658 public String toString() { 659 return "{ mBounds=" + mBounds 660 + " mAppBounds=" + mAppBounds 661 + " mMaxBounds=" + mMaxBounds 662 + " mWindowingMode=" + windowingModeToString(mWindowingMode) 663 + " mDisplayWindowingMode=" + windowingModeToString(mDisplayWindowingMode) 664 + " mActivityType=" + activityTypeToString(mActivityType) 665 + " mAlwaysOnTop=" + alwaysOnTopToString(mAlwaysOnTop) 666 + " mRotation=" + (mRotation == ROTATION_UNDEFINED 667 ? "undefined" : rotationToString(mRotation)) 668 + "}"; 669 } 670 671 /** 672 * Write to a protocol buffer output stream. 673 * Protocol buffer message definition at {@link android.app.WindowConfigurationProto} 674 * 675 * @param protoOutputStream Stream to write the WindowConfiguration object to. 676 * @param fieldId Field Id of the WindowConfiguration as defined in the parent message 677 * @hide 678 */ dumpDebug(ProtoOutputStream protoOutputStream, long fieldId)679 public void dumpDebug(ProtoOutputStream protoOutputStream, long fieldId) { 680 final long token = protoOutputStream.start(fieldId); 681 if (mAppBounds != null) { 682 mAppBounds.dumpDebug(protoOutputStream, APP_BOUNDS); 683 } 684 protoOutputStream.write(WINDOWING_MODE, mWindowingMode); 685 protoOutputStream.write(ACTIVITY_TYPE, mActivityType); 686 mBounds.dumpDebug(protoOutputStream, BOUNDS); 687 mMaxBounds.dumpDebug(protoOutputStream, MAX_BOUNDS); 688 protoOutputStream.end(token); 689 } 690 691 /** 692 * Read from a protocol buffer input stream. 693 * Protocol buffer message definition at {@link android.app.WindowConfigurationProto} 694 * 695 * @param proto Stream to read the WindowConfiguration object from. 696 * @param fieldId Field Id of the WindowConfiguration as defined in the parent message 697 * @hide 698 */ readFromProto(ProtoInputStream proto, long fieldId)699 public void readFromProto(ProtoInputStream proto, long fieldId) 700 throws IOException, WireTypeMismatchException { 701 final long token = proto.start(fieldId); 702 try { 703 while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) { 704 switch (proto.getFieldNumber()) { 705 case (int) APP_BOUNDS: 706 mAppBounds = new Rect(); 707 mAppBounds.readFromProto(proto, APP_BOUNDS); 708 break; 709 case (int) BOUNDS: 710 mBounds.readFromProto(proto, BOUNDS); 711 break; 712 case (int) MAX_BOUNDS: 713 mMaxBounds.readFromProto(proto, MAX_BOUNDS); 714 break; 715 case (int) WINDOWING_MODE: 716 mWindowingMode = proto.readInt(WINDOWING_MODE); 717 break; 718 case (int) ACTIVITY_TYPE: 719 mActivityType = proto.readInt(ACTIVITY_TYPE); 720 break; 721 } 722 } 723 } finally { 724 // Let caller handle any exceptions 725 proto.end(token); 726 } 727 } 728 729 /** 730 * Returns true if the activities associated with this window configuration display a shadow 731 * around their border. 732 * @hide 733 */ hasWindowShadow()734 public boolean hasWindowShadow() { 735 return mWindowingMode != WINDOWING_MODE_MULTI_WINDOW && tasksAreFloating(); 736 } 737 738 /** 739 * Returns true if the activities associated with this window configuration display a decor 740 * view. 741 * @hide 742 */ hasWindowDecorCaption()743 public boolean hasWindowDecorCaption() { 744 return mActivityType == ACTIVITY_TYPE_STANDARD && (mWindowingMode == WINDOWING_MODE_FREEFORM 745 || mDisplayWindowingMode == WINDOWING_MODE_FREEFORM); 746 } 747 748 /** 749 * Returns true if the tasks associated with this window configuration can be resized 750 * independently of their parent container. 751 * @hide 752 */ canResizeTask()753 public boolean canResizeTask() { 754 return mWindowingMode == WINDOWING_MODE_FREEFORM 755 || mWindowingMode == WINDOWING_MODE_MULTI_WINDOW; 756 } 757 758 /** Returns true if the task bounds should persist across power cycles. 759 * @hide */ persistTaskBounds()760 public boolean persistTaskBounds() { 761 return mWindowingMode == WINDOWING_MODE_FREEFORM; 762 } 763 764 /** 765 * Returns true if the tasks associated with this window configuration are floating. 766 * Floating tasks are laid out differently as they are allowed to extend past the display bounds 767 * without overscan insets. 768 * @hide 769 */ tasksAreFloating()770 public boolean tasksAreFloating() { 771 return isFloating(mWindowingMode); 772 } 773 774 /** 775 * Returns true if the windowingMode represents a floating window. 776 * @hide 777 */ isFloating(int windowingMode)778 public static boolean isFloating(int windowingMode) { 779 return windowingMode == WINDOWING_MODE_FREEFORM || windowingMode == WINDOWING_MODE_PINNED; 780 } 781 782 /** 783 * Returns {@code true} if the windowingMode represents a window in multi-window mode. 784 * I.e. sharing the screen with another activity. 785 * @hide 786 */ inMultiWindowMode(int windowingMode)787 public static boolean inMultiWindowMode(int windowingMode) { 788 return windowingMode != WINDOWING_MODE_FULLSCREEN 789 && windowingMode != WINDOWING_MODE_UNDEFINED; 790 } 791 792 /** 793 * Returns true if the windowingMode represents a split window. 794 * @hide 795 */ isSplitScreenWindowingMode(int windowingMode)796 public static boolean isSplitScreenWindowingMode(int windowingMode) { 797 return windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY 798 || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; 799 } 800 801 /** 802 * Returns true if the windows associated with this window configuration can receive input keys. 803 * @hide 804 */ canReceiveKeys()805 public boolean canReceiveKeys() { 806 return mWindowingMode != WINDOWING_MODE_PINNED; 807 } 808 809 /** 810 * Returns true if the container associated with this window configuration is always-on-top of 811 * its siblings. 812 * @hide 813 */ isAlwaysOnTop()814 public boolean isAlwaysOnTop() { 815 if (mWindowingMode == WINDOWING_MODE_PINNED) return true; 816 if (mActivityType == ACTIVITY_TYPE_DREAM) return true; 817 if (mAlwaysOnTop != ALWAYS_ON_TOP_ON) return false; 818 return mWindowingMode == WINDOWING_MODE_FREEFORM 819 || mWindowingMode == WINDOWING_MODE_MULTI_WINDOW; 820 } 821 822 /** 823 * Returns true if any visible windows belonging to apps with this window configuration should 824 * be kept on screen when the app is killed due to something like the low memory killer. 825 * @hide 826 */ keepVisibleDeadAppWindowOnScreen()827 public boolean keepVisibleDeadAppWindowOnScreen() { 828 return mWindowingMode != WINDOWING_MODE_PINNED; 829 } 830 831 /** 832 * Returns true if the backdrop on the client side should match the frame of the window. 833 * Returns false, if the backdrop should be fullscreen. 834 * @hide 835 */ useWindowFrameForBackdrop()836 public boolean useWindowFrameForBackdrop() { 837 return mWindowingMode == WINDOWING_MODE_FREEFORM || mWindowingMode == WINDOWING_MODE_PINNED; 838 } 839 840 /** 841 * Returns true if windows in this container should be given move animations by default. 842 * @hide 843 */ hasMovementAnimations()844 public boolean hasMovementAnimations() { 845 return mWindowingMode != WINDOWING_MODE_PINNED; 846 } 847 848 /** 849 * Returns true if this container can be put in either 850 * {@link #WINDOWING_MODE_SPLIT_SCREEN_PRIMARY} or 851 * {@link #WINDOWING_MODE_SPLIT_SCREEN_SECONDARY} windowing modes based on its current state. 852 * @hide 853 */ supportSplitScreenWindowingMode()854 public boolean supportSplitScreenWindowingMode() { 855 return supportSplitScreenWindowingMode(mActivityType); 856 } 857 858 /** @hide */ supportSplitScreenWindowingMode(int activityType)859 public static boolean supportSplitScreenWindowingMode(int activityType) { 860 return activityType != ACTIVITY_TYPE_ASSISTANT && activityType != ACTIVITY_TYPE_DREAM; 861 } 862 863 /** @hide */ windowingModeToString(@indowingMode int windowingMode)864 public static String windowingModeToString(@WindowingMode int windowingMode) { 865 switch (windowingMode) { 866 case WINDOWING_MODE_UNDEFINED: return "undefined"; 867 case WINDOWING_MODE_FULLSCREEN: return "fullscreen"; 868 case WINDOWING_MODE_MULTI_WINDOW: return "multi-window"; 869 case WINDOWING_MODE_PINNED: return "pinned"; 870 case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: return "split-screen-primary"; 871 case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY: return "split-screen-secondary"; 872 case WINDOWING_MODE_FREEFORM: return "freeform"; 873 } 874 return String.valueOf(windowingMode); 875 } 876 877 /** @hide */ activityTypeToString(@ctivityType int applicationType)878 public static String activityTypeToString(@ActivityType int applicationType) { 879 switch (applicationType) { 880 case ACTIVITY_TYPE_UNDEFINED: return "undefined"; 881 case ACTIVITY_TYPE_STANDARD: return "standard"; 882 case ACTIVITY_TYPE_HOME: return "home"; 883 case ACTIVITY_TYPE_RECENTS: return "recents"; 884 case ACTIVITY_TYPE_ASSISTANT: return "assistant"; 885 case ACTIVITY_TYPE_DREAM: return "dream"; 886 } 887 return String.valueOf(applicationType); 888 } 889 890 /** @hide */ alwaysOnTopToString(@lwaysOnTop int alwaysOnTop)891 public static String alwaysOnTopToString(@AlwaysOnTop int alwaysOnTop) { 892 switch (alwaysOnTop) { 893 case ALWAYS_ON_TOP_UNDEFINED: return "undefined"; 894 case ALWAYS_ON_TOP_ON: return "on"; 895 case ALWAYS_ON_TOP_OFF: return "off"; 896 } 897 return String.valueOf(alwaysOnTop); 898 } 899 } 900