All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.robolectric.shadows.ShadowCamera Maven / Gradle / Ivy

package org.robolectric.shadows;

import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
import static org.robolectric.shadow.api.Shadow.newInstanceOf;

import android.graphics.ImageFormat;
import android.hardware.Camera;
import android.os.Build;
import android.view.SurfaceHolder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.RealObject;
import org.robolectric.shadow.api.Shadow;

@Implements(Camera.class)
public class ShadowCamera {

  private static int lastOpenedCameraId;

  private int id;
  private boolean locked;
  private boolean previewing;
  private boolean released;
  private Camera.Parameters parameters;
  private Camera.PreviewCallback previewCallback;
  private List callbackBuffers = new ArrayList<>();
  private SurfaceHolder surfaceHolder;
  private int displayOrientation;
  private Camera.AutoFocusCallback autoFocusCallback;
  private boolean autoFocusing;
  private boolean shutterSoundEnabled;

  private static Map cameras = new HashMap<>();

  @RealObject private Camera realCamera;

  @Implementation
  protected void __constructor__() {
    locked = true;
    previewing = false;
    released = false;
    shutterSoundEnabled = true;
  }

  @Implementation
  protected static Camera open() {
    lastOpenedCameraId = 0;
    Camera camera = newInstanceOf(Camera.class);
    ShadowCamera shadowCamera = Shadow.extract(camera);
    shadowCamera.id = 0;
    return camera;
  }

  @Implementation
  protected static Camera open(int cameraId) {
    lastOpenedCameraId = cameraId;
    Camera camera = newInstanceOf(Camera.class);
    ShadowCamera shadowCamera = Shadow.extract(camera);
    shadowCamera.id = cameraId;
    return camera;
  }

  public static int getLastOpenedCameraId() {
    return lastOpenedCameraId;
  }

  @Implementation
  protected void unlock() {
    locked = false;
  }

  @Implementation
  protected void reconnect() {
    locked = true;
  }

  @Implementation
  protected Camera.Parameters getParameters() {
    if (null == parameters) {
      parameters = newInstanceOf(Camera.Parameters.class);
    }
    return parameters;
  }

  @Implementation
  protected void setParameters(Camera.Parameters params) {
    parameters = params;
  }

  @Implementation
  protected void setPreviewDisplay(SurfaceHolder holder) {
    surfaceHolder = holder;
  }

  @Implementation
  protected void startPreview() {
    previewing = true;
  }

  @Implementation
  protected void stopPreview() {
    previewing = false;
  }

  @Implementation
  protected void release() {
    released = true;
  }

  @Implementation
  protected void setPreviewCallback(Camera.PreviewCallback cb) {
    previewCallback = cb;
  }

  @Implementation
  protected void setOneShotPreviewCallback(Camera.PreviewCallback cb) {
    previewCallback = cb;
  }

  @Implementation
  protected void setPreviewCallbackWithBuffer(Camera.PreviewCallback cb) {
    previewCallback = cb;
  }

  /**
   * Allows test cases to invoke the preview callback, to simulate a frame of camera data.
   *
   * @param data byte buffer of simulated camera data
   */
  public void invokePreviewCallback(byte[] data) {
    if (previewCallback != null) {
      previewCallback.onPreviewFrame(data, realCamera);
    }
  }

  @Implementation
  protected void addCallbackBuffer(byte[] callbackBuffer) {
    callbackBuffers.add(callbackBuffer);
  }

  public List getAddedCallbackBuffers() {
    return Collections.unmodifiableList(callbackBuffers);
  }

  @Implementation
  protected void setDisplayOrientation(int degrees) {
    displayOrientation = degrees;
    if (cameras.containsKey(id)) {
      cameras.get(id).orientation = degrees;
    }
  }

  public int getDisplayOrientation() {
    return displayOrientation;
  }

  @Implementation
  protected void autoFocus(Camera.AutoFocusCallback callback) {
    autoFocusCallback = callback;
    autoFocusing = true;
  }

  @Implementation
  protected void cancelAutoFocus() {
    autoFocusCallback = null;
    autoFocusing = false;
  }

  public boolean hasRequestedAutoFocus() {
    return autoFocusing;
  }

  public void invokeAutoFocusCallback(boolean success, Camera camera) {
    if (autoFocusCallback == null) {
      throw new IllegalStateException(
          "cannot invoke AutoFocusCallback before autoFocus() has been called "
              + "or after cancelAutoFocus() has been called "
              + "or after the callback has been invoked.");
    }
    autoFocusCallback.onAutoFocus(success, camera);
    autoFocusCallback = null;
    autoFocusing = false;
  }

  @Implementation
  protected static void getCameraInfo(int cameraId, Camera.CameraInfo cameraInfo) {
    Camera.CameraInfo foundCam = cameras.get(cameraId);
    cameraInfo.facing = foundCam.facing;
    cameraInfo.orientation = foundCam.orientation;
    // canDisableShutterSound was added in API 17.
    if (Build.VERSION.SDK_INT >= JELLY_BEAN_MR1) {
      cameraInfo.canDisableShutterSound = foundCam.canDisableShutterSound;
    }
  }

