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