1 /* 2 * Copyright (C) 2006 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.content.res; 18 19 import android.annotation.Nullable; 20 import android.compat.annotation.UnsupportedAppUsage; 21 import android.content.pm.ApplicationInfo; 22 import android.graphics.Canvas; 23 import android.graphics.Insets; 24 import android.graphics.PointF; 25 import android.graphics.Rect; 26 import android.graphics.Region; 27 import android.os.Build; 28 import android.os.Build.VERSION_CODES; 29 import android.os.Parcel; 30 import android.os.Parcelable; 31 import android.util.DisplayMetrics; 32 import android.view.InsetsSourceControl; 33 import android.view.InsetsState; 34 import android.view.MotionEvent; 35 import android.view.WindowManager; 36 import android.view.WindowManager.LayoutParams; 37 38 /** 39 * CompatibilityInfo class keeps the information about the screen compatibility mode that the 40 * application is running under. 41 * 42 * {@hide} 43 */ 44 public class CompatibilityInfo implements Parcelable { 45 /** default compatibility info object for compatible applications */ 46 @UnsupportedAppUsage 47 public static final CompatibilityInfo DEFAULT_COMPATIBILITY_INFO = new CompatibilityInfo() { 48 }; 49 50 /** 51 * This is the number of pixels we would like to have along the 52 * short axis of an app that needs to run on a normal size screen. 53 */ 54 public static final int DEFAULT_NORMAL_SHORT_DIMENSION = 320; 55 56 /** 57 * This is the maximum aspect ratio we will allow while keeping 58 * applications in a compatible screen size. 59 */ 60 public static final float MAXIMUM_ASPECT_RATIO = (854f/480f); 61 62 /** 63 * A compatibility flags 64 */ 65 private final int mCompatibilityFlags; 66 67 /** 68 * A flag mask to tell if the application needs scaling (when mApplicationScale != 1.0f) 69 * {@see compatibilityFlag} 70 */ 71 private static final int SCALING_REQUIRED = 1; 72 73 /** 74 * Application must always run in compatibility mode? 75 */ 76 private static final int ALWAYS_NEEDS_COMPAT = 2; 77 78 /** 79 * Application never should run in compatibility mode? 80 */ 81 private static final int NEVER_NEEDS_COMPAT = 4; 82 83 /** 84 * Set if the application needs to run in screen size compatibility mode. 85 */ 86 private static final int NEEDS_SCREEN_COMPAT = 8; 87 88 /** 89 * Set if the application needs to run in with compat resources. 90 */ 91 private static final int NEEDS_COMPAT_RES = 16; 92 93 /** 94 * Set if the application needs to be forcibly downscaled 95 */ 96 private static final int HAS_OVERRIDE_SCALING = 32; 97 98 /** 99 * The effective screen density we have selected for this application. 100 */ 101 public final int applicationDensity; 102 103 /** 104 * Application's scale. 105 */ 106 @UnsupportedAppUsage 107 public final float applicationScale; 108 109 /** 110 * Application's inverted scale. 111 */ 112 public final float applicationInvertedScale; 113 114 @UnsupportedAppUsage 115 @Deprecated CompatibilityInfo(ApplicationInfo appInfo, int screenLayout, int sw, boolean forceCompat)116 public CompatibilityInfo(ApplicationInfo appInfo, int screenLayout, int sw, 117 boolean forceCompat) { 118 this(appInfo, screenLayout, sw, forceCompat, 1f); 119 } 120 CompatibilityInfo(ApplicationInfo appInfo, int screenLayout, int sw, boolean forceCompat, float overrideScale)121 public CompatibilityInfo(ApplicationInfo appInfo, int screenLayout, int sw, 122 boolean forceCompat, float overrideScale) { 123 int compatFlags = 0; 124 125 if (appInfo.targetSdkVersion < VERSION_CODES.O) { 126 compatFlags |= NEEDS_COMPAT_RES; 127 } 128 if (appInfo.requiresSmallestWidthDp != 0 || appInfo.compatibleWidthLimitDp != 0 129 || appInfo.largestWidthLimitDp != 0) { 130 // New style screen requirements spec. 131 int required = appInfo.requiresSmallestWidthDp != 0 132 ? appInfo.requiresSmallestWidthDp 133 : appInfo.compatibleWidthLimitDp; 134 if (required == 0) { 135 required = appInfo.largestWidthLimitDp; 136 } 137 int compat = appInfo.compatibleWidthLimitDp != 0 138 ? appInfo.compatibleWidthLimitDp : required; 139 if (compat < required) { 140 compat = required; 141 } 142 int largest = appInfo.largestWidthLimitDp; 143 144 if (required > DEFAULT_NORMAL_SHORT_DIMENSION) { 145 // For now -- if they require a size larger than the only 146 // size we can do in compatibility mode, then don't ever 147 // allow the app to go in to compat mode. Trying to run 148 // it at a smaller size it can handle will make it far more 149 // broken than running at a larger size than it wants or 150 // thinks it can handle. 151 compatFlags |= NEVER_NEEDS_COMPAT; 152 } else if (largest != 0 && sw > largest) { 153 // If the screen size is larger than the largest size the 154 // app thinks it can work with, then always force it in to 155 // compatibility mode. 156 compatFlags |= NEEDS_SCREEN_COMPAT | ALWAYS_NEEDS_COMPAT; 157 } else if (compat >= sw) { 158 // The screen size is something the app says it was designed 159 // for, so never do compatibility mode. 160 compatFlags |= NEVER_NEEDS_COMPAT; 161 } else if (forceCompat) { 162 // The app may work better with or without compatibility mode. 163 // Let the user decide. 164 compatFlags |= NEEDS_SCREEN_COMPAT; 165 } 166 167 // Modern apps always support densities. 168 applicationDensity = DisplayMetrics.DENSITY_DEVICE; 169 applicationScale = 1.0f; 170 applicationInvertedScale = 1.0f; 171 172 } else { 173 /** 174 * Has the application said that its UI is expandable? Based on the 175 * <supports-screen> android:expandible in the manifest. 176 */ 177 final int EXPANDABLE = 2; 178 179 /** 180 * Has the application said that its UI supports large screens? Based on the 181 * <supports-screen> android:largeScreens in the manifest. 182 */ 183 final int LARGE_SCREENS = 8; 184 185 /** 186 * Has the application said that its UI supports xlarge screens? Based on the 187 * <supports-screen> android:xlargeScreens in the manifest. 188 */ 189 final int XLARGE_SCREENS = 32; 190 191 int sizeInfo = 0; 192 193 // We can't rely on the application always setting 194 // FLAG_RESIZEABLE_FOR_SCREENS so will compute it based on various input. 195 boolean anyResizeable = false; 196 197 if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) { 198 sizeInfo |= LARGE_SCREENS; 199 anyResizeable = true; 200 if (!forceCompat) { 201 // If we aren't forcing the app into compatibility mode, then 202 // assume if it supports large screens that we should allow it 203 // to use the full space of an xlarge screen as well. 204 sizeInfo |= XLARGE_SCREENS | EXPANDABLE; 205 } 206 } 207 if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) != 0) { 208 anyResizeable = true; 209 if (!forceCompat) { 210 sizeInfo |= XLARGE_SCREENS | EXPANDABLE; 211 } 212 } 213 if ((appInfo.flags & ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) { 214 anyResizeable = true; 215 sizeInfo |= EXPANDABLE; 216 } 217 218 if (forceCompat) { 219 // If we are forcing compatibility mode, then ignore an app that 220 // just says it is resizable for screens. We'll only have it fill 221 // the screen if it explicitly says it supports the screen size we 222 // are running in. 223 sizeInfo &= ~EXPANDABLE; 224 } 225 226 compatFlags |= NEEDS_SCREEN_COMPAT; 227 switch (screenLayout&Configuration.SCREENLAYOUT_SIZE_MASK) { 228 case Configuration.SCREENLAYOUT_SIZE_XLARGE: 229 if ((sizeInfo&XLARGE_SCREENS) != 0) { 230 compatFlags &= ~NEEDS_SCREEN_COMPAT; 231 } 232 if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) != 0) { 233 compatFlags |= NEVER_NEEDS_COMPAT; 234 } 235 break; 236 case Configuration.SCREENLAYOUT_SIZE_LARGE: 237 if ((sizeInfo&LARGE_SCREENS) != 0) { 238 compatFlags &= ~NEEDS_SCREEN_COMPAT; 239 } 240 if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) { 241 compatFlags |= NEVER_NEEDS_COMPAT; 242 } 243 break; 244 } 245 246 if ((screenLayout&Configuration.SCREENLAYOUT_COMPAT_NEEDED) != 0) { 247 if ((sizeInfo&EXPANDABLE) != 0) { 248 compatFlags &= ~NEEDS_SCREEN_COMPAT; 249 } else if (!anyResizeable) { 250 compatFlags |= ALWAYS_NEEDS_COMPAT; 251 } 252 } else { 253 compatFlags &= ~NEEDS_SCREEN_COMPAT; 254 compatFlags |= NEVER_NEEDS_COMPAT; 255 } 256 257 if (overrideScale != 1.0f) { 258 applicationScale = overrideScale; 259 applicationInvertedScale = 1.0f / overrideScale; 260 applicationDensity = (int) ((DisplayMetrics.DENSITY_DEVICE_STABLE 261 * applicationInvertedScale) + .5f); 262 compatFlags |= HAS_OVERRIDE_SCALING; 263 } else if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) { 264 applicationDensity = DisplayMetrics.DENSITY_DEVICE; 265 applicationScale = 1.0f; 266 applicationInvertedScale = 1.0f; 267 } else { 268 applicationDensity = DisplayMetrics.DENSITY_DEFAULT; 269 applicationScale = DisplayMetrics.DENSITY_DEVICE 270 / (float) DisplayMetrics.DENSITY_DEFAULT; 271 applicationInvertedScale = 1.0f / applicationScale; 272 compatFlags |= SCALING_REQUIRED; 273 } 274 } 275 276 mCompatibilityFlags = compatFlags; 277 } 278 CompatibilityInfo(int compFlags, int dens, float scale, float invertedScale)279 private CompatibilityInfo(int compFlags, 280 int dens, float scale, float invertedScale) { 281 mCompatibilityFlags = compFlags; 282 applicationDensity = dens; 283 applicationScale = scale; 284 applicationInvertedScale = invertedScale; 285 } 286 287 @UnsupportedAppUsage CompatibilityInfo()288 private CompatibilityInfo() { 289 this(NEVER_NEEDS_COMPAT, DisplayMetrics.DENSITY_DEVICE, 290 1.0f, 291 1.0f); 292 } 293 294 /** 295 * @return true if the scaling is required 296 */ 297 @UnsupportedAppUsage isScalingRequired()298 public boolean isScalingRequired() { 299 return (mCompatibilityFlags & (SCALING_REQUIRED | HAS_OVERRIDE_SCALING)) != 0; 300 } 301 302 @UnsupportedAppUsage supportsScreen()303 public boolean supportsScreen() { 304 return (mCompatibilityFlags&NEEDS_SCREEN_COMPAT) == 0; 305 } 306 neverSupportsScreen()307 public boolean neverSupportsScreen() { 308 return (mCompatibilityFlags&ALWAYS_NEEDS_COMPAT) != 0; 309 } 310 alwaysSupportsScreen()311 public boolean alwaysSupportsScreen() { 312 return (mCompatibilityFlags&NEVER_NEEDS_COMPAT) != 0; 313 } 314 needsCompatResources()315 public boolean needsCompatResources() { 316 return (mCompatibilityFlags&NEEDS_COMPAT_RES) != 0; 317 } 318 319 /** 320 * Returns the translator which translates the coordinates in compatibility mode. 321 * @param params the window's parameter 322 */ 323 @UnsupportedAppUsage getTranslator()324 public Translator getTranslator() { 325 return (mCompatibilityFlags & SCALING_REQUIRED) != 0 ? new Translator() : null; 326 } 327 328 /** 329 * A helper object to translate the screen and window coordinates back and forth. 330 * @hide 331 */ 332 public class Translator { 333 @UnsupportedAppUsage 334 final public float applicationScale; 335 @UnsupportedAppUsage 336 final public float applicationInvertedScale; 337 338 private Rect mContentInsetsBuffer = null; 339 private Rect mVisibleInsetsBuffer = null; 340 private Region mTouchableAreaBuffer = null; 341 Translator(float applicationScale, float applicationInvertedScale)342 Translator(float applicationScale, float applicationInvertedScale) { 343 this.applicationScale = applicationScale; 344 this.applicationInvertedScale = applicationInvertedScale; 345 } 346 Translator()347 Translator() { 348 this(CompatibilityInfo.this.applicationScale, 349 CompatibilityInfo.this.applicationInvertedScale); 350 } 351 352 /** 353 * Translate the region in window to screen. 354 */ 355 @UnsupportedAppUsage translateRegionInWindowToScreen(Region transparentRegion)356 public void translateRegionInWindowToScreen(Region transparentRegion) { 357 transparentRegion.scale(applicationScale); 358 } 359 360 /** 361 * Apply translation to the canvas that is necessary to draw the content. 362 */ 363 @UnsupportedAppUsage translateCanvas(Canvas canvas)364 public void translateCanvas(Canvas canvas) { 365 if (applicationScale == 1.5f) { 366 /* When we scale for compatibility, we can put our stretched 367 bitmaps and ninepatches on exacty 1/2 pixel boundaries, 368 which can give us inconsistent drawing due to imperfect 369 float precision in the graphics engine's inverse matrix. 370 371 As a work-around, we translate by a tiny amount to avoid 372 landing on exact pixel centers and boundaries, giving us 373 the slop we need to draw consistently. 374 375 This constant is meant to resolve to 1/255 after it is 376 scaled by 1.5 (applicationScale). Note, this is just a guess 377 as to what is small enough not to create its own artifacts, 378 and big enough to avoid the precision problems. Feel free 379 to experiment with smaller values as you choose. 380 */ 381 final float tinyOffset = 2.0f / (3 * 255); 382 canvas.translate(tinyOffset, tinyOffset); 383 } 384 canvas.scale(applicationScale, applicationScale); 385 } 386 387 /** 388 * Translate the motion event captured on screen to the application's window. 389 */ 390 @UnsupportedAppUsage translateEventInScreenToAppWindow(MotionEvent event)391 public void translateEventInScreenToAppWindow(MotionEvent event) { 392 event.scale(applicationInvertedScale); 393 } 394 395 /** 396 * Translate the window's layout parameter, from application's view to 397 * Screen's view. 398 */ 399 @UnsupportedAppUsage translateWindowLayout(WindowManager.LayoutParams params)400 public void translateWindowLayout(WindowManager.LayoutParams params) { 401 params.scale(applicationScale); 402 } 403 404 /** 405 * Translate a length in application's window to screen. 406 */ translateLengthInAppWindowToScreen(float length)407 public float translateLengthInAppWindowToScreen(float length) { 408 return length * applicationScale; 409 } 410 411 /** 412 * Translate a Rect in application's window to screen. 413 */ 414 @UnsupportedAppUsage translateRectInAppWindowToScreen(Rect rect)415 public void translateRectInAppWindowToScreen(Rect rect) { 416 rect.scale(applicationScale); 417 } 418 419 /** 420 * Translate a Rect in screen coordinates into the app window's coordinates. 421 */ 422 @UnsupportedAppUsage translateRectInScreenToAppWindow(Rect rect)423 public void translateRectInScreenToAppWindow(Rect rect) { 424 rect.scale(applicationInvertedScale); 425 } 426 427 /** 428 * Translate an {@link InsetsState} in screen coordinates into the app window's coordinates. 429 */ translateInsetsStateInScreenToAppWindow(InsetsState state)430 public void translateInsetsStateInScreenToAppWindow(InsetsState state) { 431 state.scale(applicationInvertedScale); 432 } 433 434 /** 435 * Translate {@link InsetsSourceControl}s in screen coordinates into the app window's 436 * coordinates. 437 */ translateSourceControlsInScreenToAppWindow(InsetsSourceControl[] controls)438 public void translateSourceControlsInScreenToAppWindow(InsetsSourceControl[] controls) { 439 if (controls == null) { 440 return; 441 } 442 final float scale = applicationInvertedScale; 443 if (scale == 1f) { 444 return; 445 } 446 for (InsetsSourceControl control : controls) { 447 if (control == null) { 448 continue; 449 } 450 final Insets hint = control.getInsetsHint(); 451 control.setInsetsHint( 452 (int) (scale * hint.left), 453 (int) (scale * hint.top), 454 (int) (scale * hint.right), 455 (int) (scale * hint.bottom)); 456 } 457 } 458 459 /** 460 * Translate a Point in screen coordinates into the app window's coordinates. 461 */ translatePointInScreenToAppWindow(PointF point)462 public void translatePointInScreenToAppWindow(PointF point) { 463 final float scale = applicationInvertedScale; 464 if (scale != 1.0f) { 465 point.x *= scale; 466 point.y *= scale; 467 } 468 } 469 470 /** 471 * Translate the location of the sub window. 472 * @param params 473 */ translateLayoutParamsInAppWindowToScreen(LayoutParams params)474 public void translateLayoutParamsInAppWindowToScreen(LayoutParams params) { 475 params.scale(applicationScale); 476 } 477 478 /** 479 * Translate the content insets in application window to Screen. This uses 480 * the internal buffer for content insets to avoid extra object allocation. 481 */ 482 @UnsupportedAppUsage getTranslatedContentInsets(Rect contentInsets)483 public Rect getTranslatedContentInsets(Rect contentInsets) { 484 if (mContentInsetsBuffer == null) mContentInsetsBuffer = new Rect(); 485 mContentInsetsBuffer.set(contentInsets); 486 translateRectInAppWindowToScreen(mContentInsetsBuffer); 487 return mContentInsetsBuffer; 488 } 489 490 /** 491 * Translate the visible insets in application window to Screen. This uses 492 * the internal buffer for visible insets to avoid extra object allocation. 493 */ getTranslatedVisibleInsets(Rect visibleInsets)494 public Rect getTranslatedVisibleInsets(Rect visibleInsets) { 495 if (mVisibleInsetsBuffer == null) mVisibleInsetsBuffer = new Rect(); 496 mVisibleInsetsBuffer.set(visibleInsets); 497 translateRectInAppWindowToScreen(mVisibleInsetsBuffer); 498 return mVisibleInsetsBuffer; 499 } 500 501 /** 502 * Translate the touchable area in application window to Screen. This uses 503 * the internal buffer for touchable area to avoid extra object allocation. 504 */ getTranslatedTouchableArea(Region touchableArea)505 public Region getTranslatedTouchableArea(Region touchableArea) { 506 if (mTouchableAreaBuffer == null) mTouchableAreaBuffer = new Region(); 507 mTouchableAreaBuffer.set(touchableArea); 508 mTouchableAreaBuffer.scale(applicationScale); 509 return mTouchableAreaBuffer; 510 } 511 } 512 applyToDisplayMetrics(DisplayMetrics inoutDm)513 public void applyToDisplayMetrics(DisplayMetrics inoutDm) { 514 if (!supportsScreen()) { 515 // This is a larger screen device and the app is not 516 // compatible with large screens, so diddle it. 517 CompatibilityInfo.computeCompatibleScaling(inoutDm, inoutDm); 518 } else { 519 inoutDm.widthPixels = inoutDm.noncompatWidthPixels; 520 inoutDm.heightPixels = inoutDm.noncompatHeightPixels; 521 } 522 523 if (isScalingRequired()) { 524 float invertedRatio = applicationInvertedScale; 525 inoutDm.density = inoutDm.noncompatDensity * invertedRatio; 526 inoutDm.densityDpi = (int)((inoutDm.noncompatDensityDpi * invertedRatio) + .5f); 527 inoutDm.scaledDensity = inoutDm.noncompatScaledDensity * invertedRatio; 528 inoutDm.xdpi = inoutDm.noncompatXdpi * invertedRatio; 529 inoutDm.ydpi = inoutDm.noncompatYdpi * invertedRatio; 530 inoutDm.widthPixels = (int) (inoutDm.widthPixels * invertedRatio + 0.5f); 531 inoutDm.heightPixels = (int) (inoutDm.heightPixels * invertedRatio + 0.5f); 532 } 533 } 534 applyToConfiguration(int displayDensity, Configuration inoutConfig)535 public void applyToConfiguration(int displayDensity, Configuration inoutConfig) { 536 if (!supportsScreen()) { 537 // This is a larger screen device and the app is not 538 // compatible with large screens, so we are forcing it to 539 // run as if the screen is normal size. 540 inoutConfig.screenLayout = 541 (inoutConfig.screenLayout&~Configuration.SCREENLAYOUT_SIZE_MASK) 542 | Configuration.SCREENLAYOUT_SIZE_NORMAL; 543 inoutConfig.screenWidthDp = inoutConfig.compatScreenWidthDp; 544 inoutConfig.screenHeightDp = inoutConfig.compatScreenHeightDp; 545 inoutConfig.smallestScreenWidthDp = inoutConfig.compatSmallestScreenWidthDp; 546 } 547 inoutConfig.densityDpi = displayDensity; 548 if (isScalingRequired()) { 549 float invertedRatio = applicationInvertedScale; 550 inoutConfig.densityDpi = (int)((inoutConfig.densityDpi * invertedRatio) + .5f); 551 inoutConfig.windowConfiguration.getMaxBounds().scale(invertedRatio); 552 inoutConfig.windowConfiguration.getBounds().scale(invertedRatio); 553 final Rect appBounds = inoutConfig.windowConfiguration.getAppBounds(); 554 if (appBounds != null) { 555 appBounds.scale(invertedRatio); 556 } 557 } 558 } 559 560 /** 561 * Compute the frame Rect for applications runs under compatibility mode. 562 * 563 * @param dm the display metrics used to compute the frame size. 564 * @param outDm If non-null the width and height will be set to their scaled values. 565 * @return Returns the scaling factor for the window. 566 */ 567 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) computeCompatibleScaling(DisplayMetrics dm, DisplayMetrics outDm)568 public static float computeCompatibleScaling(DisplayMetrics dm, DisplayMetrics outDm) { 569 final int width = dm.noncompatWidthPixels; 570 final int height = dm.noncompatHeightPixels; 571 int shortSize, longSize; 572 if (width < height) { 573 shortSize = width; 574 longSize = height; 575 } else { 576 shortSize = height; 577 longSize = width; 578 } 579 int newShortSize = (int)(DEFAULT_NORMAL_SHORT_DIMENSION * dm.density + 0.5f); 580 float aspect = ((float)longSize) / shortSize; 581 if (aspect > MAXIMUM_ASPECT_RATIO) { 582 aspect = MAXIMUM_ASPECT_RATIO; 583 } 584 int newLongSize = (int)(newShortSize * aspect + 0.5f); 585 int newWidth, newHeight; 586 if (width < height) { 587 newWidth = newShortSize; 588 newHeight = newLongSize; 589 } else { 590 newWidth = newLongSize; 591 newHeight = newShortSize; 592 } 593 594 float sw = width/(float)newWidth; 595 float sh = height/(float)newHeight; 596 float scale = sw < sh ? sw : sh; 597 if (scale < 1) { 598 scale = 1; 599 } 600 601 if (outDm != null) { 602 outDm.widthPixels = newWidth; 603 outDm.heightPixels = newHeight; 604 } 605 606 return scale; 607 } 608 609 @Override 610 public boolean equals(@Nullable Object o) { 611 if (this == o) { 612 return true; 613 } 614 try { 615 CompatibilityInfo oc = (CompatibilityInfo)o; 616 if (mCompatibilityFlags != oc.mCompatibilityFlags) return false; 617 if (applicationDensity != oc.applicationDensity) return false; 618 if (applicationScale != oc.applicationScale) return false; 619 if (applicationInvertedScale != oc.applicationInvertedScale) return false; 620 return true; 621 } catch (ClassCastException e) { 622 return false; 623 } 624 } 625 626 @Override 627 public String toString() { 628 StringBuilder sb = new StringBuilder(128); 629 sb.append("{"); 630 sb.append(applicationDensity); 631 sb.append("dpi"); 632 if (isScalingRequired()) { 633 sb.append(" "); 634 sb.append(applicationScale); 635 sb.append("x"); 636 } 637 if (!supportsScreen()) { 638 sb.append(" resizing"); 639 } 640 if (neverSupportsScreen()) { 641 sb.append(" never-compat"); 642 } 643 if (alwaysSupportsScreen()) { 644 sb.append(" always-compat"); 645 } 646 sb.append("}"); 647 return sb.toString(); 648 } 649 650 @Override 651 public int hashCode() { 652 int result = 17; 653 result = 31 * result + mCompatibilityFlags; 654 result = 31 * result + applicationDensity; 655 result = 31 * result + Float.floatToIntBits(applicationScale); 656 result = 31 * result + Float.floatToIntBits(applicationInvertedScale); 657 return result; 658 } 659 660 @Override 661 public int describeContents() { 662 return 0; 663 } 664 665 @Override 666 public void writeToParcel(Parcel dest, int flags) { 667 dest.writeInt(mCompatibilityFlags); 668 dest.writeInt(applicationDensity); 669 dest.writeFloat(applicationScale); 670 dest.writeFloat(applicationInvertedScale); 671 } 672 673 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 674 public static final @android.annotation.NonNull Parcelable.Creator<CompatibilityInfo> CREATOR 675 = new Parcelable.Creator<CompatibilityInfo>() { 676 @Override 677 public CompatibilityInfo createFromParcel(Parcel source) { 678 return new CompatibilityInfo(source); 679 } 680 681 @Override 682 public CompatibilityInfo[] newArray(int size) { 683 return new CompatibilityInfo[size]; 684 } 685 }; 686 687 private CompatibilityInfo(Parcel source) { 688 mCompatibilityFlags = source.readInt(); 689 applicationDensity = source.readInt(); 690 applicationScale = source.readFloat(); 691 applicationInvertedScale = source.readFloat(); 692 } 693 } 694