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