  @Implementation
  protected static int getNumberOfCameras() {
    return cameras.size();
  }

  @Implementation
  protected void takePicture(
      Camera.ShutterCallback shutter, Camera.PictureCallback raw, Camera.PictureCallback jpeg) {
    if (shutter != null) {
      shutter.onShutter();
    }

    if (raw != null) {
      raw.onPictureTaken(new byte[0], realCamera);
    }

    if (jpeg != null) {
      jpeg.onPictureTaken(new byte[0], realCamera);
    }
  }

  @Implementation(minSdk = JELLY_BEAN_MR1)
  protected boolean enableShutterSound(boolean enabled) {
    if (!enabled && cameras.containsKey(id) && !cameras.get(id).canDisableShutterSound) {
      return false;
    }
    shutterSoundEnabled = enabled;
    return true;
  }

  /** Returns {@code true} if the default shutter sound is played when taking a picture. */
  public boolean isShutterSoundEnabled() {
    return shutterSoundEnabled;
  }

  public boolean isLocked() {
    return locked;
  }

  public boolean isPreviewing() {
    return previewing;
  }

  public boolean isReleased() {
    return released;
  }

  public SurfaceHolder getPreviewDisplay() {
    return surfaceHolder;
  }

  /**
   * Add a mock {@code Camera.CameraInfo} object to simulate the existence of one or more cameras.
   * By default, no cameras are defined.
   *
   * @param id The camera id
   * @param camInfo The CameraInfo
   */
  public static void addCameraInfo(int id, Camera.CameraInfo camInfo) {
    cameras.put(id, camInfo);
  }

  public static void clearCameraInfo() {
    cameras.clear();
  }

  /** Shadows the Android {@code Camera.Parameters} class. */
  @Implements(Camera.Parameters.class)
  public static class ShadowParameters {

    private int pictureWidth = 1280;
    private int pictureHeight = 960;
    private int previewWidth = 640;
    private int previewHeight = 480;
    private int previewFormat = ImageFormat.NV21;
    private int previewFpsMin = 10;
    private int previewFpsMax = 30;
    private int previewFps = 30;
    private int exposureCompensation = 0;
    private String flashMode;
    private String focusMode;
    private List supportedFlashModes = new ArrayList<>();
    private List supportedFocusModes = new ArrayList<>();
    private int maxNumFocusAreas;
    private List focusAreas = new ArrayList<>();
    private int maxNumMeteringAreas;
    private List meteringAreas = new ArrayList<>();
    private final Map paramsMap = new HashMap<>();
    private static List supportedPreviewSizes;

    /**
     * Explicitly initialize custom preview sizes array, to switch from default values to
     * individually added.
     */
    public void initSupportedPreviewSizes() {
      supportedPreviewSizes = new ArrayList<>();
    }

    /** Add custom preview sizes to supportedPreviewSizes. */
    public void addSupportedPreviewSize(int width, int height) {
      Camera.Size newSize = newInstanceOf(Camera.class).new Size(width, height);
      supportedPreviewSizes.add(newSize);
    }

    @Implementation
    protected Camera.Size getPictureSize() {
      Camera.Size pictureSize = newInstanceOf(Camera.class).new Size(0, 0);
      pictureSize.width = pictureWidth;
      pictureSize.height = pictureHeight;
      return pictureSize;
    }

    @Implementation
    protected int getPreviewFormat() {
      return previewFormat;
    }

    @Implementation
    protected void getPreviewFpsRange(int[] range) {
      range[0] = previewFpsMin;
      range[1] = previewFpsMax;
    }

    @Implementation
    protected int getPreviewFrameRate() {
      return previewFps;
    }

    @Implementation
    protected Camera.Size getPreviewSize() {
      Camera.Size previewSize = newInstanceOf(Camera.class).new Size(0, 0);
      previewSize.width = previewWidth;
      previewSize.height = previewHeight;
      return previewSize;
    }

    @Implementation
    protected List getSupportedPictureSizes() {
      List supportedSizes = new ArrayList<>();
      addSize(supportedSizes, 320, 240);
      addSize(supportedSizes, 640, 480);
      addSize(supportedSizes, 800, 600);
      return supportedSizes;
    }

    @Implementation
    protected List getSupportedPictureFormats() {
      List formats = new ArrayList<>();
      formats.add(ImageFormat.NV21);
      formats.add(ImageFormat.JPEG);
      return formats;
    }

    @Implementation
    protected List getSupportedPreviewFormats() {
      List formats = new ArrayList<>();
      formats.add(ImageFormat.NV21);
      formats.add(ImageFormat.JPEG);
      return formats;
    }

    @Implementation
    protected List getSupportedPreviewFpsRange() {
      List supportedRanges = new ArrayList<>();
      addRange(supportedRanges, 15000, 15000);
      addRange(supportedRanges, 10000, 30000);
      return supportedRanges;
    }

