• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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