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