1 package org.robolectric.shadows; 2 3 import static android.os.Build.VERSION_CODES.JELLY_BEAN; 4 import static org.robolectric.shadow.api.Shadow.directlyOn; 5 6 import android.content.Context; 7 import android.content.res.Configuration; 8 import android.graphics.Point; 9 import android.util.DisplayMetrics; 10 import android.view.Display; 11 import android.view.Surface; 12 import android.view.WindowManager; 13 import org.robolectric.RuntimeEnvironment; 14 import org.robolectric.annotation.Implementation; 15 import org.robolectric.annotation.Implements; 16 import org.robolectric.annotation.RealObject; 17 import org.robolectric.util.ReflectionHelpers; 18 19 /** 20 * It is possible to override some display properties using setters on {@link ShadowDisplay}; 21 * however, this behavior is deprecated as of Robolectric 3.6 and will be removed in 3.7. 22 * 23 * Use [device configuration](http://robolectric.org/device-configuration/) to set up your 24 * display properties instead. 25 */ 26 @SuppressWarnings({"UnusedDeclaration"}) 27 @Implements(value = Display.class) 28 public class ShadowDisplay { 29 30 /** 31 * Returns the default display. 32 * 33 * @return the default display 34 */ getDefaultDisplay()35 public static Display getDefaultDisplay() { 36 WindowManager windowManager = 37 (WindowManager) RuntimeEnvironment.application.getSystemService(Context.WINDOW_SERVICE); 38 return windowManager.getDefaultDisplay(); 39 } 40 41 @RealObject Display realObject; 42 43 private Float refreshRate; 44 45 // the following fields are used only for Jelly Bean... 46 private String name; 47 private Integer displayId; 48 private Integer width; 49 private Integer height; 50 private Integer realWidth; 51 private Integer realHeight; 52 private Integer densityDpi; 53 private Float xdpi; 54 private Float ydpi; 55 private Float scaledDensity; 56 private Integer rotation; 57 private Integer pixelFormat; 58 59 /** 60 * If {@link #setScaledDensity(float)} has been called, {@link DisplayMetrics#scaledDensity} will 61 * be modified to reflect the value specified. Note that this is not a realistic state. 62 * 63 * @deprecated This behavior is deprecated and will be removed in Robolectric 3.7. 64 */ 65 @Deprecated 66 @Implementation getMetrics(DisplayMetrics outMetrics)67 protected void getMetrics(DisplayMetrics outMetrics) { 68 if (isJB()) { 69 outMetrics.density = densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE; 70 outMetrics.densityDpi = densityDpi; 71 outMetrics.scaledDensity = scaledDensity; 72 outMetrics.widthPixels = width; 73 outMetrics.heightPixels = height; 74 outMetrics.xdpi = xdpi; 75 outMetrics.ydpi = ydpi; 76 } else { 77 directlyOn(realObject, Display.class).getMetrics(outMetrics); 78 if (scaledDensity != null) { 79 outMetrics.scaledDensity = scaledDensity; 80 } 81 } 82 } 83 84 /** 85 * If {@link #setScaledDensity(float)} has been called, {@link DisplayMetrics#scaledDensity} will 86 * be modified to reflect the value specified. Note that this is not a realistic state. 87 * 88 * @deprecated This behavior is deprecated and will be removed in Robolectric 3.7. 89 */ 90 @Deprecated 91 @Implementation getRealMetrics(DisplayMetrics outMetrics)92 protected void getRealMetrics(DisplayMetrics outMetrics) { 93 if (isJB()) { 94 getMetrics(outMetrics); 95 outMetrics.widthPixels = realWidth; 96 outMetrics.heightPixels = realHeight; 97 } else { 98 directlyOn(realObject, Display.class).getRealMetrics(outMetrics); 99 if (scaledDensity != null) { 100 outMetrics.scaledDensity = scaledDensity; 101 } 102 } 103 } 104 105 /** 106 * If {@link #setDisplayId(int)} has been called, this method will return the specified value. 107 * 108 * @deprecated This behavior is deprecated and will be removed in Robolectric 3.7. 109 */ 110 @Deprecated 111 @Implementation getDisplayId()112 protected int getDisplayId() { 113 return displayId == null 114 ? directlyOn(realObject, Display.class).getDisplayId() 115 : displayId; 116 } 117 118 /** 119 * If {@link #setRefreshRate(float)} has been called, this method will return the specified value. 120 * 121 * @deprecated This behavior is deprecated and will be removed in Robolectric 3.7. 122 */ 123 @Deprecated 124 @Implementation getRefreshRate()125 protected float getRefreshRate() { 126 return refreshRate == null 127 ? directlyOn(realObject, Display.class).getRefreshRate() 128 : refreshRate; 129 } 130 131 /** 132 * If {@link #setPixelFormat(int)} has been called, this method will return the specified value. 133 * 134 * @deprecated This behavior is deprecated and will be removed in Robolectric 3.7. 135 */ 136 @Deprecated 137 @Implementation getPixelFormat()138 protected int getPixelFormat() { 139 return pixelFormat == null 140 ? directlyOn(realObject, Display.class).getPixelFormat() 141 : pixelFormat; 142 } 143 144 @Implementation(maxSdk = JELLY_BEAN) getSizeInternal(Point outSize, boolean doCompat)145 protected void getSizeInternal(Point outSize, boolean doCompat) { 146 outSize.x = width; 147 outSize.y = height; 148 } 149 150 @Implementation(maxSdk = JELLY_BEAN) getCurrentSizeRange(Point outSmallestSize, Point outLargestSize)151 protected void getCurrentSizeRange(Point outSmallestSize, Point outLargestSize) { 152 int minimum = Math.min(width, height); 153 int maximum = Math.max(width, height); 154 outSmallestSize.set(minimum, minimum); 155 outLargestSize.set(maximum, maximum); 156 } 157 158 @Implementation(maxSdk = JELLY_BEAN) getRealSize(Point outSize)159 protected void getRealSize(Point outSize) { 160 outSize.set(realWidth, realHeight); 161 } 162 163 /** 164 * Changes the density for this display. 165 * 166 * Any registered {@link android.hardware.display.DisplayManager.DisplayListener}s will be 167 * notified of the change. 168 */ setDensity(float density)169 public void setDensity(float density) { 170 setDensityDpi((int) (density * DisplayMetrics.DENSITY_DEFAULT)); 171 } 172 173 /** 174 * Changes the density for this display. 175 * 176 * Any registered {@link android.hardware.display.DisplayManager.DisplayListener}s will be 177 * notified of the change. 178 */ setDensityDpi(int densityDpi)179 public void setDensityDpi(int densityDpi) { 180 if (isJB()) { 181 this.densityDpi = densityDpi; 182 } else { 183 ShadowDisplayManager.changeDisplay(realObject.getDisplayId(), 184 di -> di.logicalDensityDpi = densityDpi); 185 } 186 } 187 188 /** 189 * Changes the horizontal DPI for this display. 190 * 191 * Any registered {@link android.hardware.display.DisplayManager.DisplayListener}s will be 192 * notified of the change. 193 */ setXdpi(float xdpi)194 public void setXdpi(float xdpi) { 195 if (isJB()) { 196 this.xdpi = xdpi; 197 } else { 198 ShadowDisplayManager.changeDisplay(realObject.getDisplayId(), 199 di -> di.physicalXDpi = xdpi); 200 } 201 } 202 203 /** 204 * Changes the vertical DPI for this display. 205 * 206 * Any registered {@link android.hardware.display.DisplayManager.DisplayListener}s will be 207 * notified of the change. 208 */ setYdpi(float ydpi)209 public void setYdpi(float ydpi) { 210 if (isJB()) { 211 this.ydpi = ydpi; 212 } else { 213 ShadowDisplayManager.changeDisplay(realObject.getDisplayId(), 214 di -> di.physicalYDpi = ydpi); 215 } 216 } 217 218 /** 219 * Changes the scaled density for this display. 220 * 221 * @deprecated This method is deprecated and will be removed in Robolectric 3.7. 222 */ 223 @Deprecated setScaledDensity(float scaledDensity)224 public void setScaledDensity(float scaledDensity) { 225 this.scaledDensity = scaledDensity; 226 } 227 228 /** 229 * Changes the ID for this display. 230 * 231 * Any registered {@link android.hardware.display.DisplayManager.DisplayListener}s will be 232 * notified of the change. 233 * 234 * @deprecated This method is deprecated and will be removed in Robolectric 3.7. 235 */ 236 @Deprecated setDisplayId(int displayId)237 public void setDisplayId(int displayId) { 238 this.displayId = displayId; 239 } 240 241 /** 242 * Changes the name for this display. 243 * 244 * Any registered {@link android.hardware.display.DisplayManager.DisplayListener}s will be 245 * notified of the change. 246 */ setName(String name)247 public void setName(String name) { 248 if (isJB()) { 249 this.name = name; 250 } else { 251 ShadowDisplayManager.changeDisplay(realObject.getDisplayId(), 252 di -> di.name = name); 253 } 254 } 255 256 /** 257 * Changes the flags for this display. 258 * 259 * Any registered {@link android.hardware.display.DisplayManager.DisplayListener}s will be 260 * notified of the change. 261 */ setFlags(int flags)262 public void setFlags(int flags) { 263 ReflectionHelpers.setField(realObject, "mFlags", flags); 264 265 if (!isJB()) { 266 ShadowDisplayManager.changeDisplay(realObject.getDisplayId(), 267 di -> di.flags = flags); 268 } 269 } 270 271 /** 272 * Changes the width available to the application for this display. 273 * 274 * Any registered {@link android.hardware.display.DisplayManager.DisplayListener}s will be 275 * notified of the change. 276 * 277 * @param width the new width in pixels 278 */ setWidth(int width)279 public void setWidth(int width) { 280 if (isJB()) { 281 this.width = width; 282 } else { 283 ShadowDisplayManager.changeDisplay(realObject.getDisplayId(), 284 di -> di.appWidth = width); 285 } 286 } 287 288 /** 289 * Changes the height available to the application for this display. 290 * 291 * Any registered {@link android.hardware.display.DisplayManager.DisplayListener}s will be 292 * notified of the change. 293 * 294 * @param height new height in pixels 295 */ setHeight(int height)296 public void setHeight(int height) { 297 if (isJB()) { 298 this.height = height; 299 } else { 300 ShadowDisplayManager.changeDisplay(realObject.getDisplayId(), 301 di -> di.appHeight = height); 302 } 303 } 304 305 /** 306 * Changes the simulated physical width for this display. 307 * 308 * Any registered {@link android.hardware.display.DisplayManager.DisplayListener}s will be 309 * notified of the change. 310 * 311 * @param width the new width in pixels 312 */ setRealWidth(int width)313 public void setRealWidth(int width) { 314 if (isJB()) { 315 this.realWidth = width; 316 } else { 317 ShadowDisplayManager.changeDisplay(realObject.getDisplayId(), 318 di -> di.logicalWidth = width); 319 } 320 } 321 322 /** 323 * Changes the simulated physical height for this display. 324 * 325 * Any registered {@link android.hardware.display.DisplayManager.DisplayListener}s will be 326 * notified of the change. 327 * 328 * @param height the new height in pixels 329 */ setRealHeight(int height)330 public void setRealHeight(int height) { 331 if (isJB()) { 332 this.realHeight = height; 333 } else { 334 ShadowDisplayManager.changeDisplay(realObject.getDisplayId(), 335 di -> di.logicalHeight = height); 336 } 337 } 338 339 /** 340 * Changes the refresh rate for this display. 341 * 342 * Any registered {@link android.hardware.display.DisplayManager.DisplayListener}s will be 343 * notified of the change. 344 */ setRefreshRate(float refreshRate)345 public void setRefreshRate(float refreshRate) { 346 this.refreshRate = refreshRate; 347 } 348 349 /** 350 * Changes the rotation for this display. 351 * 352 * Any registered {@link android.hardware.display.DisplayManager.DisplayListener}s will be 353 * notified of the change. 354 * 355 * @param rotation one of {@link Surface#ROTATION_0}, {@link Surface#ROTATION_90}, 356 * {@link Surface#ROTATION_180}, {@link Surface#ROTATION_270} 357 358 */ setRotation(int rotation)359 public void setRotation(int rotation) { 360 if (isJB()) { 361 this.rotation = rotation; 362 } else { 363 ShadowDisplayManager.changeDisplay(realObject.getDisplayId(), 364 di -> di.rotation = rotation); 365 } 366 } 367 368 /** 369 * Changes the pixel format for this display. 370 * 371 * @deprecated This method is deprecated and will be removed in Robolectric 3.7. 372 */ 373 @Deprecated setPixelFormat(int pixelFormat)374 public void setPixelFormat(int pixelFormat) { 375 this.pixelFormat = pixelFormat; 376 } 377 378 /** 379 * Changes the simulated state for this display, such as whether it is on or off 380 * 381 * Any registered {@link android.hardware.display.DisplayManager.DisplayListener}s will be 382 * notified of the change. 383 * 384 * @param state the new state: one of {@link Display#STATE_OFF}, {@link Display#STATE_ON}, 385 * {@link Display#STATE_DOZE}, {@link Display#STATE_DOZE_SUSPEND}, or 386 * {@link Display#STATE_UNKNOWN}. 387 */ setState(int state)388 public void setState(int state) { 389 if (!isJB()) { 390 ShadowDisplayManager.changeDisplay(realObject.getDisplayId(), 391 di -> di.state = state); 392 } 393 } 394 isJB()395 private boolean isJB() { 396 return RuntimeEnvironment.getApiLevel() == JELLY_BEAN; 397 } 398 configureForJBOnly(Configuration configuration, DisplayMetrics displayMetrics)399 void configureForJBOnly(Configuration configuration, DisplayMetrics displayMetrics) { 400 int widthPx = (int) (configuration.screenWidthDp * displayMetrics.density); 401 int heightPx = (int) (configuration.screenHeightDp * displayMetrics.density); 402 403 name = "Built-in screen"; 404 displayId = 0; 405 width = widthPx; 406 height = heightPx; 407 realWidth = widthPx; 408 realHeight = heightPx; 409 densityDpi = displayMetrics.densityDpi; 410 xdpi = (float) displayMetrics.densityDpi; 411 ydpi = (float) displayMetrics.densityDpi; 412 scaledDensity = displayMetrics.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE; 413 rotation = configuration.orientation == Configuration.ORIENTATION_PORTRAIT 414 ? Surface.ROTATION_0 415 : Surface.ROTATION_90; 416 } 417 } 418