• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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