Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 42339f800c | |||
| 8d5b16c4bd | |||
| add2692f37 | |||
| 43029afab3 | |||
| 358bd1b7f3 | |||
| eb0746a780 | |||
| 58e5e93382 | |||
| 6d22e7c6ae | |||
| 0bf4a3df8f | |||
| 57729b9dd8 | |||
| f3d67273a2 | |||
| 0b0ded639b | |||
| 898769b1a4 | |||
| 0ce7f5e687 | |||
| c2b8755f5f | |||
| 628781b706 | |||
| 3dfa659723 | |||
| 37ec1addde | |||
| 6614785f7b | |||
| e89a06e4e0 | |||
| 4f46df5bfd | |||
| e5c60fb31f | |||
| 88c8281f27 | |||
| 4249bc5b30 | |||
| 541bd2d740 | |||
| b25faddf57 | |||
| 82501be283 | |||
| fcf1432c43 | |||
| 79e8b58167 | |||
| 6ad0e214da | |||
| 17f897e8a1 | |||
| 582174aeb1 | |||
| 023743135e | |||
| ba031c1437 | |||
| a951882fe9 | |||
| 2d0cc63637 | |||
| 5b953c1df2 | |||
| f25ea5d39f | |||
| aab760bab8 |
@@ -2,6 +2,9 @@
|
||||
|
||||

|
||||
|
||||
|
||||

