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