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