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