Home | History | Annotate | Download | only in shadows
      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    */
     35   public static Display getDefaultDisplay() {
     36     WindowManager windowManager = (WindowManager) RuntimeEnvironment.systemContext
     37         .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}
     61    * will 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
     67   public 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}
     86    * will 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
     92   public 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
    112   public 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
    125   public 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
    138   public int getPixelFormat() {
    139     return pixelFormat == null
    140         ? directlyOn(realObject, Display.class).getPixelFormat()
    141         : pixelFormat;
    142   }
    143 
    144   @Implementation(maxSdk = JELLY_BEAN)
    145   public void getSizeInternal(Point outSize, boolean doCompat) {
    146     outSize.x = width;
    147     outSize.y = height;
    148   }
    149 
    150   @Implementation(maxSdk = JELLY_BEAN)
    151   public 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)
    159   public 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    */
    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    */
    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    */
    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    */
    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
    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
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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
    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    */
    388   public void setState(int state) {
    389     if (!isJB()) {
    390       ShadowDisplayManager.changeDisplay(realObject.getDisplayId(),
    391           di -> di.state = state);
    392     }
    393   }
    394 
    395   private boolean isJB() {
    396     return RuntimeEnvironment.getApiLevel() == JELLY_BEAN;
    397   }
    398 
    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