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 __constructor__()42 protected void __constructor__() { 43 locked = true; 44 previewing = false; 45 released = false; 46 } 47 48 @Implementation open()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 open(int cameraId)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 getLastOpenedCameraId()66 public static int getLastOpenedCameraId() { 67 return lastOpenedCameraId; 68 } 69 70 @Implementation unlock()71 protected void unlock() { 72 locked = false; 73 } 74 75 @Implementation reconnect()76 protected void reconnect() { 77 locked = true; 78 } 79 80 @Implementation getParameters()81 protected Camera.Parameters getParameters() { 82 if (null == parameters) { 83 parameters = newInstanceOf(Camera.Parameters.class); 84 } 85 return parameters; 86 } 87 88 @Implementation setParameters(Camera.Parameters params)89 protected void setParameters(Camera.Parameters params) { 90 parameters = params; 91 } 92 93 @Implementation setPreviewDisplay(SurfaceHolder holder)94 protected void setPreviewDisplay(SurfaceHolder holder) { 95 surfaceHolder = holder; 96 } 97 98 @Implementation startPreview()99 protected void startPreview() { 100 previewing = true; 101 } 102 103 @Implementation stopPreview()104 protected void stopPreview() { 105 previewing = false; 106 } 107 108 @Implementation release()109 protected void release() { 110 released = true; 111 } 112 113 @Implementation setPreviewCallback(Camera.PreviewCallback cb)114 protected void setPreviewCallback(Camera.PreviewCallback cb) { 115 previewCallback = cb; 116 } 117 118 @Implementation setOneShotPreviewCallback(Camera.PreviewCallback cb)119 protected void setOneShotPreviewCallback(Camera.PreviewCallback cb) { 120 previewCallback = cb; 121 } 122 123 @Implementation setPreviewCallbackWithBuffer(Camera.PreviewCallback cb)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 */ invokePreviewCallback(byte[] data)133 public void invokePreviewCallback(byte[] data) { 134 if (previewCallback != null) { 135 previewCallback.onPreviewFrame(data, realCamera); 136 } 137 } 138 139 @Implementation addCallbackBuffer(byte[] callbackBuffer)140 protected void addCallbackBuffer(byte[] callbackBuffer) { 141 callbackBuffers.add(callbackBuffer); 142 } 143 getAddedCallbackBuffers()144 public List<byte[]> getAddedCallbackBuffers() { 145 return Collections.unmodifiableList(callbackBuffers); 146 } 147 148 @Implementation setDisplayOrientation(int degrees)149 protected void setDisplayOrientation(int degrees) { 150 displayOrientation = degrees; 151 if (cameras.containsKey(id)) { 152 cameras.get(id).orientation = degrees; 153 } 154 } 155 getDisplayOrientation()156 public int getDisplayOrientation() { 157 return displayOrientation; 158 } 159 160 @Implementation autoFocus(Camera.AutoFocusCallback callback)161 protected void autoFocus(Camera.AutoFocusCallback callback) { 162 autoFocusCallback = callback; 163 autoFocusing = true; 164 } 165 166 @Implementation cancelAutoFocus()167 protected void cancelAutoFocus() { 168 autoFocusCallback = null; 169 autoFocusing = false; 170 } 171 hasRequestedAutoFocus()172 public boolean hasRequestedAutoFocus() { 173 return autoFocusing; 174 } 175 invokeAutoFocusCallback(boolean success, Camera camera)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 getCameraInfo(int cameraId, Camera.CameraInfo cameraInfo)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 getNumberOfCameras()196 protected static int getNumberOfCameras() { 197 return cameras.size(); 198 } 199 200 @Implementation takePicture( Camera.ShutterCallback shutter, Camera.PictureCallback raw, Camera.PictureCallback jpeg)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 isLocked()216 public boolean isLocked() { 217 return locked; 218 } 219 isPreviewing()220 public boolean isPreviewing() { 221 return previewing; 222 } 223 isReleased()224 public boolean isReleased() { 225 return released; 226 } 227 getPreviewDisplay()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 */ addCameraInfo(int id, Camera.CameraInfo camInfo)239 public static void addCameraInfo(int id, Camera.CameraInfo camInfo) { 240 cameras.put(id, camInfo); 241 } 242 clearCameraInfo()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 */ initSupportedPreviewSizes()270 public void initSupportedPreviewSizes() { 271 supportedPreviewSizes = new ArrayList<>(); 272 } 273 274 /** Add custom preview sizes to supportedPreviewSizes. */ addSupportedPreviewSize(int width, int height)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 getPictureSize()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 getPreviewFormat()289 protected int getPreviewFormat() { 290 return previewFormat; 291 } 292 293 @Implementation getPreviewFpsRange(int[] range)294 protected void getPreviewFpsRange(int[] range) { 295 range[0] = previewFpsMin; 296 range[1] = previewFpsMax; 297 } 298 299 @Implementation getPreviewFrameRate()300 protected int getPreviewFrameRate() { 301 return previewFps; 302 } 303 304 @Implementation getPreviewSize()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 getSupportedPictureSizes()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 getSupportedPictureFormats()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 getSupportedPreviewFormats()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 getSupportedPreviewFpsRange()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 getSupportedPreviewFrameRates()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 getSupportedPreviewSizes()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 setSupportedFocusModes(String... focusModes)364 public void setSupportedFocusModes(String... focusModes) { 365 supportedFocusModes = Arrays.asList(focusModes); 366 } 367 368 @Implementation getSupportedFocusModes()369 protected List<String> getSupportedFocusModes() { 370 return supportedFocusModes; 371 } 372 373 @Implementation getFocusMode()374 protected String getFocusMode() { 375 return focusMode; 376 } 377 378 @Implementation setFocusMode(String focusMode)379 protected void setFocusMode(String focusMode) { 380 this.focusMode = focusMode; 381 } 382 383 @Implementation setPictureSize(int width, int height)384 protected void setPictureSize(int width, int height) { 385 pictureWidth = width; 386 pictureHeight = height; 387 } 388 389 @Implementation setPreviewFormat(int pixel_format)390 protected void setPreviewFormat(int pixel_format) { 391 previewFormat = pixel_format; 392 } 393 394 @Implementation setPreviewFpsRange(int min, int max)395 protected void setPreviewFpsRange(int min, int max) { 396 previewFpsMin = min; 397 previewFpsMax = max; 398 } 399 400 @Implementation setPreviewFrameRate(int fps)401 protected void setPreviewFrameRate(int fps) { 402 previewFps = fps; 403 } 404 405 @Implementation setPreviewSize(int width, int height)406 protected void setPreviewSize(int width, int height) { 407 previewWidth = width; 408 previewHeight = height; 409 } 410 411 @Implementation setRecordingHint(boolean recordingHint)412 protected void setRecordingHint(boolean recordingHint) { 413 // Do nothing - this prevents an NPE in the SDK code 414 } 415 416 @Implementation setRotation(int rotation)417 protected void setRotation(int rotation) { 418 // Do nothing - this prevents an NPE in the SDK code 419 } 420 421 @Implementation getMinExposureCompensation()422 protected int getMinExposureCompensation() { 423 return -6; 424 } 425 426 @Implementation getMaxExposureCompensation()427 protected int getMaxExposureCompensation() { 428 return 6; 429 } 430 431 @Implementation getExposureCompensationStep()432 protected float getExposureCompensationStep() { 433 return 0.5f; 434 } 435 436 @Implementation getExposureCompensation()437 protected int getExposureCompensation() { 438 return exposureCompensation; 439 } 440 441 @Implementation setExposureCompensation(int compensation)442 protected void setExposureCompensation(int compensation) { 443 exposureCompensation = compensation; 444 } 445 setSupportedFlashModes(String... flashModes)446 public void setSupportedFlashModes(String... flashModes) { 447 supportedFlashModes = Arrays.asList(flashModes); 448 } 449 450 @Implementation getSupportedFlashModes()451 protected List<String> getSupportedFlashModes() { 452 return supportedFlashModes; 453 } 454 455 @Implementation getFlashMode()456 protected String getFlashMode() { 457 return flashMode; 458 } 459 460 @Implementation setFlashMode(String flashMode)461 protected void setFlashMode(String flashMode) { 462 this.flashMode = flashMode; 463 } 464 getPreviewWidth()465 public int getPreviewWidth() { 466 return previewWidth; 467 } 468 getPreviewHeight()469 public int getPreviewHeight() { 470 return previewHeight; 471 } 472 getPictureWidth()473 public int getPictureWidth() { 474 return pictureWidth; 475 } 476 getPictureHeight()477 public int getPictureHeight() { 478 return pictureHeight; 479 } 480 addSize(List<Camera.Size> sizes, int width, int height)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 addRange(List<int[]> ranges, int min, int max)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 __constructor__(Camera camera, int width, int height)501 protected void __constructor__(Camera camera, int width, int height) { 502 realCameraSize.width = width; 503 realCameraSize.height = height; 504 } 505 } 506 } 507