Home | History | Annotate | Download | only in res
      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.content.pm.ApplicationInfo;
     20 import android.graphics.Canvas;
     21 import android.graphics.Rect;
     22 import android.graphics.Region;
     23 import android.util.DisplayMetrics;
     24 import android.util.Log;
     25 import android.view.Gravity;
     26 import android.view.MotionEvent;
     27 import android.view.WindowManager;
     28 import android.view.WindowManager.LayoutParams;
     29 
     30 /**
     31  * CompatibilityInfo class keeps the information about compatibility mode that the application is
     32  * running under.
     33  *
     34  *  {@hide}
     35  */
     36 public class CompatibilityInfo {
     37     private static final boolean DBG = false;
     38     private static final String TAG = "CompatibilityInfo";
     39 
     40     /** default compatibility info object for compatible applications */
     41     public static final CompatibilityInfo DEFAULT_COMPATIBILITY_INFO = new CompatibilityInfo() {
     42         @Override
     43         public void setExpandable(boolean expandable) {
     44             throw new UnsupportedOperationException("trying to change default compatibility info");
     45         }
     46     };
     47 
     48     /**
     49      * The default width of the screen in portrait mode.
     50      */
     51     public static final int DEFAULT_PORTRAIT_WIDTH = 320;
     52 
     53     /**
     54      * The default height of the screen in portrait mode.
     55      */
     56     public static final int DEFAULT_PORTRAIT_HEIGHT = 480;
     57 
     58     /**
     59      *  A compatibility flags
     60      */
     61     private 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      * A flag mask to indicates that the application can expand over the original size.
     71      * The flag is set to true if
     72      * 1) Application declares its expandable in manifest file using <supports-screens> or
     73      * 2) Configuration.SCREENLAYOUT_COMPAT_NEEDED is not set
     74      * {@see compatibilityFlag}
     75      */
     76     private static final int EXPANDABLE = 2;
     77 
     78     /**
     79      * A flag mask to tell if the application is configured to be expandable. This differs
     80      * from EXPANDABLE in that the application that is not expandable will be
     81      * marked as expandable if Configuration.SCREENLAYOUT_COMPAT_NEEDED is not set.
     82      */
     83     private static final int CONFIGURED_EXPANDABLE = 4;
     84 
     85     /**
     86      * A flag mask to indicates that the application supports large screens.
     87      * The flag is set to true if
     88      * 1) Application declares it supports large screens in manifest file using <supports-screens> or
     89      * 2) The screen size is not large
     90      * {@see compatibilityFlag}
     91      */
     92     private static final int LARGE_SCREENS = 8;
     93 
     94     /**
     95      * A flag mask to tell if the application supports large screens. This differs
     96      * from LARGE_SCREENS in that the application that does not support large
     97      * screens will be marked as supporting them if the current screen is not
     98      * large.
     99      */
    100     private static final int CONFIGURED_LARGE_SCREENS = 16;
    101 
    102     /**
    103      * A flag mask to indicates that the application supports xlarge screens.
    104      * The flag is set to true if
    105      * 1) Application declares it supports xlarge screens in manifest file using <supports-screens> or
    106      * 2) The screen size is not xlarge
    107      * {@see compatibilityFlag}
    108      */
    109     private static final int XLARGE_SCREENS = 32;
    110 
    111     /**
    112      * A flag mask to tell if the application supports xlarge screens. This differs
    113      * from XLARGE_SCREENS in that the application that does not support xlarge
    114      * screens will be marked as supporting them if the current screen is not
    115      * xlarge.
    116      */
    117     private static final int CONFIGURED_XLARGE_SCREENS = 64;
    118 
    119     /**
    120      * The effective screen density we have selected for this application.
    121      */
    122     public final int applicationDensity;
    123 
    124     /**
    125      * Application's scale.
    126      */
    127     public final float applicationScale;
    128 
    129     /**
    130      * Application's inverted scale.
    131      */
    132     public final float applicationInvertedScale;
    133 
    134     /**
    135      * The flags from ApplicationInfo.
    136      */
    137     public final int appFlags;
    138 
    139     public CompatibilityInfo(ApplicationInfo appInfo) {
    140         appFlags = appInfo.flags;
    141 
    142         if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) {
    143             // Saying you support large screens also implies you support xlarge
    144             // screens; there is no compatibility mode for a large app on an
    145             // xlarge screen.
    146             mCompatibilityFlags |= LARGE_SCREENS | CONFIGURED_LARGE_SCREENS
    147                     | XLARGE_SCREENS | CONFIGURED_XLARGE_SCREENS
    148                     | EXPANDABLE | CONFIGURED_EXPANDABLE;
    149         }
    150         if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) != 0) {
    151             mCompatibilityFlags |= XLARGE_SCREENS | CONFIGURED_XLARGE_SCREENS
    152                     | EXPANDABLE | CONFIGURED_EXPANDABLE;
    153         }
    154         if ((appInfo.flags & ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) {
    155             mCompatibilityFlags |= EXPANDABLE | CONFIGURED_EXPANDABLE;
    156         }
    157 
    158         if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) {
    159             applicationDensity = DisplayMetrics.DENSITY_DEVICE;
    160             applicationScale = 1.0f;
    161             applicationInvertedScale = 1.0f;
    162         } else {
    163             applicationDensity = DisplayMetrics.DENSITY_DEFAULT;
    164             applicationScale = DisplayMetrics.DENSITY_DEVICE
    165                     / (float) DisplayMetrics.DENSITY_DEFAULT;
    166             applicationInvertedScale = 1.0f / applicationScale;
    167             mCompatibilityFlags |= SCALING_REQUIRED;
    168         }
    169     }
    170 
    171     private CompatibilityInfo(int appFlags, int compFlags,
    172             int dens, float scale, float invertedScale) {
    173         this.appFlags = appFlags;
    174         mCompatibilityFlags = compFlags;
    175         applicationDensity = dens;
    176         applicationScale = scale;
    177         applicationInvertedScale = invertedScale;
    178     }
    179 
    180     private CompatibilityInfo() {
    181         this(ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS
    182                 | ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS
    183                 | ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS
    184                 | ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS
    185                 | ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS,
    186                 EXPANDABLE | CONFIGURED_EXPANDABLE,
    187                 DisplayMetrics.DENSITY_DEVICE,
    188                 1.0f,
    189                 1.0f);
    190     }
    191 
    192     /**
    193      * Returns the copy of this instance.
    194      */
    195     public CompatibilityInfo copy() {
    196         CompatibilityInfo info = new CompatibilityInfo(appFlags, mCompatibilityFlags,
    197                 applicationDensity, applicationScale, applicationInvertedScale);
    198         return info;
    199     }
    200 
    201     /**
    202      * Sets expandable bit in the compatibility flag.
    203      */
    204     public void setExpandable(boolean expandable) {
    205         if (expandable) {
    206             mCompatibilityFlags |= CompatibilityInfo.EXPANDABLE;
    207         } else {
    208             mCompatibilityFlags &= ~CompatibilityInfo.EXPANDABLE;
    209         }
    210     }
    211 
    212     /**
    213      * Sets large screen bit in the compatibility flag.
    214      */
    215     public void setLargeScreens(boolean expandable) {
    216         if (expandable) {
    217             mCompatibilityFlags |= CompatibilityInfo.LARGE_SCREENS;
    218         } else {
    219             mCompatibilityFlags &= ~CompatibilityInfo.LARGE_SCREENS;
    220         }
    221     }
    222 
    223     /**
    224      * Sets large screen bit in the compatibility flag.
    225      */
    226     public void setXLargeScreens(boolean expandable) {
    227         if (expandable) {
    228             mCompatibilityFlags |= CompatibilityInfo.XLARGE_SCREENS;
    229         } else {
    230             mCompatibilityFlags &= ~CompatibilityInfo.XLARGE_SCREENS;
    231         }
    232     }
    233 
    234     /**
    235      * @return true if the application is configured to be expandable.
    236      */
    237     public boolean isConfiguredExpandable() {
    238         return (mCompatibilityFlags & CompatibilityInfo.CONFIGURED_EXPANDABLE) != 0;
    239     }
    240 
    241     /**
    242      * @return true if the application is configured to be expandable.
    243      */
    244     public boolean isConfiguredLargeScreens() {
    245         return (mCompatibilityFlags & CompatibilityInfo.CONFIGURED_LARGE_SCREENS) != 0;
    246     }
    247 
    248     /**
    249      * @return true if the application is configured to be expandable.
    250      */
    251     public boolean isConfiguredXLargeScreens() {
    252         return (mCompatibilityFlags & CompatibilityInfo.CONFIGURED_XLARGE_SCREENS) != 0;
    253     }
    254 
    255     /**
    256      * @return true if the scaling is required
    257      */
    258     public boolean isScalingRequired() {
    259         return (mCompatibilityFlags & SCALING_REQUIRED) != 0;
    260     }
    261 
    262     public boolean supportsScreen() {
    263         return (mCompatibilityFlags & (EXPANDABLE|LARGE_SCREENS))
    264                 == (EXPANDABLE|LARGE_SCREENS);
    265     }
    266 
    267     @Override
    268     public String toString() {
    269         return "CompatibilityInfo{scale=" + applicationScale +
    270                 ", supports screen=" + supportsScreen() + "}";
    271     }
    272 
    273     /**
    274      * Returns the translator which translates the coordinates in compatibility mode.
    275      * @param params the window's parameter
    276      */
    277     public Translator getTranslator() {
    278         return isScalingRequired() ? new Translator() : null;
    279     }
    280 
    281     /**
    282      * A helper object to translate the screen and window coordinates back and forth.
    283      * @hide
    284      */
    285     public class Translator {
    286         final public float applicationScale;
    287         final public float applicationInvertedScale;
    288 
    289         private Rect mContentInsetsBuffer = null;
    290         private Rect mVisibleInsetsBuffer = null;
    291 
    292         Translator(float applicationScale, float applicationInvertedScale) {
    293             this.applicationScale = applicationScale;
    294             this.applicationInvertedScale = applicationInvertedScale;
    295         }
    296 
    297         Translator() {
    298             this(CompatibilityInfo.this.applicationScale,
    299                     CompatibilityInfo.this.applicationInvertedScale);
    300         }
    301 
    302         /**
    303          * Translate the screen rect to the application frame.
    304          */
    305         public void translateRectInScreenToAppWinFrame(Rect rect) {
    306             rect.scale(applicationInvertedScale);
    307         }
    308 
    309         /**
    310          * Translate the region in window to screen.
    311          */
    312         public void translateRegionInWindowToScreen(Region transparentRegion) {
    313             transparentRegion.scale(applicationScale);
    314         }
    315 
    316         /**
    317          * Apply translation to the canvas that is necessary to draw the content.
    318          */
    319         public void translateCanvas(Canvas canvas) {
    320             if (applicationScale == 1.5f) {
    321                 /*  When we scale for compatibility, we can put our stretched
    322                     bitmaps and ninepatches on exacty 1/2 pixel boundaries,
    323                     which can give us inconsistent drawing due to imperfect
    324                     float precision in the graphics engine's inverse matrix.
    325 
    326                     As a work-around, we translate by a tiny amount to avoid
    327                     landing on exact pixel centers and boundaries, giving us
    328                     the slop we need to draw consistently.
    329 
    330                     This constant is meant to resolve to 1/255 after it is
    331                     scaled by 1.5 (applicationScale). Note, this is just a guess
    332                     as to what is small enough not to create its own artifacts,
    333                     and big enough to avoid the precision problems. Feel free
    334                     to experiment with smaller values as you choose.
    335                  */
    336                 final float tinyOffset = 2.0f / (3 * 255);
    337                 canvas.translate(tinyOffset, tinyOffset);
    338             }
    339             canvas.scale(applicationScale, applicationScale);
    340         }
    341 
    342         /**
    343          * Translate the motion event captured on screen to the application's window.
    344          */
    345         public void translateEventInScreenToAppWindow(MotionEvent event) {
    346             event.scale(applicationInvertedScale);
    347         }
    348 
    349         /**
    350          * Translate the window's layout parameter, from application's view to
    351          * Screen's view.
    352          */
    353         public void translateWindowLayout(WindowManager.LayoutParams params) {
    354             params.scale(applicationScale);
    355         }
    356 
    357         /**
    358          * Translate a Rect in application's window to screen.
    359          */
    360         public void translateRectInAppWindowToScreen(Rect rect) {
    361             rect.scale(applicationScale);
    362         }
    363 
    364         /**
    365          * Translate a Rect in screen coordinates into the app window's coordinates.
    366          */
    367         public void translateRectInScreenToAppWindow(Rect rect) {
    368             rect.scale(applicationInvertedScale);
    369         }
    370 
    371         /**
    372          * Translate the location of the sub window.
    373          * @param params
    374          */
    375         public void translateLayoutParamsInAppWindowToScreen(LayoutParams params) {
    376             params.scale(applicationScale);
    377         }
    378 
    379         /**
    380          * Translate the content insets in application window to Screen. This uses
    381          * the internal buffer for content insets to avoid extra object allocation.
    382          */
    383         public Rect getTranslatedContentInsets(Rect contentInsets) {
    384             if (mContentInsetsBuffer == null) mContentInsetsBuffer = new Rect();
    385             mContentInsetsBuffer.set(contentInsets);
    386             translateRectInAppWindowToScreen(mContentInsetsBuffer);
    387             return mContentInsetsBuffer;
    388         }
    389 
    390         /**
    391          * Translate the visible insets in application window to Screen. This uses
    392          * the internal buffer for content insets to avoid extra object allocation.
    393          */
    394         public Rect getTranslatedVisbileInsets(Rect visibleInsets) {
    395             if (mVisibleInsetsBuffer == null) mVisibleInsetsBuffer = new Rect();
    396             mVisibleInsetsBuffer.set(visibleInsets);
    397             translateRectInAppWindowToScreen(mVisibleInsetsBuffer);
    398             return mVisibleInsetsBuffer;
    399         }
    400     }
    401 
    402     /**
    403      * Returns the frame Rect for applications runs under compatibility mode.
    404      *
    405      * @param dm the display metrics used to compute the frame size.
    406      * @param orientation the orientation of the screen.
    407      * @param outRect the output parameter which will contain the result.
    408      */
    409     public static void updateCompatibleScreenFrame(DisplayMetrics dm, int orientation,
    410             Rect outRect) {
    411         int width = dm.widthPixels;
    412         int portraitHeight = (int) (DEFAULT_PORTRAIT_HEIGHT * dm.density + 0.5f);
    413         int portraitWidth = (int) (DEFAULT_PORTRAIT_WIDTH * dm.density + 0.5f);
    414         if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
    415             int xOffset = (width - portraitHeight) / 2 ;
    416             outRect.set(xOffset, 0, xOffset + portraitHeight, portraitWidth);
    417         } else {
    418             int xOffset = (width - portraitWidth) / 2 ;
    419             outRect.set(xOffset, 0, xOffset + portraitWidth, portraitHeight);
    420         }
    421     }
    422 }
    423