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