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