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             mCompatibilityFlags |= LARGE_SCREENS | CONFIGURED_LARGE_SCREENS;
    144         }
    145         if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) != 0) {
    146             mCompatibilityFlags |= XLARGE_SCREENS | CONFIGURED_XLARGE_SCREENS;
    147         }
    148         if ((appInfo.flags & ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) {
    149             mCompatibilityFlags |= EXPANDABLE | CONFIGURED_EXPANDABLE;
    150         }
    151 
    152         if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) {
    153             applicationDensity = DisplayMetrics.DENSITY_DEVICE;
    154             applicationScale = 1.0f;
    155             applicationInvertedScale = 1.0f;
    156         } else {
    157             applicationDensity = DisplayMetrics.DENSITY_DEFAULT;
    158             applicationScale = DisplayMetrics.DENSITY_DEVICE
    159                     / (float) DisplayMetrics.DENSITY_DEFAULT;
    160             applicationInvertedScale = 1.0f / applicationScale;
    161             mCompatibilityFlags |= SCALING_REQUIRED;
    162         }
    163     }
    164 
    165     private CompatibilityInfo(int appFlags, int compFlags,
    166             int dens, float scale, float invertedScale) {
    167         this.appFlags = appFlags;
    168         mCompatibilityFlags = compFlags;
    169         applicationDensity = dens;
    170         applicationScale = scale;
    171         applicationInvertedScale = invertedScale;
    172     }
    173 
    174     private CompatibilityInfo() {
    175         this(ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS
    176                 | ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS
    177                 | ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS
    178                 | ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS
    179                 | ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS,
    180                 EXPANDABLE | CONFIGURED_EXPANDABLE,
    181                 DisplayMetrics.DENSITY_DEVICE,
    182                 1.0f,
    183                 1.0f);
    184     }
    185 
    186     /**
    187      * Returns the copy of this instance.
    188      */
    189     public CompatibilityInfo copy() {
    190         CompatibilityInfo info = new CompatibilityInfo(appFlags, mCompatibilityFlags,
    191                 applicationDensity, applicationScale, applicationInvertedScale);
    192         return info;
    193     }
    194 
    195     /**
    196      * Sets expandable bit in the compatibility flag.
    197      */
    198     public void setExpandable(boolean expandable) {
    199         if (expandable) {
    200             mCompatibilityFlags |= CompatibilityInfo.EXPANDABLE;
    201         } else {
    202             mCompatibilityFlags &= ~CompatibilityInfo.EXPANDABLE;
    203         }
    204     }
    205 
    206     /**
    207      * Sets large screen bit in the compatibility flag.
    208      */
    209     public void setLargeScreens(boolean expandable) {
    210         if (expandable) {
    211             mCompatibilityFlags |= CompatibilityInfo.LARGE_SCREENS;
    212         } else {
    213             mCompatibilityFlags &= ~CompatibilityInfo.LARGE_SCREENS;
    214         }
    215     }
    216 
    217     /**
    218      * Sets large screen bit in the compatibility flag.
    219      */
    220     public void setXLargeScreens(boolean expandable) {
    221         if (expandable) {
    222             mCompatibilityFlags |= CompatibilityInfo.XLARGE_SCREENS;
    223         } else {
    224             mCompatibilityFlags &= ~CompatibilityInfo.XLARGE_SCREENS;
    225         }
    226     }
    227 
    228     /**
    229      * @return true if the application is configured to be expandable.
    230      */
    231     public boolean isConfiguredExpandable() {
    232         return (mCompatibilityFlags & CompatibilityInfo.CONFIGURED_EXPANDABLE) != 0;
    233     }
    234 
    235     /**
    236      * @return true if the application is configured to be expandable.
    237      */
    238     public boolean isConfiguredLargeScreens() {
    239         return (mCompatibilityFlags & CompatibilityInfo.CONFIGURED_LARGE_SCREENS) != 0;
    240     }
    241 
    242     /**
    243      * @return true if the application is configured to be expandable.
    244      */
    245     public boolean isConfiguredXLargeScreens() {
    246         return (mCompatibilityFlags & CompatibilityInfo.CONFIGURED_XLARGE_SCREENS) != 0;
    247     }
    248 
    249     /**
    250      * @return true if the scaling is required
    251      */
    252     public boolean isScalingRequired() {
    253         return (mCompatibilityFlags & SCALING_REQUIRED) != 0;
    254     }
    255 
    256     public boolean supportsScreen() {
    257         return (mCompatibilityFlags & (EXPANDABLE|LARGE_SCREENS))
    258                 == (EXPANDABLE|LARGE_SCREENS);
    259     }
    260 
    261     @Override
    262     public String toString() {
    263         return "CompatibilityInfo{scale=" + applicationScale +
    264                 ", supports screen=" + supportsScreen() + "}";
    265     }
    266 
    267     /**
    268      * Returns the translator which translates the coordinates in compatibility mode.
    269      * @param params the window's parameter
    270      */
    271     public Translator getTranslator() {
    272         return isScalingRequired() ? new Translator() : null;
    273     }
    274 
    275     /**
    276      * A helper object to translate the screen and window coordinates back and forth.
    277      * @hide
    278      */
    279     public class Translator {
    280         final public float applicationScale;
    281         final public float applicationInvertedScale;
    282 
    283         private Rect mContentInsetsBuffer = null;
    284         private Rect mVisibleInsetsBuffer = null;
    285 
    286         Translator(float applicationScale, float applicationInvertedScale) {
    287             this.applicationScale = applicationScale;
    288             this.applicationInvertedScale = applicationInvertedScale;
    289         }
    290 
    291         Translator() {
    292             this(CompatibilityInfo.this.applicationScale,
    293                     CompatibilityInfo.this.applicationInvertedScale);
    294         }
    295 
    296         /**
    297          * Translate the screen rect to the application frame.
    298          */
    299         public void translateRectInScreenToAppWinFrame(Rect rect) {
    300             rect.scale(applicationInvertedScale);
    301         }
    302 
    303         /**
    304          * Translate the region in window to screen.
    305          */
    306         public void translateRegionInWindowToScreen(Region transparentRegion) {
    307             transparentRegion.scale(applicationScale);
    308         }
    309 
    310         /**
    311          * Apply translation to the canvas that is necessary to draw the content.
    312          */
    313         public void translateCanvas(Canvas canvas) {
    314             if (applicationScale == 1.5f) {
    315                 /*  When we scale for compatibility, we can put our stretched
    316                     bitmaps and ninepatches on exacty 1/2 pixel boundaries,
    317                     which can give us inconsistent drawing due to imperfect
    318                     float precision in the graphics engine's inverse matrix.
    319 
    320                     As a work-around, we translate by a tiny amount to avoid
    321                     landing on exact pixel centers and boundaries, giving us
    322                     the slop we need to draw consistently.
    323 
    324                     This constant is meant to resolve to 1/255 after it is
    325                     scaled by 1.5 (applicationScale). Note, this is just a guess
    326                     as to what is small enough not to create its own artifacts,
    327                     and big enough to avoid the precision problems. Feel free
    328                     to experiment with smaller values as you choose.
    329                  */
    330                 final float tinyOffset = 2.0f / (3 * 255);
    331                 canvas.translate(tinyOffset, tinyOffset);
    332             }
    333             canvas.scale(applicationScale, applicationScale);
    334         }
    335 
    336         /**
    337          * Translate the motion event captured on screen to the application's window.
    338          */
    339         public void translateEventInScreenToAppWindow(MotionEvent event) {
    340             event.scale(applicationInvertedScale);
    341         }
    342 
    343         /**
    344          * Translate the window's layout parameter, from application's view to
    345          * Screen's view.
    346          */
    347         public void translateWindowLayout(WindowManager.LayoutParams params) {
    348             params.scale(applicationScale);
    349         }
    350 
    351         /**
    352          * Translate a Rect in application's window to screen.
    353          */
    354         public void translateRectInAppWindowToScreen(Rect rect) {
    355             rect.scale(applicationScale);
    356         }
    357 
    358         /**
    359          * Translate a Rect in screen coordinates into the app window's coordinates.
    360          */
    361         public void translateRectInScreenToAppWindow(Rect rect) {
    362             rect.scale(applicationInvertedScale);
    363         }
    364 
    365         /**
    366          * Translate the location of the sub window.
    367          * @param params
    368          */
    369         public void translateLayoutParamsInAppWindowToScreen(LayoutParams params) {
    370             params.scale(applicationScale);
    371         }
    372 
    373         /**
    374          * Translate the content insets in application window to Screen. This uses
    375          * the internal buffer for content insets to avoid extra object allocation.
    376          */
    377         public Rect getTranslatedContentInsets(Rect contentInsets) {
    378             if (mContentInsetsBuffer == null) mContentInsetsBuffer = new Rect();
    379             mContentInsetsBuffer.set(contentInsets);
    380             translateRectInAppWindowToScreen(mContentInsetsBuffer);
    381             return mContentInsetsBuffer;
    382         }
    383 
    384         /**
    385          * Translate the visible insets in application window to Screen. This uses
    386          * the internal buffer for content insets to avoid extra object allocation.
    387          */
    388         public Rect getTranslatedVisbileInsets(Rect visibleInsets) {
    389             if (mVisibleInsetsBuffer == null) mVisibleInsetsBuffer = new Rect();
    390             mVisibleInsetsBuffer.set(visibleInsets);
    391             translateRectInAppWindowToScreen(mVisibleInsetsBuffer);
    392             return mVisibleInsetsBuffer;
    393         }
    394     }
    395 
    396     /**
    397      * Returns the frame Rect for applications runs under compatibility mode.
    398      *
    399      * @param dm the display metrics used to compute the frame size.
    400      * @param orientation the orientation of the screen.
    401      * @param outRect the output parameter which will contain the result.
    402      */
    403     public static void updateCompatibleScreenFrame(DisplayMetrics dm, int orientation,
    404             Rect outRect) {
    405         int width = dm.widthPixels;
    406         int portraitHeight = (int) (DEFAULT_PORTRAIT_HEIGHT * dm.density + 0.5f);
    407         int portraitWidth = (int) (DEFAULT_PORTRAIT_WIDTH * dm.density + 0.5f);
    408         if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
    409             int xOffset = (width - portraitHeight) / 2 ;
    410             outRect.set(xOffset, 0, xOffset + portraitHeight, portraitWidth);
    411         } else {
    412             int xOffset = (width - portraitWidth) / 2 ;
    413             outRect.set(xOffset, 0, xOffset + portraitWidth, portraitHeight);
    414         }
    415     }
    416 }
    417