    @Implementation
    protected List getSupportedPreviewFrameRates() {
      List supportedRates = new ArrayList<>();
      supportedRates.add(10);
      supportedRates.add(15);
      supportedRates.add(30);
      return supportedRates;
    }

    @Implementation
    protected List getSupportedPreviewSizes() {
      if (supportedPreviewSizes == null) {
        initSupportedPreviewSizes();
        addSupportedPreviewSize(320, 240);
        addSupportedPreviewSize(640, 480);
      }
      return supportedPreviewSizes;
    }

    public void setSupportedFocusModes(String... focusModes) {
      supportedFocusModes = Arrays.asList(focusModes);
    }

    @Implementation
    protected List getSupportedFocusModes() {
      return supportedFocusModes;
    }

    @Implementation
    protected String getFocusMode() {
      return focusMode;
    }

    @Implementation
    protected void setFocusMode(String focusMode) {
      this.focusMode = focusMode;
    }

    /**
     * Allows test cases to set the maximum number of focus areas. See {@link
     * Camera.Parameters#getMaxNumFocusAreas}.
     */
    public void setMaxNumFocusAreas(int maxNumFocusAreas) {
      this.maxNumFocusAreas = maxNumFocusAreas;
    }

    @Implementation
    protected int getMaxNumFocusAreas() {
      return maxNumFocusAreas;
    }

    @Implementation
    protected void setFocusAreas(List focusAreas) {
      this.focusAreas = focusAreas;
    }

    @Implementation
    protected List getFocusAreas() {
      return focusAreas;
    }

    /**
     * Allows test cases to set the maximum number of metering areas. See {@link
     * Camera.Parameters#getMaxNumMeteringAreas}.
     */
    public void setMaxNumMeteringAreas(int maxNumMeteringAreas) {
      this.maxNumMeteringAreas = maxNumMeteringAreas;
    }

    @Implementation
    protected int getMaxNumMeteringAreas() {
      return maxNumMeteringAreas;
    }

    @Implementation
    protected void setMeteringAreas(List meteringAreas) {
      this.meteringAreas = meteringAreas;
    }

    @Implementation
    protected List getMeteringAreas() {
      return meteringAreas;
    }

    @Implementation
    protected void setPictureSize(int width, int height) {
      pictureWidth = width;
      pictureHeight = height;
    }

    @Implementation
    protected void setPreviewFormat(int pixel_format) {
      previewFormat = pixel_format;
    }

    @Implementation
    protected void setPreviewFpsRange(int min, int max) {
      previewFpsMin = min;
      previewFpsMax = max;
    }

    @Implementation
    protected void setPreviewFrameRate(int fps) {
      previewFps = fps;
    }

    @Implementation
    protected void setPreviewSize(int width, int height) {
      previewWidth = width;
      previewHeight = height;
    }

    @Implementation
    protected void setRecordingHint(boolean recordingHint) {
      // Do nothing - this prevents an NPE in the SDK code
    }

    @Implementation
    protected void setRotation(int rotation) {
      // Do nothing - this prevents an NPE in the SDK code
    }

    @Implementation
    protected int getMinExposureCompensation() {
      return -6;
    }

    @Implementation
    protected int getMaxExposureCompensation() {
      return 6;
    }

    @Implementation
    protected float getExposureCompensationStep() {
      return 0.5f;
    }

    @Implementation
    protected int getExposureCompensation() {
      return exposureCompensation;
    }

    @Implementation
    protected void setExposureCompensation(int compensation) {
      exposureCompensation = compensation;
    }

    public void setSupportedFlashModes(String... flashModes) {
      supportedFlashModes = Arrays.asList(flashModes);
    }

    @Implementation
    protected List getSupportedFlashModes() {
      return supportedFlashModes;
    }

    @Implementation
    protected String getFlashMode() {
      return flashMode;
    }

    @Implementation
    protected void setFlashMode(String flashMode) {
      this.flashMode = flashMode;
    }

    @Implementation
    protected void set(String key, String value) {
      paramsMap.put(key, value);
    }

    @Implementation
    protected String get(String key) {
      return paramsMap.get(key);
    }

    public int getPreviewWidth() {
      return previewWidth;
    }

    public int getPreviewHeight() {
      return previewHeight;
    }

    public int getPictureWidth() {
      return pictureWidth;
    }

    public int getPictureHeight() {
      return pictureHeight;
    }

    private void addSize(List sizes, int width, int height) {
      Camera.Size newSize = newInstanceOf(Camera.class).new Size(0, 0);
      newSize.width = width;
      newSize.height = height;
      sizes.add(newSize);
    }

    private void addRange(List ranges, int min, int max) {
      int[] range = new int[2];
      range[0] = min;
      range[1] = max;
      ranges.add(range);
    }
  }

  @Implements(Camera.Size.class)
  public static class ShadowSize {
    @RealObject private Camera.Size realCameraSize;

    @Implementation
    protected void __constructor__(Camera camera, int width, int height) {
      realCameraSize.width = width;
      realCameraSize.height = height;
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy