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