|
||||
|
||||
Camera API in Android is hard. Having 2 different API for new and old Camera does not make things any easier. But fret not, that is your lucky day! After several years of working with Camera we came up with Fotoapparat.
|
||||
|
||||
What it provides:
|
||||
@@ -46,7 +49,9 @@ Configure `Fotoapparat` instance
|
||||
Fotoapparat
|
||||
.with(context)
|
||||
.into(cameraView) // view which will draw the camera preview
|
||||
.previewScaleType(ScaleType.CENTER_CROP) // we want the preview to fill the view
|
||||
.photoSize(biggestSize()) // we want to have the biggest photo possible
|
||||
.previewScaleType(centerCropped()) // we want the preview to fill the view
|
||||
.lensPosition(back()) // we want back camera
|
||||
.focusMode(firstAvailable( // (optional) use the first focus mode which is supported by device
|
||||
continuousFocus(),
|
||||
@@ -128,16 +133,20 @@ repositories {
|
||||
maven { url 'https://jitpack.io' }
|
||||
}
|
||||
|
||||
compile 'io.fotoapparat.fotoapparat:library:1.0.2'
|
||||
compile 'io.fotoapparat.fotoapparat:library:1.2.0'
|
||||
```
|
||||
|
||||
Camera permission will be automatically added to your `AndroidManifest.xml`. Do not forget to request this permission on Marshmallow and higher.
|
||||
|
||||
## Face detection
|
||||
|
||||
Optionally, you can check out our other library which adds face detection capabilities - [FaceDetector](https://github.com/Fotoapparat/FaceDetector).
|
||||
|
||||
## Credits
|
||||
|
||||
We want to say thanks to [Mark Murphy](https://github.com/commonsguy) for the awesome job he did with [CWAC-Camera](https://github.com/commonsguy/cwac-camera). We were using his library for a couple of years and now we feel that Fotoapparat is a next step in the right direction.
|
||||
|
||||
We also want to say many thanks to [Leander Lenzing](http://leanderlenzing.com/) for the amazing icon. Don't forget to follow his work in [dribbble](https://dribbble.com/leanderlenzing).
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -19,6 +19,10 @@ android {
|
||||
minifyEnabled false
|
||||
}
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests.returnDefaultValues = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -5,6 +5,8 @@ import android.content.Context;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import io.fotoapparat.error.Callbacks;
|
||||
import io.fotoapparat.error.CameraErrorCallback;
|
||||
import io.fotoapparat.hardware.CameraDevice;
|
||||
import io.fotoapparat.hardware.orientation.OrientationSensor;
|
||||
import io.fotoapparat.hardware.orientation.RotationListener;
|
||||
@@ -13,14 +15,16 @@ import io.fotoapparat.parameter.provider.CapabilitiesProvider;
|
||||
import io.fotoapparat.parameter.provider.InitialParametersProvider;
|
||||
import io.fotoapparat.parameter.provider.InitialParametersValidator;
|
||||
import io.fotoapparat.result.CapabilitiesResult;
|
||||
import io.fotoapparat.result.FocusResult;
|
||||
import io.fotoapparat.result.PendingResult;
|
||||
import io.fotoapparat.result.PhotoResult;
|
||||
import io.fotoapparat.routine.AutoFocusRoutine;
|
||||
import io.fotoapparat.routine.CheckAvailabilityRoutine;
|
||||
import io.fotoapparat.routine.ConfigurePreviewStreamRoutine;
|
||||
import io.fotoapparat.routine.StartCameraRoutine;
|
||||
import io.fotoapparat.routine.StopCameraRoutine;
|
||||
import io.fotoapparat.routine.TakePictureRoutine;
|
||||
import io.fotoapparat.routine.UpdateOrientationRoutine;
|
||||
import io.fotoapparat.routine.focus.AutoFocusRoutine;
|
||||
import io.fotoapparat.routine.picture.TakePictureRoutine;
|
||||
|
||||
/**
|
||||
* Camera. Takes pictures.
|
||||
@@ -70,8 +74,12 @@ public class Fotoapparat {
|
||||
}
|
||||
|
||||
static Fotoapparat create(FotoapparatBuilder builder) {
|
||||
CameraErrorCallback cameraErrorCallback = Callbacks.onMainThread(
|
||||
builder.cameraErrorCallback
|
||||
);
|
||||
|
||||
CameraDevice cameraDevice = builder.cameraProvider.get(builder.logger);
|
||||
|
||||
ScreenOrientationProvider screenOrientationProvider = new ScreenOrientationProvider(builder.context);
|
||||
RotationListener rotationListener = new RotationListener(builder.context);
|
||||
|
||||
@@ -88,9 +96,11 @@ public class Fotoapparat {
|
||||
StartCameraRoutine startCameraRoutine = new StartCameraRoutine(
|
||||
cameraDevice,
|
||||
builder.renderer,
|
||||
builder.scaleType,
|
||||
builder.lensPositionSelector,
|
||||
screenOrientationProvider,
|
||||
initialParametersProvider
|
||||
initialParametersProvider,
|
||||
cameraErrorCallback
|
||||
);
|
||||
|
||||
StopCameraRoutine stopCameraRoutine = new StopCameraRoutine(cameraDevice);
|
||||
@@ -121,7 +131,10 @@ public class Fotoapparat {
|
||||
SERIAL_EXECUTOR
|
||||
);
|
||||
|
||||
AutoFocusRoutine autoFocusRoutine = new AutoFocusRoutine(cameraDevice);
|
||||
AutoFocusRoutine autoFocusRoutine = new AutoFocusRoutine(
|
||||
cameraDevice,
|
||||
SERIAL_EXECUTOR
|
||||
);
|
||||
|
||||
CheckAvailabilityRoutine checkAvailabilityRoutine = new CheckAvailabilityRoutine(
|
||||
cameraDevice,
|
||||
@@ -175,15 +188,22 @@ public class Fotoapparat {
|
||||
* Performs auto focus. If it is not available or not enabled, does nothing.
|
||||
*/
|
||||
public Fotoapparat autoFocus() {
|
||||
ensureStarted();
|
||||
|
||||
executor.execute(
|
||||
autoFocusRoutine
|
||||
);
|
||||
focus();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to focus the camera asynchronously.
|
||||
*
|
||||
* @return the pending result of focus operation which will deliver result asynchronously.
|
||||
*/
|
||||
public PendingResult<FocusResult> focus() {
|
||||
ensureStarted();
|
||||
|
||||
return autoFocusRoutine.autoFocus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts camera.
|
||||
*
|
||||
|
||||
@@ -3,6 +3,7 @@ package io.fotoapparat;
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import io.fotoapparat.error.CameraErrorCallback;
|
||||
import io.fotoapparat.hardware.CameraDevice;
|
||||
import io.fotoapparat.hardware.provider.CameraProvider;
|
||||
import io.fotoapparat.log.Logger;
|
||||
@@ -10,6 +11,7 @@ import io.fotoapparat.log.Loggers;
|
||||
import io.fotoapparat.parameter.Flash;
|
||||
import io.fotoapparat.parameter.FocusMode;
|
||||
import io.fotoapparat.parameter.LensPosition;
|
||||
import io.fotoapparat.parameter.ScaleType;
|
||||
import io.fotoapparat.parameter.Size;
|
||||
import io.fotoapparat.parameter.selector.FlashSelectors;
|
||||
import io.fotoapparat.parameter.selector.SelectorFunction;
|
||||
@@ -50,10 +52,14 @@ public class FotoapparatBuilder {
|
||||
);
|
||||
SelectorFunction<Flash> flashSelector = FlashSelectors.off();
|
||||
|
||||
ScaleType scaleType = ScaleType.CENTER_CROP;
|
||||
|
||||
FrameProcessor frameProcessor = null;
|
||||
|
||||
Logger logger = Loggers.none();
|
||||
|
||||
CameraErrorCallback cameraErrorCallback = CameraErrorCallback.NULL;
|
||||
|
||||
FotoapparatBuilder(@NonNull Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
@@ -61,7 +67,7 @@ public class FotoapparatBuilder {
|
||||
/**
|
||||
* @param cameraProvider decides which {@link CameraDevice} to use.
|
||||
*/
|
||||
public FotoapparatBuilder cameraProvider(CameraProvider cameraProvider) {
|
||||
public FotoapparatBuilder cameraProvider(@NonNull CameraProvider cameraProvider) {
|
||||
this.cameraProvider = cameraProvider;
|
||||
return this;
|
||||
}
|
||||
@@ -69,7 +75,7 @@ public class FotoapparatBuilder {
|
||||
/**
|
||||
* @param selector selects size of the photo (in pixels) from list of available sizes.
|
||||
*/
|
||||
public FotoapparatBuilder photoSize(SelectorFunction<Size> selector) {
|
||||
public FotoapparatBuilder photoSize(@NonNull SelectorFunction<Size> selector) {
|
||||
photoSizeSelector = selector;
|
||||
return this;
|
||||
}
|
||||
@@ -77,15 +83,23 @@ public class FotoapparatBuilder {
|
||||
/**
|
||||
* @param selector selects size of preview stream (in pixels) from list of available sizes.
|
||||
*/
|
||||
public FotoapparatBuilder previewSize(SelectorFunction<Size> selector) {
|
||||
public FotoapparatBuilder previewSize(@NonNull SelectorFunction<Size> selector) {
|
||||
previewSizeSelector = selector;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param scaleType of preview inside the view.
|
||||
*/
|
||||
public FotoapparatBuilder previewScaleType(ScaleType scaleType) {
|
||||
this.scaleType = scaleType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param selector selects focus mode from list of available modes.
|
||||
*/
|
||||
public FotoapparatBuilder focusMode(SelectorFunction<FocusMode> selector) {
|
||||
public FotoapparatBuilder focusMode(@NonNull SelectorFunction<FocusMode> selector) {
|
||||
focusModeSelector = selector;
|
||||
return this;
|
||||
}
|
||||
@@ -93,7 +107,7 @@ public class FotoapparatBuilder {
|
||||
/**
|
||||
* @param selector selects flash mode from list of available modes.
|
||||
*/
|
||||
public FotoapparatBuilder flash(SelectorFunction<Flash> selector) {
|
||||
public FotoapparatBuilder flash(@NonNull SelectorFunction<Flash> selector) {
|
||||
flashSelector = selector;
|
||||
return this;
|
||||
}
|
||||
@@ -101,7 +115,7 @@ public class FotoapparatBuilder {
|
||||
/**
|
||||
* @param selector camera sensor position from list of available positions.
|
||||
*/
|
||||
public FotoapparatBuilder lensPosition(SelectorFunction<LensPosition> selector) {
|
||||
public FotoapparatBuilder lensPosition(@NonNull SelectorFunction<LensPosition> selector) {
|
||||
lensPositionSelector = selector;
|
||||
return this;
|
||||
}
|
||||
@@ -110,7 +124,7 @@ public class FotoapparatBuilder {
|
||||
* @param frameProcessor receives preview frames for processing.
|
||||
* @see FrameProcessor
|
||||
*/
|
||||
public FotoapparatBuilder frameProcessor(FrameProcessor frameProcessor) {
|
||||
public FotoapparatBuilder frameProcessor(@NonNull FrameProcessor frameProcessor) {
|
||||
this.frameProcessor = frameProcessor;
|
||||
return this;
|
||||
}
|
||||
@@ -119,16 +133,25 @@ public class FotoapparatBuilder {
|
||||
* @param logger logger which will print logs. No logger is set by default.
|
||||
* @see Loggers
|
||||
*/
|
||||
public FotoapparatBuilder logger(Logger logger) {
|
||||
public FotoapparatBuilder logger(@NonNull Logger logger) {
|
||||
this.logger = logger;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callback which will be notified when camera error happens in Fotoapparat.
|
||||
* @see CameraErrorCallback
|
||||
*/
|
||||
public FotoapparatBuilder cameraErrorCallback(@NonNull CameraErrorCallback callback) {
|
||||
this.cameraErrorCallback = callback;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param renderer view which will draw the stream from the camera.
|
||||
* @see CameraView
|
||||
*/
|
||||
public FotoapparatBuilder into(CameraRenderer renderer) {
|
||||
public FotoapparatBuilder into(@NonNull CameraRenderer renderer) {
|
||||
this.renderer = renderer;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package io.fotoapparat.error;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import io.fotoapparat.hardware.CameraException;
|
||||
|
||||
/**
|
||||
* Factory methods for callbacks.
|
||||
*/
|
||||
public class Callbacks {
|
||||
|
||||
private static final Handler MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper());
|
||||
|
||||
/**
|
||||
* @return CameraErrorCallback which will always move execution to the main thread.
|
||||
*/
|
||||
public static CameraErrorCallback onMainThread(final CameraErrorCallback original) {
|
||||
return new CameraErrorCallback() {
|
||||
@Override
|
||||
public void onError(final CameraException e) {
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
original.onError(e);
|
||||
} else {
|
||||
MAIN_THREAD_HANDLER.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
original.onError(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package io.fotoapparat.error;
|
||||
|
||||
import io.fotoapparat.hardware.CameraException;
|
||||
|
||||
/**
|
||||
* Notified when an camera error happens within Fotoapparat.
|
||||
* <p>
|
||||
* This method is always called from the main thread.
|
||||
*/
|
||||
public interface CameraErrorCallback {
|
||||
|
||||
/**
|
||||
* No-op implementation of {@link CameraErrorCallback}.
|
||||
*/
|
||||
CameraErrorCallback NULL = new CameraErrorCallback() {
|
||||
@Override
|
||||
public void onError(CameraException e) {
|
||||
// Do nothing
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Notified when a camera error happens within Fotoapparat.
|
||||
*/
|
||||
void onError(CameraException e);
|
||||
|
||||
}
|
||||
@@ -4,6 +4,7 @@ package io.fotoapparat.hardware;
|
||||
* A generic camera exception.
|
||||
*/
|
||||
public class CameraException extends RuntimeException {
|
||||
|
||||
public CameraException(Exception e) {
|
||||
super(e);
|
||||
}
|
||||
@@ -11,4 +12,9 @@ public class CameraException extends RuntimeException {
|
||||
public CameraException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
public CameraException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,15 +8,21 @@ import android.view.TextureView;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import io.fotoapparat.hardware.CameraDevice;
|
||||
import io.fotoapparat.hardware.CameraException;
|
||||
import io.fotoapparat.hardware.Capabilities;
|
||||
import io.fotoapparat.hardware.operators.ParametersOperator;
|
||||
import io.fotoapparat.hardware.orientation.OrientationUtils;
|
||||
import io.fotoapparat.hardware.provider.AvailableLensPositionsProvider;
|
||||
import io.fotoapparat.hardware.provider.V1AvailableLensPositionProvider;
|
||||
import io.fotoapparat.hardware.v1.capabilities.CapabilitiesFactory;
|
||||
import io.fotoapparat.hardware.v1.parameters.SplitParametersOperator;
|
||||
import io.fotoapparat.hardware.v1.parameters.SupressExceptionsParametersOperator;
|
||||
import io.fotoapparat.hardware.v1.parameters.SwitchOnFailureParametersOperator;
|
||||
import io.fotoapparat.hardware.v1.parameters.UnsafeParametersOperator;
|
||||
import io.fotoapparat.lens.FocusResult;
|
||||
import io.fotoapparat.log.Logger;
|
||||
import io.fotoapparat.parameter.LensPosition;
|
||||
@@ -32,13 +38,15 @@ import io.fotoapparat.preview.PreviewStream;
|
||||
@SuppressWarnings("deprecation")
|
||||
public class Camera1 implements CameraDevice {
|
||||
|
||||
private static final long AUTOFOCUS_TIMEOUT_SECONDS = 3L;
|
||||
|
||||
private final CapabilitiesFactory capabilitiesFactory;
|
||||
private final ParametersConverter parametersConverter;
|
||||
private final AvailableLensPositionsProvider availableLensPositionsProvider;
|
||||
private final Logger logger;
|
||||
|
||||
private Camera camera;
|
||||
private int cameraId;
|
||||
private int cameraId = -1;
|
||||
private PreviewStream1 previewStream;
|
||||
|
||||
private Throwable lastStacktrace;
|
||||
@@ -51,6 +59,10 @@ public class Camera1 implements CameraDevice {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
private static void throwOnFailSetDisplaySurface(Object displaySurface, IOException e) {
|
||||
throw new CameraException("Unable to set display surface: " + displaySurface, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open(LensPosition lensPosition) {
|
||||
recordMethod();
|
||||
@@ -60,7 +72,7 @@ public class Camera1 implements CameraDevice {
|
||||
camera = Camera.open(cameraId);
|
||||
previewStream = new PreviewStream1(camera);
|
||||
} catch (RuntimeException e) {
|
||||
throw new CameraException(e);
|
||||
throwOnFailedToOpenCamera(lensPosition, e);
|
||||
}
|
||||
|
||||
camera.setErrorCallback(new Camera.ErrorCallback() {
|
||||
@@ -70,11 +82,18 @@ public class Camera1 implements CameraDevice {
|
||||
lastStacktrace.printStackTrace();
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Camera error code: " + error);
|
||||
logger.log("Camera error code: " + error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void throwOnFailedToOpenCamera(LensPosition lensPosition, RuntimeException e) {
|
||||
throw new CameraException(
|
||||
"Failed to open camera with lens position: " + lensPosition + " and id: " + cameraId,
|
||||
e
|
||||
);
|
||||
}
|
||||
|
||||
private int cameraIdForLensPosition(LensPosition lensPosition) {
|
||||
int numberOfCameras = Camera.getNumberOfCameras();
|
||||
|
||||
@@ -104,7 +123,7 @@ public class Camera1 implements CameraDevice {
|
||||
public void close() {
|
||||
recordMethod();
|
||||
|
||||
if (camera != null) {
|
||||
if (isCameraOpened()) {
|
||||
camera.release();
|
||||
}
|
||||
}
|
||||
@@ -113,14 +132,27 @@ public class Camera1 implements CameraDevice {
|
||||
public void startPreview() {
|
||||
recordMethod();
|
||||
|
||||
camera.startPreview();
|
||||
try {
|
||||
camera.startPreview();
|
||||
} catch (RuntimeException e) {
|
||||
throwOnFailStartPreview(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void throwOnFailStartPreview(RuntimeException e) {
|
||||
throw new CameraException(
|
||||
"Failed to start preview for camera devices: " + cameraId,
|
||||
e
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopPreview() {
|
||||
recordMethod();
|
||||
|
||||
camera.stopPreview();
|
||||
if (isCameraOpened()) {
|
||||
camera.stopPreview();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -130,7 +162,7 @@ public class Camera1 implements CameraDevice {
|
||||
try {
|
||||
trySetDisplaySurface(displaySurface);
|
||||
} catch (IOException e) {
|
||||
throw new CameraException(e);
|
||||
throwOnFailSetDisplaySurface(displaySurface, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,6 +170,10 @@ public class Camera1 implements CameraDevice {
|
||||
public void setDisplayOrientation(int degrees) {
|
||||
recordMethod();
|
||||
|
||||
if (!isCameraOpened()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Camera.CameraInfo info = getCameraInfo(cameraId);
|
||||
|
||||
imageRotation = computeImageOrientation(degrees, info);
|
||||
@@ -170,12 +206,27 @@ public class Camera1 implements CameraDevice {
|
||||
public void updateParameters(Parameters parameters) {
|
||||
recordMethod();
|
||||
|
||||
Camera.Parameters cameraParameters = parametersConverter.convert(
|
||||
parameters,
|
||||
camera.getParameters()
|
||||
parametersOperator().updateParameters(parameters);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private SwitchOnFailureParametersOperator parametersOperator() {
|
||||
ParametersOperator unsafeParametersOperator = new UnsafeParametersOperator(
|
||||
camera,
|
||||
parametersConverter
|
||||
);
|
||||
|
||||
camera.setParameters(cameraParameters);
|
||||
ParametersOperator fallbackOperator = new SplitParametersOperator(
|
||||
new SupressExceptionsParametersOperator(
|
||||
unsafeParametersOperator,
|
||||
logger
|
||||
)
|
||||
);
|
||||
|
||||
return new SwitchOnFailureParametersOperator(
|
||||
unsafeParametersOperator,
|
||||
fallbackOperator
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -232,25 +283,32 @@ public class Camera1 implements CameraDevice {
|
||||
@Override
|
||||
public PreviewStream getPreviewStream() {
|
||||
recordMethod();
|
||||
ensurePreviewStreamAvailable();
|
||||
|
||||
return previewStream;
|
||||
return isPreviewStreamInitialized()
|
||||
? previewStream
|
||||
: PreviewStream.NULL;
|
||||
}
|
||||
|
||||
private void ensurePreviewStreamAvailable() {
|
||||
if (previewStream == null) {
|
||||
throw new IllegalStateException("Preview stream is null. Make sure camera is opened.");
|
||||
}
|
||||
private boolean isPreviewStreamInitialized() {
|
||||
return previewStream != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RendererParameters getRendererParameters() {
|
||||
recordMethod();
|
||||
|
||||
return new RendererParameters(
|
||||
RendererParameters rendererParameters = new RendererParameters(
|
||||
previewSize(),
|
||||
imageRotation
|
||||
);
|
||||
|
||||
logRendererParameters(rendererParameters);
|
||||
|
||||
return rendererParameters;
|
||||
}
|
||||
|
||||
private void logRendererParameters(RendererParameters rendererParameters) {
|
||||
logger.log("Renderer parameters are: " + rendererParameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -258,15 +316,22 @@ public class Camera1 implements CameraDevice {
|
||||
recordMethod();
|
||||
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
camera.autoFocus(new Camera.AutoFocusCallback() {
|
||||
@Override
|
||||
public void onAutoFocus(boolean success, Camera camera) {
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
latch.await();
|
||||
camera.autoFocus(new Camera.AutoFocusCallback() {
|
||||
@Override
|
||||
public void onAutoFocus(boolean success, Camera camera) {
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
logFailedAutoFocus(e);
|
||||
|
||||
return FocusResult.none();
|
||||
}
|
||||
|
||||
try {
|
||||
latch.await(AUTOFOCUS_TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
// Do nothing
|
||||
}
|
||||
@@ -274,9 +339,13 @@ public class Camera1 implements CameraDevice {
|
||||
return FocusResult.successNoMeasurement();
|
||||
}
|
||||
|
||||
private void logFailedAutoFocus(Exception e) {
|
||||
logger.log("Failed to perform autofocus using device " + cameraId + " e: " + e.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void measureExposure() {
|
||||
// TODO: 30.04.17
|
||||
// Do nothing. Not supported by Camera1.
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -300,6 +369,10 @@ public class Camera1 implements CameraDevice {
|
||||
return info;
|
||||
}
|
||||
|
||||
private boolean isCameraOpened() {
|
||||
return camera != null;
|
||||
}
|
||||
|
||||
private void recordMethod() {
|
||||
lastStacktrace = new Exception();
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ import android.support.annotation.NonNull;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import io.fotoapparat.parameter.Size;
|
||||
import io.fotoapparat.preview.Frame;
|
||||
@@ -18,6 +20,8 @@ import io.fotoapparat.preview.PreviewStream;
|
||||
@SuppressWarnings("deprecation")
|
||||
public class PreviewStream1 implements PreviewStream {
|
||||
|
||||
private static Executor FRAME_PROCESSORS_EXECUTOR = Executors.newSingleThreadExecutor();
|
||||
|
||||
private final Camera camera;
|
||||
|
||||
private final Set<FrameProcessor> frameProcessors = new LinkedHashSet<>();
|
||||
@@ -86,6 +90,15 @@ public class PreviewStream1 implements PreviewStream {
|
||||
camera.setPreviewCallbackWithBuffer(new Camera.PreviewCallback() {
|
||||
@Override
|
||||
public void onPreviewFrame(byte[] data, Camera camera) {
|
||||
dispatchFrameOnBackgroundThread(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void dispatchFrameOnBackgroundThread(final byte[] data) {
|
||||
FRAME_PROCESSORS_EXECUTOR.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (frameProcessors) {
|
||||
dispatchFrame(data);
|
||||
}
|
||||
|
||||
@@ -84,7 +84,6 @@ public class CapabilitiesFactory {
|
||||
}
|
||||
|
||||
result.add(FocusMode.FIXED);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package io.fotoapparat.hardware.v1.parameters;
|
||||
|
||||
import io.fotoapparat.hardware.operators.ParametersOperator;
|
||||
import io.fotoapparat.parameter.Parameters;
|
||||
|
||||
/**
|
||||
* Instead of updating all parameters at once, updates each parameter one by one.
|
||||
*/
|
||||
public class SplitParametersOperator implements ParametersOperator {
|
||||
|
||||
private final ParametersOperator wrapped;
|
||||
|
||||
public SplitParametersOperator(ParametersOperator wrapped) {
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateParameters(Parameters parameters) {
|
||||
for (Parameters.Type type : parameters.storedTypes()) {
|
||||
wrapped.updateParameters(
|
||||
newParameters(type, parameters.getValue(type))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private Parameters newParameters(Parameters.Type type, Object value) {
|
||||
Parameters parameters = new Parameters();
|
||||
parameters.putValue(type, value);
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package io.fotoapparat.hardware.v1.parameters;
|
||||
|
||||
import io.fotoapparat.hardware.operators.ParametersOperator;
|
||||
import io.fotoapparat.log.Logger;
|
||||
import io.fotoapparat.parameter.Parameters;
|
||||
|
||||
/**
|
||||
* Tries to update parameters. If that fails, suppresses the exception and writes relevant
|
||||
* information to log.
|
||||
*/
|
||||
public class SupressExceptionsParametersOperator implements ParametersOperator {
|
||||
|
||||
private final ParametersOperator wrapped;
|
||||
private final Logger logger;
|
||||
|
||||
public SupressExceptionsParametersOperator(ParametersOperator wrapped,
|
||||
Logger logger) {
|
||||
this.wrapped = wrapped;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateParameters(Parameters parameters) {
|
||||
try {
|
||||
wrapped.updateParameters(parameters);
|
||||
} catch (Exception e) {
|
||||
logger.log("Unable to set parameters: " + parameters);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package io.fotoapparat.hardware.v1.parameters;
|
||||
|
||||
import io.fotoapparat.hardware.operators.ParametersOperator;
|
||||
import io.fotoapparat.parameter.Parameters;
|
||||
|
||||
/**
|
||||
* Tries to execute first operator. If that fails, tries to execute the second one.
|
||||
*/
|
||||
public class SwitchOnFailureParametersOperator implements ParametersOperator {
|
||||
|
||||
private final ParametersOperator first;
|
||||
private final ParametersOperator second;
|
||||
|
||||
public SwitchOnFailureParametersOperator(ParametersOperator first,
|
||||
ParametersOperator second) {
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateParameters(Parameters parameters) {
|
||||
try {
|
||||
first.updateParameters(parameters);
|
||||
} catch (Exception e) {
|
||||
second.updateParameters(parameters);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package io.fotoapparat.hardware.v1.parameters;
|
||||
|
||||
import android.hardware.Camera;
|
||||
|
||||
import io.fotoapparat.hardware.operators.ParametersOperator;
|
||||
import io.fotoapparat.hardware.v1.ParametersConverter;
|
||||
import io.fotoapparat.parameter.Parameters;
|
||||
|
||||
/**
|
||||
* Updates parameters of camera, possibly throwing a {@link RuntimeException} if something goes
|
||||
* wrong. We can't really know why camera might reject parameters, so it should be expected.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class UnsafeParametersOperator implements ParametersOperator {
|
||||
|
||||
private final Camera camera;
|
||||
private final ParametersConverter parametersConverter;
|
||||
|
||||
public UnsafeParametersOperator(Camera camera,
|
||||
ParametersConverter parametersConverter) {
|
||||
this.camera = camera;
|
||||
this.parametersConverter = parametersConverter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateParameters(Parameters parameters) {
|
||||
Camera.Parameters cameraParameters = parametersConverter.convert(
|
||||
parameters,
|
||||
camera.getParameters()
|
||||
);
|
||||
|
||||
camera.setParameters(cameraParameters);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,78 +23,78 @@ import static io.fotoapparat.hardware.v2.parameters.converters.FlashConverter.ex
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
public class CapabilitiesFactory implements CapabilitiesOperator {
|
||||
|
||||
private final CameraConnection cameraConnection;
|
||||
private final CameraConnection cameraConnection;
|
||||
|
||||
public CapabilitiesFactory(CameraConnection cameraConnection) {
|
||||
this.cameraConnection = cameraConnection;
|
||||
}
|
||||
public CapabilitiesFactory(CameraConnection cameraConnection) {
|
||||
this.cameraConnection = cameraConnection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Capabilities getCapabilities() {
|
||||
return new Capabilities(
|
||||
availableJpegSizes(),
|
||||
availablePreviewSizes(),
|
||||
availableFocusModes(),
|
||||
availableFlashModes()
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public Capabilities getCapabilities() {
|
||||
return new Capabilities(
|
||||
availableJpegSizes(),
|
||||
availablePreviewSizes(),
|
||||
availableFocusModes(),
|
||||
availableFlashModes()
|
||||
);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private Set<Size> availableJpegSizes() {
|
||||
return characteristics().getJpegOutputSizes();
|
||||
}
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private Set<Size> availableJpegSizes() {
|
||||
return characteristics().getJpegOutputSizes();
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private Set<Size> availablePreviewSizes() {
|
||||
HashSet<Size> filteredOutputSizes = new HashSet<>();
|
||||
for (Size outputSize : characteristics().getSurfaceOutputSizes()) {
|
||||
if (outputSize.width <= PreviewSizeInfo.MAX_PREVIEW_WIDTH && outputSize.height <= PreviewSizeInfo.MAX_PREVIEW_HEIGHT) {
|
||||
filteredOutputSizes.add(outputSize);
|
||||
}
|
||||
}
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private Set<Size> availablePreviewSizes() {
|
||||
HashSet<Size> filteredOutputSizes = new HashSet<>();
|
||||
for (Size outputSize : characteristics().getSurfaceOutputSizes()) {
|
||||
if (outputSize.width <= PreviewSizeInfo.MAX_PREVIEW_WIDTH && outputSize.height <= PreviewSizeInfo.MAX_PREVIEW_HEIGHT) {
|
||||
filteredOutputSizes.add(outputSize);
|
||||
}
|
||||
}
|
||||
|
||||
return filteredOutputSizes;
|
||||
}
|
||||
return filteredOutputSizes;
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private Set<FocusMode> availableFocusModes() {
|
||||
Set<FocusMode> focusModes = new HashSet<>();
|
||||
for (int afMode : characteristics().autoFocusModes()) {
|
||||
focusModes.add(FocusConverter.afModeToFocus(afMode));
|
||||
}
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private Set<FocusMode> availableFocusModes() {
|
||||
Set<FocusMode> focusModes = new HashSet<>();
|
||||
for (int afMode : characteristics().autoFocusModes()) {
|
||||
focusModes.add(FocusConverter.afModeToFocus(afMode));
|
||||
}
|
||||
|
||||
return focusModes;
|
||||
}
|
||||
return focusModes;
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private Set<Flash> availableFlashModes() {
|
||||
if (characteristics().isFlashAvailable()) {
|
||||
return availableFlashUnitModes();
|
||||
}
|
||||
return Collections.singleton(Flash.OFF);
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private Set<Flash> availableFlashModes() {
|
||||
if (characteristics().isFlashAvailable()) {
|
||||
return availableFlashUnitModes();
|
||||
}
|
||||
return Collections.singleton(Flash.OFF);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private Set<Flash> availableFlashUnitModes() {
|
||||
Set<Flash> flashes = new HashSet<>();
|
||||
flashes.add(Flash.OFF);
|
||||
flashes.add(Flash.TORCH);
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private Set<Flash> availableFlashUnitModes() {
|
||||
Set<Flash> flashes = new HashSet<>();
|
||||
flashes.add(Flash.OFF);
|
||||
flashes.add(Flash.TORCH);
|
||||
|
||||
int[] autoExposureModes = characteristics().autoExposureModes();
|
||||
int[] autoExposureModes = characteristics().autoExposureModes();
|
||||
|
||||
for (int autoExposureMode : autoExposureModes) {
|
||||
Flash flash = exposureModeToFlash(autoExposureMode);
|
||||
if (flash != null) {
|
||||
flashes.add(flash);
|
||||
}
|
||||
}
|
||||
for (int autoExposureMode : autoExposureModes) {
|
||||
Flash flash = exposureModeToFlash(autoExposureMode);
|
||||
if (flash != null) {
|
||||
flashes.add(flash);
|
||||
}
|
||||
}
|
||||
|
||||
return flashes;
|
||||
}
|
||||
return flashes;
|
||||
}
|
||||
|
||||
private Characteristics characteristics() {
|
||||
return cameraConnection.getCharacteristics();
|
||||
}
|
||||
private Characteristics characteristics() {
|
||||
return cameraConnection.getCharacteristics();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
package io.fotoapparat.hardware.v2.surface;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.os.Build;
|
||||
|
||||
import io.fotoapparat.parameter.Size;
|
||||
|
||||
/**
|
||||
* Sets the preview {@link Size} on a {@link SurfaceTexture}.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
class SetTextureBufferSizeTask implements Runnable {
|
||||
|
||||
private final SurfaceTexture surfaceTexture;
|
||||
|
||||
@@ -64,6 +64,28 @@ public class Parameters {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Parameters that = (Parameters) o;
|
||||
|
||||
return values.equals(that.values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return values.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Parameters{" +
|
||||
"values=" + values +
|
||||
'}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of values which can be stored in {@link Parameters}.
|
||||
*/
|
||||
|
||||
@@ -33,7 +33,6 @@ public class RendererParameters {
|
||||
RendererParameters that = (RendererParameters) o;
|
||||
|
||||
return frameRotation == that.frameRotation && (previewSize != null ? previewSize.equals(that.previewSize) : that.previewSize == null);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -42,4 +41,12 @@ public class RendererParameters {
|
||||
result = 31 * result + frameRotation;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RendererParameters{" +
|
||||
"previewSize=" + previewSize +
|
||||
", frameRotation=" + frameRotation +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package io.fotoapparat.parameter;
|
||||
|
||||
/**
|
||||
* The scale type of the preview relatively to the view.
|
||||
*/
|
||||
public enum ScaleType {
|
||||
|
||||
/**
|
||||
* The preview will be scaled so as its one dimensions will be equal and the other one equal or
|
||||
* larger than the corresponding dimension of the view
|
||||
*/
|
||||
CENTER_CROP,
|
||||
|
||||
/**
|
||||
* The preview will be scaled so as its one dimensions will be equal and the other one equal or
|
||||
* smaller than the corresponding dimension of the view
|
||||
*/
|
||||
CENTER_INSIDE
|
||||
|
||||
}
|
||||
@@ -7,6 +7,31 @@ import android.support.annotation.NonNull;
|
||||
*/
|
||||
public interface PreviewStream {
|
||||
|
||||
/**
|
||||
* Null-object for {@link PreviewStream}.
|
||||
*/
|
||||
PreviewStream NULL = new PreviewStream() {
|
||||
@Override
|
||||
public void addFrameToBuffer() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addProcessor(@NonNull FrameProcessor processor) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeProcessor(@NonNull FrameProcessor processor) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
// Do nothing
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds new frame to buffer.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package io.fotoapparat.result;
|
||||
|
||||
/**
|
||||
* Result of trying to focus the camera.
|
||||
*/
|
||||
public enum FocusResult {
|
||||
|
||||
/**
|
||||
* Camera is focused successfully.
|
||||
*/
|
||||
FOCUSED,
|
||||
|
||||
/**
|
||||
* Camera is unable to focus for some reason.
|
||||
*/
|
||||
UNABLE_TO_FOCUS
|
||||
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package io.fotoapparat.routine;
|
||||
|
||||
import io.fotoapparat.hardware.CameraDevice;
|
||||
|
||||
/**
|
||||
* Performs auto focus.
|
||||
*/
|
||||
public class AutoFocusRoutine implements Runnable {
|
||||
|
||||
private final CameraDevice cameraDevice;
|
||||
|
||||
public AutoFocusRoutine(CameraDevice cameraDevice) {
|
||||
this.cameraDevice = cameraDevice;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
cameraDevice.autoFocus();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
package io.fotoapparat.routine;
|
||||
|
||||
import io.fotoapparat.error.CameraErrorCallback;
|
||||
import io.fotoapparat.hardware.CameraDevice;
|
||||
import io.fotoapparat.hardware.CameraException;
|
||||
import io.fotoapparat.hardware.orientation.ScreenOrientationProvider;
|
||||
import io.fotoapparat.parameter.LensPosition;
|
||||
import io.fotoapparat.parameter.ScaleType;
|
||||
import io.fotoapparat.parameter.provider.InitialParametersProvider;
|
||||
import io.fotoapparat.parameter.selector.SelectorFunction;
|
||||
import io.fotoapparat.view.CameraRenderer;
|
||||
@@ -12,38 +15,53 @@ import io.fotoapparat.view.CameraRenderer;
|
||||
*/
|
||||
public class StartCameraRoutine implements Runnable {
|
||||
|
||||
private final CameraDevice cameraDevice;
|
||||
private final CameraRenderer cameraRenderer;
|
||||
private final SelectorFunction<LensPosition> lensPositionSelector;
|
||||
private final ScreenOrientationProvider screenOrientationProvider;
|
||||
private final InitialParametersProvider initialParametersProvider;
|
||||
private final CameraDevice cameraDevice;
|
||||
private final CameraRenderer cameraRenderer;
|
||||
private final ScaleType scaleType;
|
||||
private final SelectorFunction<LensPosition> lensPositionSelector;
|
||||
private final ScreenOrientationProvider screenOrientationProvider;
|
||||
private final InitialParametersProvider initialParametersProvider;
|
||||
private final CameraErrorCallback cameraErrorCallback;
|
||||
|
||||
public StartCameraRoutine(CameraDevice cameraDevice,
|
||||
CameraRenderer cameraRenderer,
|
||||
SelectorFunction<LensPosition> lensPositionSelector,
|
||||
ScreenOrientationProvider screenOrientationProvider,
|
||||
InitialParametersProvider initialParametersProvider) {
|
||||
this.cameraDevice = cameraDevice;
|
||||
this.cameraRenderer = cameraRenderer;
|
||||
this.lensPositionSelector = lensPositionSelector;
|
||||
this.screenOrientationProvider = screenOrientationProvider;
|
||||
this.initialParametersProvider = initialParametersProvider;
|
||||
}
|
||||
public StartCameraRoutine(CameraDevice cameraDevice,
|
||||
CameraRenderer cameraRenderer,
|
||||
ScaleType scaleType,
|
||||
SelectorFunction<LensPosition> lensPositionSelector,
|
||||
ScreenOrientationProvider screenOrientationProvider,
|
||||
InitialParametersProvider initialParametersProvider,
|
||||
CameraErrorCallback cameraErrorCallback) {
|
||||
this.cameraDevice = cameraDevice;
|
||||
this.cameraRenderer = cameraRenderer;
|
||||
this.scaleType = scaleType;
|
||||
this.lensPositionSelector = lensPositionSelector;
|
||||
this.screenOrientationProvider = screenOrientationProvider;
|
||||
this.initialParametersProvider = initialParametersProvider;
|
||||
this.cameraErrorCallback = cameraErrorCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
LensPosition lensPosition = lensPositionSelector.select(
|
||||
cameraDevice.getAvailableLensPositions()
|
||||
);
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
tryToStartCamera();
|
||||
} catch (CameraException e) {
|
||||
cameraErrorCallback.onError(e);
|
||||
}
|
||||
}
|
||||
|
||||
cameraDevice.open(lensPosition);
|
||||
cameraDevice.updateParameters(
|
||||
initialParametersProvider.initialParameters()
|
||||
);
|
||||
cameraDevice.setDisplayOrientation(
|
||||
screenOrientationProvider.getScreenRotation()
|
||||
);
|
||||
cameraRenderer.attachCamera(cameraDevice);
|
||||
cameraDevice.startPreview();
|
||||
}
|
||||
private void tryToStartCamera() {
|
||||
LensPosition lensPosition = lensPositionSelector.select(
|
||||
cameraDevice.getAvailableLensPositions()
|
||||
);
|
||||
|
||||
cameraDevice.open(lensPosition);
|
||||
cameraDevice.updateParameters(
|
||||
initialParametersProvider.initialParameters()
|
||||
);
|
||||
cameraDevice.setDisplayOrientation(
|
||||
screenOrientationProvider.getScreenRotation()
|
||||
);
|
||||
cameraRenderer.setScaleType(scaleType);
|
||||
cameraRenderer.attachCamera(cameraDevice);
|
||||
cameraDevice.startPreview();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
package io.fotoapparat.routine;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
import io.fotoapparat.hardware.CameraDevice;
|
||||
import io.fotoapparat.lens.FocusResult;
|
||||
import io.fotoapparat.photo.Photo;
|
||||
|
||||
/**
|
||||
* Takes photo and returns result as {@link Photo}.
|
||||
*/
|
||||
class TakePictureTask extends FutureTask<Photo> {
|
||||
|
||||
TakePictureTask(final CameraDevice cameraDevice) {
|
||||
super(new Callable<Photo>() {
|
||||
@Override
|
||||
public Photo call() throws Exception {
|
||||
|
||||
int focusAttempts = 0;
|
||||
FocusResult focusResult = FocusResult.none();
|
||||
|
||||
while (focusAttempts < 3 && !focusResult.succeeded) {
|
||||
focusResult = cameraDevice.autoFocus();
|
||||
focusAttempts++;
|
||||
}
|
||||
|
||||
if (focusResult.needsExposureMeasurement) {
|
||||
cameraDevice.measureExposure();
|
||||
}
|
||||
|
||||
Photo photo = cameraDevice.takePicture();
|
||||
|
||||
cameraDevice.startPreview();
|
||||
|
||||
return photo;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package io.fotoapparat.routine.focus;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import io.fotoapparat.hardware.CameraDevice;
|
||||
import io.fotoapparat.result.FocusResult;
|
||||
import io.fotoapparat.result.PendingResult;
|
||||
|
||||
/**
|
||||
* Performs auto focus.
|
||||
*/
|
||||
public class AutoFocusRoutine {
|
||||
|
||||
private final CameraDevice cameraDevice;
|
||||
private final Executor cameraExecutor;
|
||||
|
||||
public AutoFocusRoutine(CameraDevice cameraDevice,
|
||||
Executor cameraExecutor) {
|
||||
this.cameraDevice = cameraDevice;
|
||||
this.cameraExecutor = cameraExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform auto focus asynchronously.
|
||||
*/
|
||||
public PendingResult<FocusResult> autoFocus() {
|
||||
AutoFocusTask autoFocusTask = new AutoFocusTask(cameraDevice);
|
||||
cameraExecutor.execute(autoFocusTask);
|
||||
|
||||
return PendingResult.fromFuture(autoFocusTask);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package io.fotoapparat.routine.focus;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
import io.fotoapparat.hardware.CameraDevice;
|
||||
import io.fotoapparat.result.FocusResult;
|
||||
|
||||
/**
|
||||
* Tries to perform auto focus and returns result as {@link FocusResult}.
|
||||
*/
|
||||
public class AutoFocusTask extends FutureTask<FocusResult> {
|
||||
|
||||
public AutoFocusTask(final CameraDevice cameraDevice) {
|
||||
super(new Callable<FocusResult>() {
|
||||
@Override
|
||||
public FocusResult call() throws Exception {
|
||||
return performFocus(cameraDevice);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static FocusResult performFocus(CameraDevice cameraDevice) {
|
||||
return cameraDevice.autoFocus().succeeded
|
||||
? FocusResult.FOCUSED
|
||||
: FocusResult.UNABLE_TO_FOCUS;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package io.fotoapparat.routine;
|
||||
package io.fotoapparat.routine.picture;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package io.fotoapparat.routine.picture;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
import io.fotoapparat.hardware.CameraDevice;
|
||||
import io.fotoapparat.hardware.CameraException;
|
||||
import io.fotoapparat.lens.FocusResult;
|
||||
import io.fotoapparat.photo.Photo;
|
||||
|
||||
/**
|
||||
* Takes photo and returns result as {@link Photo}.
|
||||
*/
|
||||
class TakePictureTask extends FutureTask<Photo> {
|
||||
|
||||
private static final int MAX_FOCUS_ATTEMPTS = 3;
|
||||
|
||||
TakePictureTask(final CameraDevice cameraDevice) {
|
||||
super(new Callable<Photo>() {
|
||||
@Override
|
||||
public Photo call() throws Exception {
|
||||
adjustCameraForBestShot(cameraDevice);
|
||||
|
||||
Photo photo = cameraDevice.takePicture();
|
||||
|
||||
startPreviewSafe(cameraDevice);
|
||||
|
||||
return photo;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void adjustCameraForBestShot(CameraDevice cameraDevice) {
|
||||
FocusResult focusResult = autoFocus(cameraDevice);
|
||||
|
||||
if (focusResult.needsExposureMeasurement) {
|
||||
cameraDevice.measureExposure();
|
||||
}
|
||||
}
|
||||
|
||||
private static FocusResult autoFocus(CameraDevice cameraDevice) {
|
||||
int focusAttempts = 0;
|
||||
FocusResult focusResult = FocusResult.none();
|
||||
|
||||
while (focusAttempts < MAX_FOCUS_ATTEMPTS && !focusResult.succeeded) {
|
||||
focusResult = cameraDevice.autoFocus();
|
||||
focusAttempts++;
|
||||
}
|
||||
|
||||
return focusResult;
|
||||
}
|
||||
|
||||
private static void startPreviewSafe(CameraDevice cameraDevice) {
|
||||
try {
|
||||
cameraDevice.startPreview();
|
||||
} catch (CameraException e) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +1,19 @@
|
||||
package io.fotoapparat.view;
|
||||
|
||||
import io.fotoapparat.hardware.CameraDevice;
|
||||
import io.fotoapparat.parameter.ScaleType;
|
||||
|
||||
/**
|
||||
* Renders the stream from {@link io.fotoapparat.Fotoapparat}.
|
||||
*/
|
||||
public interface CameraRenderer {
|
||||
|
||||
/**
|
||||
* Sets the scale type of the preview to the renderer. This method will be called from camera
|
||||
* thread, so it is safe to perform blocking operations here.
|
||||
*/
|
||||
void setScaleType(ScaleType scaleType);
|
||||
|
||||
/**
|
||||
* Attaches renderer to camera, so that it will display the preview when camera is started. This
|
||||
* method will be called from camera thread, so it is safe to perform blocking operations here.
|
||||
|
||||
@@ -11,6 +11,7 @@ import android.util.AttributeSet;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import io.fotoapparat.hardware.CameraDevice;
|
||||
import io.fotoapparat.parameter.ScaleType;
|
||||
|
||||
/**
|
||||
* Displays stream from camera.
|
||||
@@ -55,6 +56,11 @@ public class CameraView extends FrameLayout implements CameraRenderer {
|
||||
addView(rendererView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScaleType(ScaleType scaleType) {
|
||||
rendererView.setScaleType(scaleType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attachCamera(CameraDevice camera) {
|
||||
rendererView.attachCamera(camera);
|
||||
|
||||
@@ -16,6 +16,7 @@ import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import io.fotoapparat.hardware.CameraDevice;
|
||||
import io.fotoapparat.parameter.RendererParameters;
|
||||
import io.fotoapparat.parameter.ScaleType;
|
||||
import io.fotoapparat.parameter.Size;
|
||||
|
||||
/**
|
||||
@@ -29,6 +30,7 @@ class TextureRendererView extends FrameLayout implements CameraRenderer {
|
||||
private TextureView textureView;
|
||||
|
||||
private Size previewSize = null;
|
||||
private ScaleType scaleType;
|
||||
|
||||
public TextureRendererView(@NonNull Context context) {
|
||||
super(context);
|
||||
@@ -74,6 +76,11 @@ class TextureRendererView extends FrameLayout implements CameraRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScaleType(ScaleType scaleType) {
|
||||
this.scaleType = scaleType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attachCamera(CameraDevice camera) {
|
||||
awaitSurfaceTexture();
|
||||
@@ -82,7 +89,7 @@ class TextureRendererView extends FrameLayout implements CameraRenderer {
|
||||
camera.setDisplaySurface(textureView);
|
||||
}
|
||||
|
||||
private void updateLayout(CameraDevice camera) {
|
||||
private void updateLayout(final CameraDevice camera) {
|
||||
final Size previewSize = toPreviewSize(
|
||||
camera.getRendererParameters()
|
||||
);
|
||||
@@ -117,11 +124,39 @@ class TextureRendererView extends FrameLayout implements CameraRenderer {
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
if (previewSize == null) {
|
||||
if (previewSize == null || scaleType == null) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
return;
|
||||
}
|
||||
|
||||
if (scaleType == ScaleType.CENTER_INSIDE) {
|
||||
centerInside(previewSize);
|
||||
} else {
|
||||
centerCrop(previewSize);
|
||||
}
|
||||
}
|
||||
|
||||
private void centerInside(Size previewSize) {
|
||||
final float scale = Math.min(
|
||||
getMeasuredWidth() / (float) previewSize.width,
|
||||
getMeasuredHeight() / (float) previewSize.height
|
||||
);
|
||||
|
||||
final int width = (int) (previewSize.width * scale);
|
||||
final int height = (int) (previewSize.height * scale);
|
||||
|
||||
final int extraX = Math.max(0, getMeasuredWidth() - width);
|
||||
final int extraY = Math.max(0, getMeasuredHeight() - height);
|
||||
|
||||
getChildAt(0).layout(
|
||||
extraX / 2,
|
||||
extraY / 2,
|
||||
width + (extraX / 2),
|
||||
height + (extraY / 2)
|
||||
);
|
||||
}
|
||||
|
||||
private void centerCrop(Size previewSize) {
|
||||
final float scale = Math.max(
|
||||
getMeasuredWidth() / (float) previewSize.width,
|
||||
getMeasuredHeight() / (float) previewSize.height
|
||||
|
||||
@@ -10,11 +10,13 @@ import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import io.fotoapparat.error.CameraErrorCallback;
|
||||
import io.fotoapparat.hardware.provider.CameraProvider;
|
||||
import io.fotoapparat.log.Logger;
|
||||
import io.fotoapparat.parameter.Flash;
|
||||
import io.fotoapparat.parameter.FocusMode;
|
||||
import io.fotoapparat.parameter.LensPosition;
|
||||
import io.fotoapparat.parameter.ScaleType;
|
||||
import io.fotoapparat.parameter.Size;
|
||||
import io.fotoapparat.parameter.selector.SelectorFunction;
|
||||
import io.fotoapparat.preview.FrameProcessor;
|
||||
@@ -28,227 +30,274 @@ import static org.mockito.BDDMockito.given;
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class FotoapparatBuilderTest {
|
||||
|
||||
@Mock
|
||||
Context context;
|
||||
@Mock
|
||||
CameraProvider cameraProvider;
|
||||
@Mock
|
||||
CameraRenderer cameraRenderer;
|
||||
@Mock
|
||||
Context context;
|
||||
@Mock
|
||||
CameraProvider cameraProvider;
|
||||
@Mock
|
||||
CameraRenderer cameraRenderer;
|
||||
|
||||
@Mock
|
||||
SelectorFunction<Size> photoSizeSelector;
|
||||
@Mock
|
||||
SelectorFunction<Size> previewSizeSelector;
|
||||
@Mock
|
||||
SelectorFunction<LensPosition> lensPositionSelector;
|
||||
@Mock
|
||||
SelectorFunction<FocusMode> focusModeSelector;
|
||||
@Mock
|
||||
SelectorFunction<Flash> flashSelector;
|
||||
@Mock
|
||||
SelectorFunction<Size> photoSizeSelector;
|
||||
@Mock
|
||||
SelectorFunction<Size> previewSizeSelector;
|
||||
@Mock
|
||||
SelectorFunction<LensPosition> lensPositionSelector;
|
||||
@Mock
|
||||
SelectorFunction<FocusMode> focusModeSelector;
|
||||
@Mock
|
||||
SelectorFunction<Flash> flashSelector;
|
||||
|
||||
@Mock
|
||||
FrameProcessor frameProcessor;
|
||||
@Mock
|
||||
FrameProcessor frameProcessor;
|
||||
|
||||
@Mock
|
||||
Logger logger;
|
||||
@Mock
|
||||
Logger logger;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
given(context.getSystemService(Context.WINDOW_SERVICE))
|
||||
.willReturn(Mockito.mock(WindowManager.class));
|
||||
}
|
||||
@Mock
|
||||
CameraErrorCallback cameraErrorCallback;
|
||||
|
||||
@Test
|
||||
public void onlyMandatoryParameters() throws Exception {
|
||||
// Given
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments();
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
given(context.getSystemService(Context.WINDOW_SERVICE))
|
||||
.willReturn(Mockito.mock(WindowManager.class));
|
||||
}
|
||||
|
||||
// When
|
||||
Fotoapparat result = builder.build();
|
||||
@Test
|
||||
public void onlyMandatoryParameters() throws Exception {
|
||||
// Given
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments();
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
}
|
||||
// When
|
||||
Fotoapparat result = builder.build();
|
||||
|
||||
@Test
|
||||
public void cameraProvider_HasDefault() throws Exception {
|
||||
// When
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments();
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
}
|
||||
|
||||
// Then
|
||||
assertNotNull(builder.cameraProvider);
|
||||
}
|
||||
@Test
|
||||
public void cameraProvider_HasDefault() throws Exception {
|
||||
// When
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments();
|
||||
|
||||
@Test
|
||||
public void cameraProvider_IsConfigurable() throws Exception {
|
||||
// When
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments()
|
||||
.cameraProvider(cameraProvider);
|
||||
// Then
|
||||
assertNotNull(builder.cameraProvider);
|
||||
}
|
||||
|
||||
// Then
|
||||
assertEquals(
|
||||
cameraProvider,
|
||||
builder.cameraProvider
|
||||
);
|
||||
}
|
||||
@Test
|
||||
public void cameraProvider_IsConfigurable() throws Exception {
|
||||
// When
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments()
|
||||
.cameraProvider(cameraProvider);
|
||||
|
||||
@Test
|
||||
public void logger_HasDefault() throws Exception {
|
||||
// When
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments();
|
||||
// Then
|
||||
assertEquals(
|
||||
cameraProvider,
|
||||
builder.cameraProvider
|
||||
);
|
||||
}
|
||||
|
||||
// Then
|
||||
assertNotNull(builder.logger);
|
||||
}
|
||||
@Test
|
||||
public void logger_HasDefault() throws Exception {
|
||||
// When
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments();
|
||||
|
||||
@Test
|
||||
public void logger_IsConfigurable() throws Exception {
|
||||
// When
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments()
|
||||
.logger(logger);
|
||||
// Then
|
||||
assertNotNull(builder.logger);
|
||||
}
|
||||
|
||||
// Then
|
||||
assertEquals(
|
||||
logger,
|
||||
builder.logger
|
||||
);
|
||||
}
|
||||
@Test
|
||||
public void logger_IsConfigurable() throws Exception {
|
||||
// When
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments()
|
||||
.logger(logger);
|
||||
|
||||
@Test
|
||||
public void focusMode_HasDefault() throws Exception {
|
||||
// When
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments();
|
||||
// Then
|
||||
assertEquals(
|
||||
logger,
|
||||
builder.logger
|
||||
);
|
||||
}
|
||||
|
||||
// Then
|
||||
assertNotNull(builder.focusModeSelector);
|
||||
}
|
||||
@Test
|
||||
public void focusMode_HasDefault() throws Exception {
|
||||
// When
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments();
|
||||
|
||||
@Test
|
||||
public void focusMode_IsConfigurable() throws Exception {
|
||||
// When
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments()
|
||||
.focusMode(focusModeSelector);
|
||||
// Then
|
||||
assertNotNull(builder.focusModeSelector);
|
||||
}
|
||||
|
||||
// Then
|
||||
assertEquals(
|
||||
focusModeSelector,
|
||||
builder.focusModeSelector
|
||||
);
|
||||
}
|
||||
@Test
|
||||
public void focusMode_IsConfigurable() throws Exception {
|
||||
// When
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments()
|
||||
.focusMode(focusModeSelector);
|
||||
|
||||
@Test
|
||||
public void flashMode_IsConfigurable() throws Exception {
|
||||
// When
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments()
|
||||
.flash(flashSelector);
|
||||
// Then
|
||||
assertEquals(
|
||||
focusModeSelector,
|
||||
builder.focusModeSelector
|
||||
);
|
||||
}
|
||||
|
||||
// Then
|
||||
assertEquals(
|
||||
flashSelector,
|
||||
builder.flashSelector
|
||||
);
|
||||
}
|
||||
@Test
|
||||
public void flashMode_IsConfigurable() throws Exception {
|
||||
// When
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments()
|
||||
.flash(flashSelector);
|
||||
|
||||
@Test
|
||||
public void frameProcessor_NullByDefault() throws Exception {
|
||||
// When
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments();
|
||||
// Then
|
||||
assertEquals(
|
||||
flashSelector,
|
||||
builder.flashSelector
|
||||
);
|
||||
}
|
||||
|
||||
// Then
|
||||
assertNull(builder.frameProcessor);
|
||||
}
|
||||
@Test
|
||||
public void frameProcessor_NullByDefault() throws Exception {
|
||||
// When
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments();
|
||||
|
||||
@Test
|
||||
public void frameProcessor_IsConfigurable() throws Exception {
|
||||
// When
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments()
|
||||
.frameProcessor(frameProcessor);
|
||||
// Then
|
||||
assertNull(builder.frameProcessor);
|
||||
}
|
||||
|
||||
// Then
|
||||
assertEquals(
|
||||
frameProcessor,
|
||||
builder.frameProcessor
|
||||
);
|
||||
}
|
||||
@Test
|
||||
public void frameProcessor_IsConfigurable() throws Exception {
|
||||
// When
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments()
|
||||
.frameProcessor(frameProcessor);
|
||||
|
||||
@Test
|
||||
public void photoSize_IsConfigurable() throws Exception {
|
||||
// When
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments()
|
||||
.photoSize(photoSizeSelector);
|
||||
// Then
|
||||
assertEquals(
|
||||
frameProcessor,
|
||||
builder.frameProcessor
|
||||
);
|
||||
}
|
||||
|
||||
// Then
|
||||
assertEquals(
|
||||
photoSizeSelector,
|
||||
builder.photoSizeSelector
|
||||
);
|
||||
}
|
||||
@Test
|
||||
public void photoSize_IsConfigurable() throws Exception {
|
||||
// When
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments()
|
||||
.photoSize(photoSizeSelector);
|
||||
|
||||
@Test
|
||||
public void previewSize_HasDefault() throws Exception {
|
||||
// When
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments();
|
||||
// Then
|
||||
assertEquals(
|
||||
photoSizeSelector,
|
||||
builder.photoSizeSelector
|
||||
);
|
||||
}
|
||||
|
||||
// Then
|
||||
assertNotNull(builder.previewSizeSelector);
|
||||
}
|
||||
@Test
|
||||
public void previewSize_HasDefault() throws Exception {
|
||||
// When
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments();
|
||||
|
||||
@Test
|
||||
public void previewSize_IsConfigurable() throws Exception {
|
||||
// When
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments()
|
||||
.previewSize(previewSizeSelector);
|
||||
// Then
|
||||
assertNotNull(builder.previewSizeSelector);
|
||||
}
|
||||
|
||||
// Then
|
||||
assertEquals(
|
||||
previewSizeSelector,
|
||||
builder.previewSizeSelector
|
||||
);
|
||||
}
|
||||
@Test
|
||||
public void previewSize_IsConfigurable() throws Exception {
|
||||
// When
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments()
|
||||
.previewSize(previewSizeSelector);
|
||||
|
||||
// Then
|
||||
assertEquals(
|
||||
previewSizeSelector,
|
||||
builder.previewSizeSelector
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void previewStyle_HasDefault() throws Exception {
|
||||
// When
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments();
|
||||
|
||||
// Then
|
||||
assertNotNull(builder.scaleType);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void previewStyle_IsConfigurable() throws Exception {
|
||||
// When
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments()
|
||||
.previewScaleType(ScaleType.CENTER_INSIDE);
|
||||
|
||||
// Then
|
||||
assertEquals(
|
||||
ScaleType.CENTER_INSIDE,
|
||||
builder.scaleType
|
||||
);
|
||||
}
|
||||
@Test
|
||||
public void cameraErrorCallback_HasDefault() throws Exception {
|
||||
// When
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments();
|
||||
|
||||
// Then
|
||||
assertNotNull(builder.cameraErrorCallback);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cameraErrorCallback_IsConfigurable() throws Exception {
|
||||
// When
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments()
|
||||
.cameraErrorCallback(cameraErrorCallback);
|
||||
|
||||
// Then
|
||||
assertEquals(
|
||||
cameraErrorCallback,
|
||||
builder.cameraErrorCallback
|
||||
);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public void rendererIsMandatory() throws Exception {
|
||||
// Given
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments()
|
||||
.into(null);
|
||||
|
||||
// When
|
||||
builder.build();
|
||||
|
||||
// Then
|
||||
// Expect exception
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void rendererIsMandatory() throws Exception {
|
||||
// Given
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments()
|
||||
.into(null);
|
||||
|
||||
// When
|
||||
builder.build();
|
||||
|
||||
// Then
|
||||
// Expect exception
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void lensPositionIsMandatory() throws Exception {
|
||||
@SuppressWarnings("ConstantConditions")public void lensPositionIsMandatory() throws Exception {
|
||||
// Given
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments()
|
||||
.lensPosition(null);
|
||||
|
||||
// When
|
||||
builder.build();
|
||||
// When
|
||||
builder.build();
|
||||
|
||||
// Then
|
||||
// Expect exception
|
||||
}
|
||||
// Then
|
||||
// Expect exception
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void photoSizeIsMandatory() throws Exception {
|
||||
@SuppressWarnings("ConstantConditions")public void photoSizeIsMandatory() throws Exception {
|
||||
// Given
|
||||
FotoapparatBuilder builder = builderWithMandatoryArguments()
|
||||
.photoSize(null);
|
||||
|
||||
// When
|
||||
builder.build();
|
||||
// When
|
||||
builder.build();
|
||||
|
||||
// Then
|
||||
// Expect exception
|
||||
}
|
||||
// Then
|
||||
// Expect exception
|
||||
}
|
||||
|
||||
private FotoapparatBuilder builderWithMandatoryArguments() {
|
||||
return new FotoapparatBuilder(context)
|
||||
.lensPosition(lensPositionSelector)
|
||||
.photoSize(photoSizeSelector)
|
||||
.into(cameraRenderer);
|
||||
}
|
||||
private FotoapparatBuilder builderWithMandatoryArguments() {
|
||||
return new FotoapparatBuilder(context)
|
||||
.lensPosition(lensPositionSelector)
|
||||
.photoSize(photoSizeSelector)
|
||||
.into(cameraRenderer);
|
||||
}
|
||||
}
|
||||
@@ -11,14 +11,16 @@ import io.fotoapparat.hardware.Capabilities;
|
||||
import io.fotoapparat.parameter.provider.CapabilitiesProvider;
|
||||
import io.fotoapparat.photo.Photo;
|
||||
import io.fotoapparat.result.CapabilitiesResult;
|
||||
import io.fotoapparat.result.FocusResult;
|
||||
import io.fotoapparat.result.PendingResult;
|
||||
import io.fotoapparat.result.PhotoResult;
|
||||
import io.fotoapparat.routine.AutoFocusRoutine;
|
||||
import io.fotoapparat.routine.CheckAvailabilityRoutine;
|
||||
import io.fotoapparat.routine.ConfigurePreviewStreamRoutine;
|
||||
import io.fotoapparat.routine.StartCameraRoutine;
|
||||
import io.fotoapparat.routine.StopCameraRoutine;
|
||||
import io.fotoapparat.routine.TakePictureRoutine;
|
||||
import io.fotoapparat.routine.UpdateOrientationRoutine;
|
||||
import io.fotoapparat.routine.focus.AutoFocusRoutine;
|
||||
import io.fotoapparat.routine.picture.TakePictureRoutine;
|
||||
import io.fotoapparat.test.ImmediateExecutor;
|
||||
|
||||
import static io.fotoapparat.test.TestUtils.immediateFuture;
|
||||
@@ -37,6 +39,13 @@ public class FotoapparatTest {
|
||||
Photo.empty()
|
||||
)
|
||||
);
|
||||
|
||||
static final PendingResult<FocusResult> FOCUS_RESULT = PendingResult.fromFuture(
|
||||
immediateFuture(
|
||||
FocusResult.FOCUSED
|
||||
)
|
||||
);
|
||||
|
||||
static final CapabilitiesResult CAPABILITIES_RESULT = CapabilitiesResult.fromFuture(
|
||||
immediateFuture(
|
||||
Capabilities.empty()
|
||||
@@ -196,7 +205,7 @@ public class FotoapparatTest {
|
||||
testee.autoFocus();
|
||||
|
||||
// Then
|
||||
verify(autoFocusRoutine).run();
|
||||
verify(autoFocusRoutine).autoFocus();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
@@ -208,6 +217,34 @@ public class FotoapparatTest {
|
||||
// Expect exception
|
||||
}
|
||||
|
||||
@Test
|
||||
public void focus() throws Exception {
|
||||
// Given
|
||||
given(autoFocusRoutine.autoFocus())
|
||||
.willReturn(FOCUS_RESULT);
|
||||
|
||||
testee.start();
|
||||
|
||||
// When
|
||||
PendingResult<FocusResult> result = testee.focus();
|
||||
|
||||
// Then
|
||||
assertEquals(
|
||||
FOCUS_RESULT,
|
||||
result
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void focus_NotStartedYet() throws Exception {
|
||||
// When
|
||||
testee.focus();
|
||||
|
||||
// Then
|
||||
// Expect exception
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void ensureNonNullContext() throws Exception {
|
||||
// Given
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package io.fotoapparat.hardware.v1.parameters;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import io.fotoapparat.hardware.operators.ParametersOperator;
|
||||
import io.fotoapparat.parameter.Parameters;
|
||||
import io.fotoapparat.parameter.Size;
|
||||
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class SplitParametersOperatorTest {
|
||||
|
||||
static final Size PICTURE_SIZE = new Size(100, 100);
|
||||
static final Size PREVIEW_SIZE = new Size(50, 50);
|
||||
|
||||
@Mock
|
||||
ParametersOperator wrapped;
|
||||
|
||||
@InjectMocks
|
||||
SplitParametersOperator testee;
|
||||
|
||||
@Test
|
||||
public void updateParameters() throws Exception {
|
||||
// Given
|
||||
Parameters parameters = new Parameters();
|
||||
parameters.putValue(Parameters.Type.PICTURE_SIZE, PICTURE_SIZE);
|
||||
parameters.putValue(Parameters.Type.PREVIEW_SIZE, PREVIEW_SIZE);
|
||||
|
||||
// When
|
||||
testee.updateParameters(parameters);
|
||||
|
||||
// Then
|
||||
verify(wrapped).updateParameters(
|
||||
parametersWithJust(Parameters.Type.PICTURE_SIZE, PICTURE_SIZE)
|
||||
);
|
||||
verify(wrapped).updateParameters(
|
||||
parametersWithJust(Parameters.Type.PREVIEW_SIZE, PREVIEW_SIZE)
|
||||
);
|
||||
|
||||
verifyNoMoreInteractions(wrapped);
|
||||
}
|
||||
|
||||
private Parameters parametersWithJust(Parameters.Type type, Object value) {
|
||||
Parameters parameters = new Parameters();
|
||||
parameters.putValue(type, value);
|
||||
|
||||
return parameters;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package io.fotoapparat.hardware.v1.parameters;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import io.fotoapparat.hardware.operators.ParametersOperator;
|
||||
import io.fotoapparat.log.Logger;
|
||||
import io.fotoapparat.parameter.Parameters;
|
||||
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class SupressExceptionsParametersOperatorTest {
|
||||
|
||||
@Mock
|
||||
ParametersOperator wrapped;
|
||||
@Mock
|
||||
Logger logger;
|
||||
@Mock
|
||||
Parameters parameters;
|
||||
|
||||
@InjectMocks
|
||||
SupressExceptionsParametersOperator testee;
|
||||
|
||||
@Test
|
||||
public void updateParameters() throws Exception {
|
||||
// Given
|
||||
doThrow(new RuntimeException())
|
||||
.when(wrapped)
|
||||
.updateParameters(parameters);
|
||||
|
||||
// When
|
||||
testee.updateParameters(parameters);
|
||||
|
||||
// Then
|
||||
verify(wrapped).updateParameters(parameters);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package io.fotoapparat.hardware.v1.parameters;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import io.fotoapparat.hardware.operators.ParametersOperator;
|
||||
import io.fotoapparat.parameter.Parameters;
|
||||
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class SwitchOnFailureParametersOperatorTest {
|
||||
|
||||
@Mock
|
||||
ParametersOperator first;
|
||||
@Mock
|
||||
ParametersOperator second;
|
||||
@Mock
|
||||
Parameters parameters;
|
||||
|
||||
SwitchOnFailureParametersOperator testee;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
testee = new SwitchOnFailureParametersOperator(
|
||||
first, second
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void firstSucceeds() throws Exception {
|
||||
// When
|
||||
testee.updateParameters(parameters);
|
||||
|
||||
// Then
|
||||
verify(first).updateParameters(parameters);
|
||||
|
||||
verifyZeroInteractions(second);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void firstFails() throws Exception {
|
||||
// Given
|
||||
doThrow(new RuntimeException())
|
||||
.when(first)
|
||||
.updateParameters(parameters);
|
||||
|
||||
// When
|
||||
testee.updateParameters(parameters);
|
||||
|
||||
// Then
|
||||
verify(second).updateParameters(parameters);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package io.fotoapparat.routine;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import io.fotoapparat.hardware.CameraDevice;
|
||||
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class AutoFocusRoutineTest {
|
||||
|
||||
@Mock
|
||||
CameraDevice cameraDevice;
|
||||
|
||||
@InjectMocks
|
||||
AutoFocusRoutine testee;
|
||||
|
||||
@Test
|
||||
public void autoFocus() throws Exception {
|
||||
// When
|
||||
testee.run();
|
||||
|
||||
// Then
|
||||
verify(cameraDevice).autoFocus();
|
||||
}
|
||||
}
|
||||
@@ -1,98 +1,162 @@
|
||||
package io.fotoapparat.routine;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.fotoapparat.error.CameraErrorCallback;
|
||||
import io.fotoapparat.hardware.CameraDevice;
|
||||
import io.fotoapparat.hardware.CameraException;
|
||||
import io.fotoapparat.hardware.orientation.ScreenOrientationProvider;
|
||||
import io.fotoapparat.parameter.LensPosition;
|
||||
import io.fotoapparat.parameter.Parameters;
|
||||
import io.fotoapparat.parameter.ScaleType;
|
||||
import io.fotoapparat.parameter.provider.InitialParametersProvider;
|
||||
import io.fotoapparat.parameter.selector.SelectorFunction;
|
||||
import io.fotoapparat.view.CameraRenderer;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class StartCameraRoutineTest {
|
||||
|
||||
static final int SCREEN_ROTATION_DEGREES = 90;
|
||||
static final Parameters INITIAL_PARAMETERS = new Parameters();
|
||||
static final int SCREEN_ROTATION_DEGREES = 90;
|
||||
static final Parameters INITIAL_PARAMETERS = new Parameters();
|
||||
|
||||
@Mock
|
||||
CameraDevice cameraDevice;
|
||||
@Mock
|
||||
CameraRenderer cameraRenderer;
|
||||
@Mock
|
||||
SelectorFunction<LensPosition> lensPositionSelector;
|
||||
@Mock
|
||||
ScreenOrientationProvider screenOrientationProvider;
|
||||
@Mock
|
||||
InitialParametersProvider initialParametersProvider;
|
||||
@SuppressWarnings("ThrowableInstanceNeverThrown")
|
||||
static final CameraException CAMERA_EXCEPTION = new CameraException("test");
|
||||
|
||||
@InjectMocks
|
||||
StartCameraRoutine testee;
|
||||
@Mock
|
||||
CameraDevice cameraDevice;
|
||||
@Mock
|
||||
CameraRenderer cameraRenderer;
|
||||
@Mock
|
||||
SelectorFunction<LensPosition> lensPositionSelector;
|
||||
@Mock
|
||||
ScreenOrientationProvider screenOrientationProvider;
|
||||
@Mock
|
||||
InitialParametersProvider initialParametersProvider;
|
||||
@Mock
|
||||
CameraErrorCallback cameraErrorCallback;
|
||||
|
||||
@Test
|
||||
public void routine() throws Exception {
|
||||
// Given
|
||||
List<LensPosition> availableLensPositions = asList(
|
||||
LensPosition.FRONT,
|
||||
LensPosition.BACK
|
||||
);
|
||||
StartCameraRoutine testee;
|
||||
|
||||
LensPosition preferredLensPosition = LensPosition.FRONT;
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
testee = new StartCameraRoutine(
|
||||
cameraDevice,
|
||||
cameraRenderer,
|
||||
ScaleType.CENTER_INSIDE,
|
||||
lensPositionSelector,
|
||||
screenOrientationProvider,
|
||||
initialParametersProvider,
|
||||
cameraErrorCallback
|
||||
);
|
||||
}
|
||||
|
||||
givenLensPositionsAvailable(availableLensPositions);
|
||||
givenPositionSelected(preferredLensPosition);
|
||||
givenScreenRotation();
|
||||
givenInitialParametersAvailable();
|
||||
@Test
|
||||
public void routine() throws Exception {
|
||||
// Given
|
||||
StartCameraRoutine testee = new StartCameraRoutine(
|
||||
cameraDevice,
|
||||
cameraRenderer,
|
||||
ScaleType.CENTER_INSIDE,
|
||||
lensPositionSelector,
|
||||
screenOrientationProvider,
|
||||
initialParametersProvider,
|
||||
cameraErrorCallback
|
||||
);
|
||||
List<LensPosition> availableLensPositions = asList(
|
||||
LensPosition.FRONT,
|
||||
LensPosition.BACK
|
||||
);
|
||||
|
||||
// When
|
||||
testee.run();
|
||||
LensPosition preferredLensPosition = LensPosition.FRONT;
|
||||
ScaleType scaleType = ScaleType.CENTER_INSIDE;
|
||||
|
||||
// Then
|
||||
InOrder inOrder = inOrder(
|
||||
cameraDevice,
|
||||
cameraRenderer,
|
||||
lensPositionSelector
|
||||
);
|
||||
givenLensPositionsAvailable(availableLensPositions);
|
||||
givenPositionSelected(preferredLensPosition);
|
||||
givenScreenRotation();
|
||||
givenInitialParametersAvailable();
|
||||
|
||||
inOrder.verify(lensPositionSelector).select(availableLensPositions);
|
||||
inOrder.verify(cameraDevice).open(preferredLensPosition);
|
||||
inOrder.verify(cameraDevice).updateParameters(INITIAL_PARAMETERS);
|
||||
inOrder.verify(cameraDevice).setDisplayOrientation(SCREEN_ROTATION_DEGREES);
|
||||
inOrder.verify(cameraRenderer).attachCamera(cameraDevice);
|
||||
inOrder.verify(cameraDevice).startPreview();
|
||||
}
|
||||
// When
|
||||
testee.run();
|
||||
|
||||
private void givenInitialParametersAvailable() {
|
||||
given(initialParametersProvider.initialParameters())
|
||||
.willReturn(INITIAL_PARAMETERS);
|
||||
}
|
||||
// Then
|
||||
InOrder inOrder = inOrder(
|
||||
cameraDevice,
|
||||
cameraRenderer,
|
||||
lensPositionSelector
|
||||
);
|
||||
|
||||
private void givenScreenRotation() {
|
||||
given(screenOrientationProvider.getScreenRotation())
|
||||
.willReturn(SCREEN_ROTATION_DEGREES);
|
||||
}
|
||||
inOrder.verify(lensPositionSelector).select(availableLensPositions);
|
||||
inOrder.verify(cameraDevice).open(preferredLensPosition);
|
||||
inOrder.verify(cameraDevice).updateParameters(INITIAL_PARAMETERS);
|
||||
inOrder.verify(cameraDevice).setDisplayOrientation(SCREEN_ROTATION_DEGREES);
|
||||
inOrder.verify(cameraRenderer).setScaleType(scaleType);
|
||||
inOrder.verify(cameraRenderer).attachCamera(cameraDevice);
|
||||
inOrder.verify(cameraDevice).startPreview();
|
||||
verifyZeroInteractions(cameraErrorCallback);
|
||||
}
|
||||
|
||||
private void givenPositionSelected(LensPosition lensPosition) {
|
||||
given(lensPositionSelector.select(ArgumentMatchers.<LensPosition>anyCollection()))
|
||||
.willReturn(lensPosition);
|
||||
}
|
||||
@Test
|
||||
public void failedToOpenCamera() throws Exception {
|
||||
// Given
|
||||
List<LensPosition> availableLensPositions = asList(
|
||||
LensPosition.FRONT,
|
||||
LensPosition.BACK
|
||||
);
|
||||
|
||||
private void givenLensPositionsAvailable(List<LensPosition> lensPositions) {
|
||||
given(cameraDevice.getAvailableLensPositions())
|
||||
.willReturn(lensPositions);
|
||||
}
|
||||
LensPosition preferredLensPosition = LensPosition.FRONT;
|
||||
|
||||
givenLensPositionsAvailable(availableLensPositions);
|
||||
givenPositionSelected(preferredLensPosition);
|
||||
|
||||
doThrow(CAMERA_EXCEPTION)
|
||||
.when(cameraDevice)
|
||||
.open(preferredLensPosition);
|
||||
|
||||
// When
|
||||
testee.run();
|
||||
|
||||
// Then
|
||||
verify(cameraErrorCallback).onError(CAMERA_EXCEPTION);
|
||||
|
||||
verify(cameraDevice).getAvailableLensPositions();
|
||||
verify(cameraDevice).open(preferredLensPosition);
|
||||
verifyNoMoreInteractions(cameraDevice);
|
||||
}
|
||||
|
||||
private void givenInitialParametersAvailable() {
|
||||
given(initialParametersProvider.initialParameters())
|
||||
.willReturn(INITIAL_PARAMETERS);
|
||||
}
|
||||
|
||||
private void givenScreenRotation() {
|
||||
given(screenOrientationProvider.getScreenRotation())
|
||||
.willReturn(SCREEN_ROTATION_DEGREES);
|
||||
}
|
||||
|
||||
private void givenPositionSelected(LensPosition lensPosition) {
|
||||
given(lensPositionSelector.select(ArgumentMatchers.<LensPosition>anyCollection()))
|
||||
.willReturn(lensPosition);
|
||||
}
|
||||
|
||||
private void givenLensPositionsAvailable(List<LensPosition> lensPositions) {
|
||||
given(cameraDevice.getAvailableLensPositions())
|
||||
.willReturn(lensPositions);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package io.fotoapparat.routine.focus;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import io.fotoapparat.hardware.CameraDevice;
|
||||
import io.fotoapparat.result.FocusResult;
|
||||
import io.fotoapparat.result.PendingResult;
|
||||
import io.fotoapparat.test.ImmediateExecutor;
|
||||
|
||||
import static junit.framework.Assert.assertNotNull;
|
||||
import static org.mockito.ArgumentMatchers.isA;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class AutoFocusRoutineTest {
|
||||
|
||||
@Mock
|
||||
CameraDevice cameraDevice;
|
||||
@Spy
|
||||
Executor executor = new ImmediateExecutor();
|
||||
|
||||
@InjectMocks
|
||||
AutoFocusRoutine testee;
|
||||
|
||||
@Test
|
||||
public void autoFocus() throws Exception {
|
||||
// When
|
||||
PendingResult<FocusResult> result = testee.autoFocus();
|
||||
|
||||
// Then
|
||||
verify(executor).execute(isA(AutoFocusTask.class));
|
||||
|
||||
assertNotNull(result);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package io.fotoapparat.routine.focus;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import io.fotoapparat.hardware.CameraDevice;
|
||||
import io.fotoapparat.lens.FocusResult;
|
||||
|
||||
import static io.fotoapparat.result.FocusResult.FOCUSED;
|
||||
import static io.fotoapparat.result.FocusResult.UNABLE_TO_FOCUS;
|
||||
import static io.fotoapparat.test.TestUtils.resultOf;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class AutoFocusTaskTest {
|
||||
|
||||
@Mock
|
||||
CameraDevice cameraDevice;
|
||||
|
||||
@InjectMocks
|
||||
AutoFocusTask testee;
|
||||
|
||||
@Test
|
||||
public void autoFocus_Focused() throws Exception {
|
||||
// Given
|
||||
given(cameraDevice.autoFocus())
|
||||
.willReturn(new FocusResult(true, false));
|
||||
|
||||
// When
|
||||
io.fotoapparat.result.FocusResult result = resultOf(testee);
|
||||
|
||||
// Then
|
||||
assertEquals(result, FOCUSED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void autoFocus_UnableToFocus() throws Exception {
|
||||
// Given
|
||||
given(cameraDevice.autoFocus())
|
||||
.willReturn(new FocusResult(false, false));
|
||||
|
||||
// When
|
||||
io.fotoapparat.result.FocusResult result = resultOf(testee);
|
||||
|
||||
// Then
|
||||
assertEquals(result, UNABLE_TO_FOCUS);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package io.fotoapparat.routine;
|
||||
package io.fotoapparat.routine.picture;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -11,6 +11,8 @@ import java.util.concurrent.Executor;
|
||||
|
||||
import io.fotoapparat.hardware.CameraDevice;
|
||||
import io.fotoapparat.result.PhotoResult;
|
||||
import io.fotoapparat.routine.picture.TakePictureRoutine;
|
||||
import io.fotoapparat.routine.picture.TakePictureTask;
|
||||
import io.fotoapparat.test.ImmediateExecutor;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
@@ -1,4 +1,4 @@
|
||||
package io.fotoapparat.routine;
|
||||
package io.fotoapparat.routine.picture;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -9,14 +9,17 @@ import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import io.fotoapparat.hardware.CameraDevice;
|
||||
import io.fotoapparat.hardware.CameraException;
|
||||
import io.fotoapparat.lens.FocusResult;
|
||||
import io.fotoapparat.photo.Photo;
|
||||
|
||||
import static io.fotoapparat.test.TestUtils.resultOf;
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class TakePictureTaskTest {
|
||||
@@ -89,4 +92,24 @@ public class TakePictureTaskTest {
|
||||
|
||||
assertEquals(result, PHOTO);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startPreviewFailed() throws Exception {
|
||||
// Given
|
||||
given(cameraDevice.autoFocus())
|
||||
.willReturn(new FocusResult(true, false));
|
||||
|
||||
doThrow(new CameraException("test"))
|
||||
.when(cameraDevice)
|
||||
.startPreview();
|
||||
|
||||
// When
|
||||
Photo result = resultOf(testee);
|
||||
|
||||
// Then
|
||||
verify(cameraDevice).startPreview();
|
||||
|
||||
assertEquals(result, PHOTO);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
androidBuildToolsVersion=2.3.2
|
||||
buildToolsVersion=25.0.2
|
||||
compileSdkVersion=25
|
||||
minSdkVersion=15
|
||||
minSdkVersion=14
|
||||
targetSdkVersion=25
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ mockitoVersion=2.2.28
|
||||
|
||||
|
||||
#Gradle Properties
|
||||
gradleVersion=3.3
|
||||
gradleVersion=4.0
|
||||
org.gradle.daemon=true
|
||||
org.gradle.parallel=true
|
||||
org.gradle.jvmargs=-Xmx1536m
|
||||
|
||||
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-all.zip
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
|
||||
@@ -5,12 +5,16 @@ import android.support.annotation.NonNull;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import io.fotoapparat.Fotoapparat;
|
||||
import io.fotoapparat.FotoapparatSwitcher;
|
||||
import io.fotoapparat.error.CameraErrorCallback;
|
||||
import io.fotoapparat.hardware.CameraException;
|
||||
import io.fotoapparat.parameter.LensPosition;
|
||||
import io.fotoapparat.parameter.ScaleType;
|
||||
import io.fotoapparat.photo.BitmapPhoto;
|
||||
import io.fotoapparat.preview.Frame;
|
||||
import io.fotoapparat.preview.FrameProcessor;
|
||||
@@ -58,24 +62,32 @@ public class MainActivity extends AppCompatActivity {
|
||||
permissionsDelegate.requestCameraPermission();
|
||||
}
|
||||
|
||||
setupFotoapparat();
|
||||
|
||||
takePictureOnClick(cameraView);
|
||||
focusOnLongClick(cameraView);
|
||||
|
||||
setupSwitchCameraButton();
|
||||
}
|
||||
|
||||
private void setupFotoapparat() {
|
||||
frontFotoapparat = createFotoapparat(LensPosition.FRONT);
|
||||
backFotoapparat = createFotoapparat(LensPosition.BACK);
|
||||
fotoapparatSwitcher = FotoapparatSwitcher.withDefault(backFotoapparat);
|
||||
}
|
||||
|
||||
cameraView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
takePicture();
|
||||
}
|
||||
});
|
||||
|
||||
private void setupSwitchCameraButton() {
|
||||
View switchCameraButton = findViewById(R.id.switchCamera);
|
||||
switchCameraButton.setVisibility(
|
||||
canSwitchCameras()
|
||||
? View.VISIBLE
|
||||
: View.GONE
|
||||
);
|
||||
switchCameraButton.setOnClickListener(new View.OnClickListener() {
|
||||
switchCameraOnClick(switchCameraButton);
|
||||
}
|
||||
|
||||
private void switchCameraOnClick(View view) {
|
||||
view.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
switchCamera();
|
||||
@@ -83,6 +95,26 @@ public class MainActivity extends AppCompatActivity {
|
||||
});
|
||||
}
|
||||
|
||||
private void focusOnLongClick(View view) {
|
||||
view.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
fotoapparatSwitcher.getCurrentFotoapparat().autoFocus();
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void takePictureOnClick(View view) {
|
||||
view.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
takePicture();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean canSwitchCameras() {
|
||||
return frontFotoapparat.isAvailable() == backFotoapparat.isAvailable();
|
||||
}
|
||||
@@ -91,6 +123,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
return Fotoapparat
|
||||
.with(this)
|
||||
.into(cameraView)
|
||||
.previewScaleType(ScaleType.CENTER_CROP)
|
||||
.photoSize(standardRatio(biggestSize()))
|
||||
.lensPosition(lensPosition(position))
|
||||
.focusMode(firstAvailable(
|
||||
@@ -109,6 +142,12 @@ public class MainActivity extends AppCompatActivity {
|
||||
logcat(),
|
||||
fileLogger(this)
|
||||
))
|
||||
.cameraErrorCallback(new CameraErrorCallback() {
|
||||
@Override
|
||||
public void onError(CameraException e) {
|
||||
Toast.makeText(MainActivity.this, e.toString(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 14 KiB |