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