
org.robolectric.shadows.ShadowCamera Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of shadows-framework Show documentation
Show all versions of shadows-framework Show documentation
An alternative Android testing framework.
The newest version!
package org.robolectric.shadows;
import static org.robolectric.shadow.api.Shadow.newInstanceOf;
import android.hardware.Camera;
import android.view.SurfaceHolder;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.util.ArrayList;
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.annotation.Resetter;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.util.ReflectionHelpers;
import org.robolectric.util.ReflectionHelpers.ClassParameter;
@Implements(Camera.class)
public class ShadowCamera {
// These are completely arbitrary and likely outdated default parameters that have been added long
// ago.
private static final ImmutableMap DEFAULT_PARAMS =
ImmutableMap.builder()
.put("picture-size", "1280x960")
.put("preview-size", "640x480")
.put("preview-fps-range", "10,30")
.put("preview-frame-rate", "30")
.put("preview-format", "yuv420sp")
.put("picture-format-values", "yuv420sp,jpeg")
.put("preview-format-values", "yuv420sp,jpeg")
.put("picture-size-values", "320x240,640x480,800x600")
.put("preview-size-values", "320x240,640x480")
.put("preview-fps-range-values", "(15000,15000),(10000,30000)")
.put("preview-frame-rate-values", "10,15,30")
.put("exposure-compensation", "0")
.put("exposure-compensation-step", "0.5")
.put("min-exposure-compensation", "-6")
.put("max-exposure-compensation", "6")
.put("focus-mode-values", Camera.Parameters.FOCUS_MODE_AUTO)
.put("focus-mode", Camera.Parameters.FOCUS_MODE_AUTO)
.put(
"flash-mode-values",
Camera.Parameters.FLASH_MODE_AUTO
+ ","
+ Camera.Parameters.FLASH_MODE_ON
+ ","
+ Camera.Parameters.FLASH_MODE_OFF)
.put("flash-mode", Camera.Parameters.FLASH_MODE_AUTO)
.put("max-num-focus-areas", "1")
.put("max-num-metering-areas", "1")
.build();
private static int lastOpenedCameraId;
private int id;
private boolean locked = true;
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 = true;
private static final Map cameras = new HashMap<>();
private static final Map cameraParameters = new HashMap<>();
@RealObject private Camera realCamera;
@Implementation
protected static Camera open() {
return open(0);
}
@Implementation
protected static Camera open(int cameraId) {
lastOpenedCameraId = cameraId;
Camera camera = newInstanceOf(Camera.class);
ShadowCamera shadowCamera = Shadow.extract(camera);
shadowCamera.id = cameraId;
if (cameraParameters.containsKey(cameraId)) {
shadowCamera.parameters = cameraParameters.get(cameraId);
} else {
cameraParameters.put(cameraId, camera.getParameters());
}
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 (parameters == null) {
parameters =
ReflectionHelpers.callConstructor(
Camera.Parameters.class, ClassParameter.from(Camera.class, realCamera));
Joiner.MapJoiner mapJoiner = Joiner.on(";").withKeyValueSeparator("=");
parameters.unflatten(mapJoiner.join(DEFAULT_PARAMS));
}
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;
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
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);
}
@Resetter
public static void clearCameraInfo() {
cameras.clear();
cameraParameters.clear();
}
/** Shadows the Android {@code Camera.Parameters} class. */
@Implements(Camera.Parameters.class)
public static class ShadowParameters {
@SuppressWarnings("nullness:initialization.field.uninitialized") // Managed by Robolectric
@RealObject
private Camera.Parameters realParameters;
public void initSupportedPreviewSizes() {
realParameters.remove("preview-size-values");
}
public void setSupportedFocusModes(String... focusModes) {
realParameters.set("focus-mode-values", Joiner.on(",").join(focusModes));
if (focusModes.length == 0) {
realParameters.remove("focus-mode");
}
}
public void setSupportedFlashModes(String... flashModes) {
realParameters.set("flash-mode-values", Joiner.on(",").join(flashModes));
if (flashModes.length == 0) {
realParameters.remove("flash-mode");
}
}
/**
* Allows test cases to set the maximum number of focus areas. See {@link
* Camera.Parameters#getMaxNumFocusAreas}.
*/
public void setMaxNumFocusAreas(int maxNumFocusAreas) {
realParameters.set("max-num-focus-areas", maxNumFocusAreas);
}
public void addSupportedPreviewSize(int width, int height) {
List sizesStrings = new ArrayList<>();
List sizes = realParameters.getSupportedPreviewSizes();
if (sizes == null) {
sizes = ImmutableList.of();
}
for (Camera.Size size : sizes) {
sizesStrings.add(size.width + "x" + size.height);
}
sizesStrings.add(width + "x" + height);
realParameters.set("preview-size-values", Joiner.on(",").join(sizesStrings));
}
/**
* Allows test cases to set the maximum number of metering areas. See {@link
* Camera.Parameters#getMaxNumMeteringAreas}.
*/
public void setMaxNumMeteringAreas(int maxNumMeteringAreas) {
realParameters.set("max-num-metering-areas", maxNumMeteringAreas);
}
public int getPreviewWidth() {
return realParameters.getPreviewSize().width;
}
public int getPreviewHeight() {
return realParameters.getPreviewSize().height;
}
public int getPictureWidth() {
return realParameters.getPictureSize().width;
}
public int getPictureHeight() {
return realParameters.getPictureSize().height;
}
public int getRotation() {
return realParameters.getInt("rotation");
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy