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(@ullable Rect rect)423 public void translateRectInScreenToAppWindow(@Nullable Rect rect) { 424 if (rect == null) { 425 return; 426 } 427 rect.scale(applicationInvertedScale); 428 } 429 430 /** 431 * Translate an {@link InsetsState} in screen coordinates into the app window's coordinates. 432 */ translateInsetsStateInScreenToAppWindow(InsetsState state)433 public void translateInsetsStateInScreenToAppWindow(InsetsState state) { 434 state.scale(applicationInvertedScale); 435 } 436 437 /** 438 * Translate {@link InsetsSourceControl}s in screen coordinates into the app window's 439 * coordinates. 440 */ translateSourceControlsInScreenToAppWindow(InsetsSourceControl[] controls)441 public void translateSourceControlsInScreenToAppWindow(InsetsSourceControl[] controls) { 442 if (controls == null) { 443 return; 444 } 445 final float scale = applicationInvertedScale; 446 if (scale == 1f) { 447 return; 448 } 449 for (InsetsSourceControl control : controls) { 450 if (control == null) { 451 continue; 452 } 453 final Insets hint = control.getInsetsHint(); 454 control.setInsetsHint( 455 (int) (scale * hint.left), 456 (int) (scale * hint.top), 457 (int) (scale * hint.right), 458 (int) (scale * hint.bottom)); 459 } 460 } 461 462 /** 463 * Translate a Point in screen coordinates into the app window's coordinates. 464 */ translatePointInScreenToAppWindow(PointF point)465 public void translatePointInScreenToAppWindow(PointF point) { 466 final float scale = applicationInvertedScale; 467 if (scale != 1.0f) { 468 point.x *= scale; 469 point.y *= scale; 470 } 471 } 472 473 /** 474 * Translate the location of the sub window. 475 * @param params 476 */ translateLayoutParamsInAppWindowToScreen(LayoutParams params)477 public void translateLayoutParamsInAppWindowToScreen(LayoutParams params) { 478 params.scale(applicationScale); 479 } 480 481 /** 482 * Translate the content insets in application window to Screen. This uses 483 * the internal buffer for content insets to avoid extra object allocation. 484 */ 485 @UnsupportedAppUsage getTranslatedContentInsets(Rect contentInsets)486 public Rect getTranslatedContentInsets(Rect contentInsets) { 487 if (mContentInsetsBuffer == null) mContentInsetsBuffer = new Rect(); 488 mContentInsetsBuffer.set(contentInsets); 489 translateRectInAppWindowToScreen(mContentInsetsBuffer); 490 return mContentInsetsBuffer; 491 } 492 493 /** 494 * Translate the visible insets in application window to Screen. This uses 495 * the internal buffer for visible insets to avoid extra object allocation. 496 */ getTranslatedVisibleInsets(Rect visibleInsets)497 public Rect getTranslatedVisibleInsets(Rect visibleInsets) { 498 if (mVisibleInsetsBuffer == null) mVisibleInsetsBuffer = new Rect(); 499 mVisibleInsetsBuffer.set(visibleInsets); 500 translateRectInAppWindowToScreen(mVisibleInsetsBuffer); 501 return mVisibleInsetsBuffer; 502 } 503 504 /** 505 * Translate the touchable area in application window to Screen. This uses 506 * the internal buffer for touchable area to avoid extra object allocation. 507 */ getTranslatedTouchableArea(Region touchableArea)508 public Region getTranslatedTouchableArea(Region touchableArea) { 509 if (mTouchableAreaBuffer == null) mTouchableAreaBuffer = new Region(); 510 mTouchableAreaBuffer.set(touchableArea); 511 mTouchableAreaBuffer.scale(applicationScale); 512 return mTouchableAreaBuffer; 513 } 514 } 515 applyToDisplayMetrics(DisplayMetrics inoutDm)516 public void applyToDisplayMetrics(DisplayMetrics inoutDm) { 517 if (!supportsScreen()) { 518 // This is a larger screen device and the app is not 519 // compatible with large screens, so diddle it. 520 CompatibilityInfo.computeCompatibleScaling(inoutDm, inoutDm); 521 } else { 522 inoutDm.widthPixels = inoutDm.noncompatWidthPixels; 523 inoutDm.heightPixels = inoutDm.noncompatHeightPixels; 524 } 525 526 if (isScalingRequired()) { 527 float invertedRatio = applicationInvertedScale; 528 inoutDm.density = inoutDm.noncompatDensity * invertedRatio; 529 inoutDm.densityDpi = (int)((inoutDm.noncompatDensityDpi * invertedRatio) + .5f); 530 inoutDm.scaledDensity = inoutDm.noncompatScaledDensity * invertedRatio; 531 inoutDm.xdpi = inoutDm.noncompatXdpi * invertedRatio; 532 inoutDm.ydpi = inoutDm.noncompatYdpi * invertedRatio; 533 inoutDm.widthPixels = (int) (inoutDm.widthPixels * invertedRatio + 0.5f); 534 inoutDm.heightPixels = (int) (inoutDm.heightPixels * invertedRatio + 0.5f); 535 } 536 } 537 applyToConfiguration(int displayDensity, Configuration inoutConfig)538 public void applyToConfiguration(int displayDensity, Configuration inoutConfig) { 539 if (!supportsScreen()) { 540 // This is a larger screen device and the app is not 541 // compatible with large screens, so we are forcing it to 542 // run as if the screen is normal size. 543 inoutConfig.screenLayout = 544 (inoutConfig.screenLayout&~Configuration.SCREENLAYOUT_SIZE_MASK) 545 | Configuration.SCREENLAYOUT_SIZE_NORMAL; 546 inoutConfig.screenWidthDp = inoutConfig.compatScreenWidthDp; 547 inoutConfig.screenHeightDp = inoutConfig.compatScreenHeightDp; 548 inoutConfig.smallestScreenWidthDp = inoutConfig.compatSmallestScreenWidthDp; 549 } 550 inoutConfig.densityDpi = displayDensity; 551 if (isScalingRequired()) { 552 float invertedRatio = applicationInvertedScale; 553 inoutConfig.densityDpi = (int)((inoutConfig.densityDpi * invertedRatio) + .5f); 554 inoutConfig.windowConfiguration.scale(invertedRatio); 555 } 556 } 557 558 /** 559 * Compute the frame Rect for applications runs under compatibility mode. 560 * 561 * @param dm the display metrics used to compute the frame size. 562 * @param outDm If non-null the width and height will be set to their scaled values. 563 * @return Returns the scaling factor for the window. 564 */ 565 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) computeCompatibleScaling(DisplayMetrics dm, DisplayMetrics outDm)566 public static float computeCompatibleScaling(DisplayMetrics dm, DisplayMetrics outDm) { 567 final int width = dm.noncompatWidthPixels; 568 final int height = dm.noncompatHeightPixels; 569 int shortSize, longSize; 570 if (width < height) { 571 shortSize = width; 572 longSize = height; 573 } else { 574 shortSize = height; 575 longSize = width; 576 } 577 int newShortSize = (int)(DEFAULT_NORMAL_SHORT_DIMENSION * dm.density + 0.5f); 578 float aspect = ((float)longSize) / shortSize; 579 if (aspect > MAXIMUM_ASPECT_RATIO) { 580 aspect = MAXIMUM_ASPECT_RATIO; 581 } 582 int newLongSize = (int)(newShortSize * aspect + 0.5f); 583 int newWidth, newHeight; 584 if (width < height) { 585 newWidth = newShortSize; 586 newHeight = newLongSize; 587 } else { 588 newWidth = newLongSize; 589 newHeight = newShortSize; 590 } 591 592 float sw = width/(float)newWidth; 593 float sh = height/(float)newHeight; 594 float scale = sw < sh ? sw : sh; 595 if (scale < 1) { 596 scale = 1; 597 } 598 599 if (outDm != null) { 600 outDm.widthPixels = newWidth; 601 outDm.heightPixels = newHeight; 602 } 603 604 return scale; 605 } 606 607 @Override 608 public boolean equals(@Nullable Object o) { 609 if (this == o) { 610 return true; 611 } 612 try { 613 CompatibilityInfo oc = (CompatibilityInfo)o; 614 if (mCompatibilityFlags != oc.mCompatibilityFlags) return false; 615 if (applicationDensity != oc.applicationDensity) return false; 616 if (applicationScale != oc.applicationScale) return false; 617 if (applicationInvertedScale != oc.applicationInvertedScale) return false; 618 return true; 619 } catch (ClassCastException e) { 620 return false; 621 } 622 } 623 624 @Override 625 public String toString() { 626 StringBuilder sb = new StringBuilder(128); 627 sb.append("{"); 628 sb.append(applicationDensity); 629 sb.append("dpi"); 630 if (isScalingRequired()) { 631 sb.append(" "); 632 sb.append(applicationScale); 633 sb.append("x"); 634 } 635 if (!supportsScreen()) { 636 sb.append(" resizing"); 637 } 638 if (neverSupportsScreen()) { 639 sb.append(" never-compat"); 640 } 641 if (alwaysSupportsScreen()) { 642 sb.append(" always-compat"); 643 } 644 sb.append("}"); 645 return sb.toString(); 646 } 647 648 @Override 649 public int hashCode() { 650 int result = 17; 651 result = 31 * result + mCompatibilityFlags; 652 result = 31 * result + applicationDensity; 653 result = 31 * result + Float.floatToIntBits(applicationScale); 654 result = 31 * result + Float.floatToIntBits(applicationInvertedScale); 655 return result; 656 } 657 658 @Override 659 public int describeContents() { 660 return 0; 661 } 662 663 @Override 664 public void writeToParcel(Parcel dest, int flags) { 665 dest.writeInt(mCompatibilityFlags); 666 dest.writeInt(applicationDensity); 667 dest.writeFloat(applicationScale); 668 dest.writeFloat(applicationInvertedScale); 669 } 670 671 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 672 public static final @android.annotation.NonNull Parcelable.Creator<CompatibilityInfo> CREATOR 673 = new Parcelable.Creator<CompatibilityInfo>() { 674 @Override 675 public CompatibilityInfo createFromParcel(Parcel source) { 676 return new CompatibilityInfo(source); 677 } 678 679 @Override 680 public CompatibilityInfo[] newArray(int size) { 681 return new CompatibilityInfo[size]; 682 } 683 }; 684 685 private CompatibilityInfo(Parcel source) { 686 mCompatibilityFlags = source.readInt(); 687 applicationDensity = source.readInt(); 688 applicationScale = source.readFloat(); 689 applicationInvertedScale = source.readFloat(); 690 } 691 } 692