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 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