Home | History | Annotate | Download | only in shadows
      1 package org.robolectric.shadows;
      2 
      3 import static org.robolectric.shadow.api.Shadow.newInstanceOf;
      4 
      5 import android.graphics.ImageFormat;
      6 import android.hardware.Camera;
      7 import android.view.SurfaceHolder;
      8 import java.util.ArrayList;
      9 import java.util.Arrays;
     10 import java.util.Collections;
     11 import java.util.HashMap;
     12 import java.util.List;
     13 import java.util.Map;
     14 import org.robolectric.annotation.Implementation;
     15 import org.robolectric.annotation.Implements;
     16 import org.robolectric.annotation.RealObject;
     17 import org.robolectric.shadow.api.Shadow;
     18 import org.robolectric.util.ReflectionHelpers;
     19 
     20 @Implements(Camera.class)
     21 public class ShadowCamera {
     22 
     23   private static int lastOpenedCameraId;
     24 
     25   private int id;
     26   private boolean locked;
     27   private boolean previewing;
     28   private boolean released;
     29   private Camera.Parameters parameters;
     30   private Camera.PreviewCallback previewCallback;
     31   private List<byte[]> callbackBuffers = new ArrayList<>();
     32   private SurfaceHolder surfaceHolder;
     33   private int displayOrientation;
     34   private Camera.AutoFocusCallback autoFocusCallback;
     35   private boolean autoFocusing;
     36 
     37   private static Map<Integer, Camera.CameraInfo> cameras = new HashMap<>();
     38 
     39   @RealObject private Camera realCamera;
     40 
     41   @Implementation
     42   protected void __constructor__() {
     43     locked = true;
     44     previewing = false;
     45     released = false;
     46   }
     47 
     48   @Implementation
     49   protected static Camera open() {
     50     lastOpenedCameraId = 0;
     51     Camera camera = newInstanceOf(Camera.class);
     52     ShadowCamera shadowCamera = Shadow.extract(camera);
     53     shadowCamera.id = 0;
     54     return camera;
     55   }
     56 
     57   @Implementation
     58   protected static Camera open(int cameraId) {
     59     lastOpenedCameraId = cameraId;
     60     Camera camera = newInstanceOf(Camera.class);
     61     ShadowCamera shadowCamera = Shadow.extract(camera);
     62     shadowCamera.id = cameraId;
     63     return camera;
     64   }
     65 
     66   public static int getLastOpenedCameraId() {
     67     return lastOpenedCameraId;
     68   }
     69 
     70   @Implementation
     71   protected void unlock() {
     72     locked = false;
     73   }
     74 
     75   @Implementation
     76   protected void reconnect() {
     77     locked = true;
     78   }
     79 
     80   @Implementation
     81   protected Camera.Parameters getParameters() {
     82     if (null == parameters) {
     83       parameters = newInstanceOf(Camera.Parameters.class);
     84     }
     85     return parameters;
     86   }
     87 
     88   @Implementation
     89   protected void setParameters(Camera.Parameters params) {
     90     parameters = params;
     91   }
     92 
     93   @Implementation
     94   protected void setPreviewDisplay(SurfaceHolder holder) {
     95     surfaceHolder = holder;
     96   }
     97 
     98   @Implementation
     99   protected void startPreview() {
    100     previewing = true;
    101   }
    102 
    103   @Implementation
    104   protected void stopPreview() {
    105     previewing = false;
    106   }
    107 
    108   @Implementation
    109   protected void release() {
    110     released = true;
    111   }
    112 
    113   @Implementation
    114   protected void setPreviewCallback(Camera.PreviewCallback cb) {
    115     previewCallback = cb;
    116   }
    117 
    118   @Implementation
    119   protected void setOneShotPreviewCallback(Camera.PreviewCallback cb) {
    120     previewCallback = cb;
    121   }
    122 
    123   @Implementation
    124   protected void setPreviewCallbackWithBuffer(Camera.PreviewCallback cb) {
    125     previewCallback = cb;
    126   }
    127 
    128   /**
    129    * Allows test cases to invoke the preview callback, to simulate a frame of camera data.
    130    *
    131    * @param data byte buffer of simulated camera data
    132    */
    133   public void invokePreviewCallback(byte[] data) {
    134     if (previewCallback != null) {
    135       previewCallback.onPreviewFrame(data, realCamera);
    136     }
    137   }
    138 
    139   @Implementation
    140   protected void addCallbackBuffer(byte[] callbackBuffer) {
    141     callbackBuffers.add(callbackBuffer);
    142   }
    143 
    144   public List<byte[]> getAddedCallbackBuffers() {
    145     return Collections.unmodifiableList(callbackBuffers);
    146   }
    147 
    148   @Implementation
    149   protected void setDisplayOrientation(int degrees) {
    150     displayOrientation = degrees;
    151     if (cameras.containsKey(id)) {
    152       cameras.get(id).orientation = degrees;
    153     }
    154   }
    155 
    156   public int getDisplayOrientation() {
    157     return displayOrientation;
    158   }
    159 
    160   @Implementation
    161   protected void autoFocus(Camera.AutoFocusCallback callback) {
    162     autoFocusCallback = callback;
    163     autoFocusing = true;
    164   }
    165 
    166   @Implementation
    167   protected void cancelAutoFocus() {
    168     autoFocusCallback = null;
    169     autoFocusing = false;
    170   }
    171 
    172   public boolean hasRequestedAutoFocus() {
    173     return autoFocusing;
    174   }
    175 
    176   public void invokeAutoFocusCallback(boolean success, Camera camera) {
    177     if (autoFocusCallback == null) {
    178       throw new IllegalStateException(
    179           "cannot invoke AutoFocusCallback before autoFocus() has been called "
    180               + "or after cancelAutoFocus() has been called "
    181               + "or after the callback has been invoked.");
    182     }
    183     autoFocusCallback.onAutoFocus(success, camera);
    184     autoFocusCallback = null;
    185     autoFocusing = false;
    186   }
    187 
    188   @Implementation
    189   protected static void getCameraInfo(int cameraId, Camera.CameraInfo cameraInfo) {
    190     Camera.CameraInfo foundCam = cameras.get(cameraId);
    191     cameraInfo.facing = foundCam.facing;
    192     cameraInfo.orientation = foundCam.orientation;
    193   }
    194 
    195   @Implementation
    196   protected static int getNumberOfCameras() {
    197     return cameras.size();
    198   }
    199 
    200   @Implementation
    201   protected void takePicture(
    202       Camera.ShutterCallback shutter, Camera.PictureCallback raw, Camera.PictureCallback jpeg) {
    203     if (shutter != null) {
    204       shutter.onShutter();
    205     }
    206 
    207     if (raw != null) {
    208       raw.onPictureTaken(new byte[0], realCamera);
    209     }
    210 
    211     if (jpeg != null) {
    212       jpeg.onPictureTaken(new byte[0], realCamera);
    213     }
    214   }
    215 
    216   public boolean isLocked() {
    217     return locked;
    218   }
    219 
    220   public boolean isPreviewing() {
    221     return previewing;
    222   }
    223 
    224   public boolean isReleased() {
    225     return released;
    226   }
    227 
    228   public SurfaceHolder getPreviewDisplay() {
    229     return surfaceHolder;
    230   }
    231 
    232   /**
    233    * Add a mock {@code Camera.CameraInfo} object to simulate the existence of one or more cameras.
    234    * By default, no cameras are defined.
    235    *
    236    * @param id The camera id
    237    * @param camInfo The CameraInfo
    238    */
    239   public static void addCameraInfo(int id, Camera.CameraInfo camInfo) {
    240     cameras.put(id, camInfo);
    241   }
    242 
    243   public static void clearCameraInfo() {
    244     cameras.clear();
    245   }
    246 
    247   /** Shadows the Android {@code Camera.Parameters} class. */
    248   @Implements(Camera.Parameters.class)
    249   public static class ShadowParameters {
    250 
    251     private int pictureWidth = 1280;
    252     private int pictureHeight = 960;
    253     private int previewWidth = 640;
    254     private int previewHeight = 480;
    255     private int previewFormat = ImageFormat.NV21;
    256     private int previewFpsMin = 10;
    257     private int previewFpsMax = 30;
    258     private int previewFps = 30;
    259     private int exposureCompensation = 0;
    260     private String flashMode;
    261     private String focusMode;
    262     private List<String> supportedFlashModes = new ArrayList<>();
    263     private List<String> supportedFocusModes = new ArrayList<>();
    264     private static List<Camera.Size> supportedPreviewSizes;
    265 
    266     /**
    267      * Explicitly initialize custom preview sizes array, to switch from default values to
    268      * individually added.
    269      */
    270     public void initSupportedPreviewSizes() {
    271       supportedPreviewSizes = new ArrayList<>();
    272     }
    273 
    274     /** Add custom preview sizes to supportedPreviewSizes. */
    275     public void addSupportedPreviewSize(int width, int height) {
    276       Camera.Size newSize = ReflectionHelpers.newInstance(Camera.class).new Size(width, height);
    277       supportedPreviewSizes.add(newSize);
    278     }
    279 
    280     @Implementation
    281     protected Camera.Size getPictureSize() {
    282       Camera.Size pictureSize = newInstanceOf(Camera.class).new Size(0, 0);
    283       pictureSize.width = pictureWidth;
    284       pictureSize.height = pictureHeight;
    285       return pictureSize;
    286     }
    287 
    288     @Implementation
    289     protected int getPreviewFormat() {
    290       return previewFormat;
    291     }
    292 
    293     @Implementation
    294     protected void getPreviewFpsRange(int[] range) {
    295       range[0] = previewFpsMin;
    296       range[1] = previewFpsMax;
    297     }
    298 
    299     @Implementation
    300     protected int getPreviewFrameRate() {
    301       return previewFps;
    302     }
    303 
    304     @Implementation
    305     protected Camera.Size getPreviewSize() {
    306       Camera.Size previewSize = newInstanceOf(Camera.class).new Size(0, 0);
    307       previewSize.width = previewWidth;
    308       previewSize.height = previewHeight;
    309       return previewSize;
    310     }
    311 
    312     @Implementation
    313     protected List<Camera.Size> getSupportedPictureSizes() {
    314       List<Camera.Size> supportedSizes = new ArrayList<>();
    315       addSize(supportedSizes, 320, 240);
    316       addSize(supportedSizes, 640, 480);
    317       addSize(supportedSizes, 800, 600);
    318       return supportedSizes;
    319     }
    320 
    321     @Implementation
    322     protected List<Integer> getSupportedPictureFormats() {
    323       List<Integer> formats = new ArrayList<>();
    324       formats.add(ImageFormat.NV21);
    325       formats.add(ImageFormat.JPEG);
    326       return formats;
    327     }
    328 
    329     @Implementation
    330     protected List<Integer> getSupportedPreviewFormats() {
    331       List<Integer> formats = new ArrayList<>();
    332       formats.add(ImageFormat.NV21);
    333       formats.add(ImageFormat.JPEG);
    334       return formats;
    335     }
    336 
    337     @Implementation
    338     protected List<int[]> getSupportedPreviewFpsRange() {
    339       List<int[]> supportedRanges = new ArrayList<>();
    340       addRange(supportedRanges, 15000, 15000);
    341       addRange(supportedRanges, 10000, 30000);
    342       return supportedRanges;
    343     }
    344 
    345     @Implementation
    346     protected List<Integer> getSupportedPreviewFrameRates() {
    347       List<Integer> supportedRates = new ArrayList<>();
    348       supportedRates.add(10);
    349       supportedRates.add(15);
    350       supportedRates.add(30);
    351       return supportedRates;
    352     }
    353 
    354     @Implementation
    355     protected List<Camera.Size> getSupportedPreviewSizes() {
    356       if (supportedPreviewSizes == null) {
    357         initSupportedPreviewSizes();
    358         addSupportedPreviewSize(320, 240);
    359         addSupportedPreviewSize(640, 480);
    360       }
    361       return supportedPreviewSizes;
    362     }
    363 
    364     public void setSupportedFocusModes(String... focusModes) {
    365       supportedFocusModes = Arrays.asList(focusModes);
    366     }
    367 
    368     @Implementation
    369     protected List<String> getSupportedFocusModes() {
    370       return supportedFocusModes;
    371     }
    372 
    373     @Implementation
    374     protected String getFocusMode() {
    375       return focusMode;
    376     }
    377 
    378     @Implementation
    379     protected void setFocusMode(String focusMode) {
    380       this.focusMode = focusMode;
    381     }
    382 
    383     @Implementation
    384     protected void setPictureSize(int width, int height) {
    385       pictureWidth = width;
    386       pictureHeight = height;
    387     }
    388 
    389     @Implementation
    390     protected void setPreviewFormat(int pixel_format) {
    391       previewFormat = pixel_format;
    392     }
    393 
    394     @Implementation
    395     protected void setPreviewFpsRange(int min, int max) {
    396       previewFpsMin = min;
    397       previewFpsMax = max;
    398     }
    399 
    400     @Implementation
    401     protected void setPreviewFrameRate(int fps) {
    402       previewFps = fps;
    403     }
    404 
    405     @Implementation
    406     protected void setPreviewSize(int width, int height) {
    407       previewWidth = width;
    408       previewHeight = height;
    409     }
    410 
    411     @Implementation
    412     protected void setRecordingHint(boolean recordingHint) {
    413       // Do nothing - this prevents an NPE in the SDK code
    414     }
    415 
    416     @Implementation
    417     protected void setRotation(int rotation) {
    418       // Do nothing - this prevents an NPE in the SDK code
    419     }
    420 
    421     @Implementation
    422     protected int getMinExposureCompensation() {
    423       return -6;
    424     }
    425 
    426     @Implementation
    427     protected int getMaxExposureCompensation() {
    428       return 6;
    429     }
    430 
    431     @Implementation
    432     protected float getExposureCompensationStep() {
    433       return 0.5f;
    434     }
    435 
    436     @Implementation
    437     protected int getExposureCompensation() {
    438       return exposureCompensation;
    439     }
    440 
    441     @Implementation
    442     protected void setExposureCompensation(int compensation) {
    443       exposureCompensation = compensation;
    444     }
    445 
    446     public void setSupportedFlashModes(String... flashModes) {
    447       supportedFlashModes = Arrays.asList(flashModes);
    448     }
    449 
    450     @Implementation
    451     protected List<String> getSupportedFlashModes() {
    452       return supportedFlashModes;
    453     }
    454 
    455     @Implementation
    456     protected String getFlashMode() {
    457       return flashMode;
    458     }
    459 
    460     @Implementation
    461     protected void setFlashMode(String flashMode) {
    462       this.flashMode = flashMode;
    463     }
    464 
    465     public int getPreviewWidth() {
    466       return previewWidth;
    467     }
    468 
    469     public int getPreviewHeight() {
    470       return previewHeight;
    471     }
    472 
    473     public int getPictureWidth() {
    474       return pictureWidth;
    475     }
    476 
    477     public int getPictureHeight() {
    478       return pictureHeight;
    479     }
    480 
    481     private void addSize(List<Camera.Size> sizes, int width, int height) {
    482       Camera.Size newSize = newInstanceOf(Camera.class).new Size(0, 0);
    483       newSize.width = width;
    484       newSize.height = height;
    485       sizes.add(newSize);
    486     }
    487 
    488     private void addRange(List<int[]> ranges, int min, int max) {
    489       int[] range = new int[2];
    490       range[0] = min;
    491       range[1] = max;
    492       ranges.add(range);
    493     }
    494   }
    495 
    496   @Implements(Camera.Size.class)
    497   public static class ShadowSize {
    498     @RealObject private Camera.Size realCameraSize;
    499 
    500     @Implementation
    501     protected void __constructor__(Camera camera, int width, int height) {
    502       realCameraSize.width = width;
    503       realCameraSize.height = height;
    504     }
    505   }
    506 }
    507