Compare commits

...

39 Commits

Author SHA1 Message Date
Dionysis 42339f800c Merge branch 'develop' 2017-07-16 23:14:02 +02:00
Dmitry Zaitsev 8d5b16c4bd Update README.md 2017-07-16 22:08:11 +02:00
Dionysis Lorentzos add2692f37 Update README.md 2017-07-16 22:03:23 +02:00
Dionysis Lorentzos 43029afab3 Merge pull request #59 from Fotoapparat/feature/preview_scaletype
Feature/preview scaletype
2017-07-16 22:01:43 +02:00
Dionysis 358bd1b7f3 Remove ScaleTypeSelectors 2017-07-16 22:00:43 +02:00
Dionysis eb0746a780 Format 2017-07-16 21:54:38 +02:00
Dionysis 58e5e93382 Merge branch 'develop' into feature/preview_scaletype
# Conflicts:
#	fotoapparat/src/main/java/io/fotoapparat/hardware/v1/capabilities/CapabilitiesFactory.java
#	fotoapparat/src/main/java/io/fotoapparat/routine/StartCameraRoutine.java
#	fotoapparat/src/test/java/io/fotoapparat/FotoapparatBuilderTest.java
#	fotoapparat/src/test/java/io/fotoapparat/routine/StartCameraRoutineTest.java
2017-07-16 21:53:07 +02:00
Dionysis 6d22e7c6ae Merge branch 'develop' into feature/preview_scaletype
# Conflicts:
#	fotoapparat/src/main/java/io/fotoapparat/hardware/v1/capabilities/CapabilitiesFactory.java
#	fotoapparat/src/main/java/io/fotoapparat/routine/StartCameraRoutine.java
#	fotoapparat/src/test/java/io/fotoapparat/FotoapparatBuilderTest.java
#	fotoapparat/src/test/java/io/fotoapparat/routine/StartCameraRoutineTest.java
2017-07-16 21:52:56 +02:00
Dionysis 0bf4a3df8f Remove redundant code 2017-07-16 21:45:02 +02:00
Dionysis 57729b9dd8 Remove traveling param and use it in the start routine 2017-07-16 21:40:16 +02:00
Dionysis f3d67273a2 Jdoc typos 2017-07-16 20:32:56 +02:00
Dionysis 0b0ded639b Update readme and example 2017-07-16 20:29:13 +02:00
Dionysis 898769b1a4 Camera 2 tests 2017-07-16 20:24:27 +02:00
Dmitry Zaitsev 0ce7f5e687 Merge pull request #58 from defer/ft-ICS-MR0
fotoapparat: Support API level 14
2017-07-16 18:59:32 +02:00
Diogo Ferreira c2b8755f5f fotoapparat: Support API level 14
15 is already supported, and 14 isn't much different. The only call that
surfaced lint errors is only used on api 21 or above for camera 2.
2017-07-16 16:12:16 +01:00
Dionysis 628781b706 Camera 1 now supports preview scale type 2017-07-16 15:40:50 +02:00
Dmitry Zaitsev 3dfa659723 Added mention about FaceDetector 2017-07-16 12:48:14 +02:00
Dionysis 37ec1addde Add scale type in the builder 2017-07-15 22:57:46 +02:00
Dionysis 6614785f7b Add scale type selectors 2017-07-15 22:54:35 +02:00
Dmitry Zaytsev e89a06e4e0 [issue-56] Ignoring CameraException when trying to restart preview after taking a photo. 2017-07-14 22:35:27 +02:00
Dmitry Zaytsev 4f46df5bfd Added example of CameraErrorHandler to sample app. 2017-07-14 22:17:48 +02:00
Dmitry Zaytsev e5c60fb31f Redirecting Camera1 errors to CameraErrorCallback instead of crashing the app. 2017-07-14 22:14:38 +02:00
Dmitry Zaytsev 88c8281f27 UnrecoverableErrorCallback can now be attached to Fotoapparat 2017-07-12 23:42:18 +02:00
Dmitry Zaytsev 4249bc5b30 Added @NonNull annotations to FotoapparatBuilder 2017-07-12 23:23:50 +02:00
Dmitry Zaytsev 541bd2d740 Updated version in README to 1.1.0 2017-07-08 23:16:06 +02:00
Dmitry Zaytsev b25faddf57 In Camera1 we now try to split parameters into individual sub-maps of size 1 in case if updating all parameters at once fails. 2017-07-08 23:15:28 +02:00
Dmitry Zaytsev 82501be283 Added Fotoapparat.focus(). 2017-07-08 22:33:14 +02:00
Dmitry Zaytsev fcf1432c43 AutoFocusRoutine now returns PendingResult of FocusResult. Cleaned up sample app. 2017-07-08 22:25:53 +02:00
Dmitry Zaytsev 79e8b58167 Added timeout for autofocus callback in Camera1 2017-07-07 19:23:08 +02:00
Dmitry Zaytsev 6ad0e214da Updated version name in README 2017-07-04 16:21:58 +02:00
Dmitry Zaytsev 17f897e8a1 Frame processors were called from the main thread 2017-07-04 16:21:32 +02:00
Dmitry Zaytsev 582174aeb1 Updated version number in README 2017-07-03 20:17:47 +02:00
Dmitry Zaytsev 023743135e Added logging to capture renderer parameters 2017-07-03 20:07:06 +02:00
Dmitry Zaytsev ba031c1437 Added more logging to Camera1 2017-07-03 19:46:48 +02:00
Dionysis Lorentzos a951882fe9 fix typo 2017-06-29 20:01:43 +02:00
Dionysis Lorentzos 2d0cc63637 Merge pull request #45 from Fotoapparat/icon
Update lib & sample icon.
2017-06-26 14:10:12 +02:00
Dionysis 5b953c1df2 Update lib & sample icon. 2017-06-25 20:53:13 +02:00
Dionysis f25ea5d39f Revert "Update README.md"
This reverts commit aab760bab8.
2017-06-16 23:51:00 +02:00
Dionysis Lorentzos aab760bab8 Update README.md 2017-06-16 23:48:24 +02:00
56 changed files with 1485 additions and 485 deletions
+10 -1
View File
@@ -2,6 +2,9 @@
![Build status](https://travis-ci.org/Fotoapparat/Fotoapparat.svg?branch=master)
![ ](sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png)
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
+4
View File
@@ -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 -2
View File
@@ -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
+1 -1
View File
@@ -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
-1
View File
@@ -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();
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB