Compare commits

...

63 Commits

Author SHA1 Message Date
Dmitry Zaytsev 0ad6dbe102 Updated version in README to 1.3.0 2017-08-26 17:08:33 +02:00
Dmitry Zaitsev c7c0173da1 Merge pull request #85 from Fotoapparat/feature/zoom
Zoom
2017-08-26 16:36:31 +02:00
Dmitry Zaytsev d32dfc72de Performance optimizations in Camera1. 2017-08-26 16:35:26 +02:00
Dmitry Zaytsev 54f5c2bb65 Added zoom seekbar to the sample app. 2017-08-26 16:22:38 +02:00
Dmitry Zaytsev 91d8819cd9 Added Capabilities caching to Camera1. 2017-08-26 16:12:06 +02:00
Dmitry Zaytsev 180c238ae2 Implemented UpdateZoomLevelRoutine. 2017-08-26 16:10:17 +02:00
Dmitry Zaytsev d0969068ed Added setZoom to Fotoapparat API. 2017-08-26 16:01:44 +02:00
Dmitry Zaytsev 7fe3db197e Added ZoomOperator which Camera1 now implements. 2017-08-26 15:53:29 +02:00
Dmitry Zaytsev ad42f3a675 Capabilities now provide information about whether zoom feature is available or not. 2017-08-26 15:44:41 +02:00
Dmitry Zaytsev da7ebf7c35 Fixed racing condition when we were waiting for SurfaceTexture even though view was already detached from window. 2017-08-26 15:35:55 +02:00
Dmitry Zaitsev 0375a0c78d Merge pull request #75 from Fotoapparat/feature/update-configuration
Updating configuration after Fotoapparat is already started
2017-08-09 21:44:43 +02:00
Dmitry Zaitsev 96685954b1 Added part about updating parameters 2017-08-09 21:43:53 +02:00
Dmitry Zaytsev ee73135b8d Added test for InitialParametersProvider. 2017-08-09 21:39:00 +02:00
Dmitry Zaytsev 290994b83e Added test for UpdateParametersRoutine. 2017-08-09 21:07:19 +02:00
Dmitry Zaytsev 4663358edd Updated example to toggle torch 2017-08-09 01:19:35 +02:00
Dmitry Zaytsev 1df33da0c6 Implemented UpdateParametersRoutine. 2017-08-09 00:59:03 +02:00
Dmitry Zaytsev 80c290cb1f Added ParametersFactory. Rewritten InitialParametersProvider. Refactored tests. 2017-08-09 00:50:30 +02:00
Dmitry Zaytsev 3b8e8a8395 Added UpdateRequest. 2017-08-08 21:35:20 +02:00
Dmitry Zaytsev 15e4f0aa9a Merge branch 'develop' of github.com:dmitry-zaitsev/Fotoapparat into develop 2017-08-07 22:57:20 +02:00
Dmitry Zaytsev 33ea208754 Using support-annotations instead of appcompat 2017-08-07 22:56:55 +02:00
Dionysis b6ade7868a Merge branch 'develop' of github.com:Fotoapparat/Fotoapparat into develop 2017-07-16 23:25:30 +02:00
Dionysis Lorentzos 295777c622 Update README.md 2017-07-16 23:23:56 +02:00
Dionysis 42339f800c Merge branch 'develop' 2017-07-16 23:14:02 +02:00
Dmitry Zaytsev 6df93c4527 Merge branch 'master' of github.com:dmitry-zaitsev/Fotoapparat into develop 2017-07-16 22:08:36 +02:00
Dmitry Zaitsev 8d5b16c4bd Update README.md 2017-07-16 22:08:11 +02:00
Dionysis Lorentzos 7e9c31aabc Update README.md 2017-07-16 22:06:58 +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
76 changed files with 2594 additions and 675 deletions
+25 -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,6 +49,7 @@ 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
.lensPosition(back()) // we want back camera
.focusMode(firstAvailable( // (optional) use the first focus mode which is supported by device
@@ -119,6 +123,22 @@ photoResult
});
```
## Update parameters
It is also possible to update some parameters after `Fotoapparat` was already started.
```java
fotoapparat.updateParameters(
UpdateRequest.builder()
.flash(
isTurnedOn
? torch()
: off()
)
.build()
)
```
## Set up
Add dependency to your `build.gradle`
@@ -128,16 +148,20 @@ repositories {
maven { url 'https://jitpack.io' }
}
compile 'io.fotoapparat.fotoapparat:library:1.0.2'
compile 'io.fotoapparat.fotoapparat:library:1.3.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
+5 -2
View File
@@ -19,13 +19,16 @@ android {
minifyEnabled false
}
}
testOptions {
unitTests.returnDefaultValues = true
}
}
dependencies {
compile "com.android.support:appcompat-v7:${project.appCompatVersion}"
compile "com.android.support:support-annotations:${project.appCompatVersion}"
testCompile "junit:junit:${project.junitVersion}"
testCompile "org.mockito:mockito-core:${project.mockitoVersion}"
testCompile 'commons-io:commons-io:2.5'
}
@@ -1,10 +1,14 @@
package io.fotoapparat;
import android.content.Context;
import android.support.annotation.FloatRange;
import android.support.annotation.NonNull;
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;
@@ -12,15 +16,20 @@ import io.fotoapparat.hardware.orientation.ScreenOrientationProvider;
import io.fotoapparat.parameter.provider.CapabilitiesProvider;
import io.fotoapparat.parameter.provider.InitialParametersProvider;
import io.fotoapparat.parameter.provider.InitialParametersValidator;
import io.fotoapparat.parameter.update.UpdateRequest;
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.parameter.UpdateParametersRoutine;
import io.fotoapparat.routine.picture.TakePictureRoutine;
import io.fotoapparat.routine.zoom.UpdateZoomLevelRoutine;
/**
* Camera. Takes pictures.
@@ -37,6 +46,8 @@ public class Fotoapparat {
private final TakePictureRoutine takePictureRoutine;
private final AutoFocusRoutine autoFocusRoutine;
private final CheckAvailabilityRoutine checkAvailabilityRoutine;
private final UpdateParametersRoutine updateParametersRoutine;
private final UpdateZoomLevelRoutine updateZoomLevelRoutine;
private final Executor executor;
private boolean started = false;
@@ -49,6 +60,8 @@ public class Fotoapparat {
TakePictureRoutine takePictureRoutine,
AutoFocusRoutine autoFocusRoutine,
CheckAvailabilityRoutine checkAvailabilityRoutine,
UpdateParametersRoutine updateParametersRoutine,
UpdateZoomLevelRoutine updateZoomLevelRoutine,
Executor executor) {
this.startCameraRoutine = startCameraRoutine;
this.stopCameraRoutine = stopCameraRoutine;
@@ -58,6 +71,8 @@ public class Fotoapparat {
this.takePictureRoutine = takePictureRoutine;
this.autoFocusRoutine = autoFocusRoutine;
this.checkAvailabilityRoutine = checkAvailabilityRoutine;
this.updateParametersRoutine = updateParametersRoutine;
this.updateZoomLevelRoutine = updateZoomLevelRoutine;
this.executor = executor;
}
@@ -70,8 +85,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 +107,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,13 +142,24 @@ public class Fotoapparat {
SERIAL_EXECUTOR
);
AutoFocusRoutine autoFocusRoutine = new AutoFocusRoutine(cameraDevice);
AutoFocusRoutine autoFocusRoutine = new AutoFocusRoutine(
cameraDevice,
SERIAL_EXECUTOR
);
CheckAvailabilityRoutine checkAvailabilityRoutine = new CheckAvailabilityRoutine(
cameraDevice,
builder.lensPositionSelector
);
UpdateParametersRoutine updateParametersRoutine = new UpdateParametersRoutine(
cameraDevice
);
UpdateZoomLevelRoutine updateZoomLevelRoutine = new UpdateZoomLevelRoutine(
cameraDevice
);
return new Fotoapparat(
startCameraRoutine,
stopCameraRoutine,
@@ -137,6 +169,8 @@ public class Fotoapparat {
takePictureRoutine,
autoFocusRoutine,
checkAvailabilityRoutine,
updateParametersRoutine,
updateZoomLevelRoutine,
SERIAL_EXECUTOR
);
}
@@ -175,15 +209,54 @@ 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();
}
/**
* Asynchronously updates parameters of the camera. Must be called only after {@link #start()}.
*/
public void updateParameters(@NonNull final UpdateRequest updateRequest) {
ensureStarted();
executor.execute(new Runnable() {
@Override
public void run() {
updateParametersRoutine.updateParameters(updateRequest);
}
});
}
/**
* Asynchronously updates zoom level of the camera. Must be called only after {@link #start()}.
* <p>
* If zoom is not supported by the device - does nothing.
*
* @param zoomLevel zoom level of the camera. A value between 0 and 1.
*/
public void setZoom(@FloatRange(from = 0f, to = 1f) final float zoomLevel) {
ensureStarted();
executor.execute(new Runnable() {
@Override
public void run() {
updateZoomLevelRoutine.updateZoomLevel(zoomLevel);
}
});
}
/**
* 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);
}
@@ -1,5 +1,7 @@
package io.fotoapparat.hardware;
import android.support.annotation.FloatRange;
import java.util.List;
import io.fotoapparat.hardware.operators.AutoFocusOperator;
@@ -13,13 +15,14 @@ import io.fotoapparat.hardware.operators.PreviewOperator;
import io.fotoapparat.hardware.operators.PreviewStreamOperator;
import io.fotoapparat.hardware.operators.RendererParametersOperator;
import io.fotoapparat.hardware.operators.SurfaceOperator;
import io.fotoapparat.hardware.operators.ZoomOperator;
import io.fotoapparat.hardware.provider.AvailableLensPositionsProvider;
import io.fotoapparat.lens.FocusResult;
import io.fotoapparat.parameter.LensPosition;
import io.fotoapparat.parameter.Parameters;
import io.fotoapparat.parameter.RendererParameters;
import io.fotoapparat.photo.Photo;
import io.fotoapparat.preview.PreviewStream;
import io.fotoapparat.lens.FocusResult;
/**
* Abstraction for camera hardware.
@@ -27,7 +30,8 @@ import io.fotoapparat.lens.FocusResult;
public interface CameraDevice extends CaptureOperator,
PreviewOperator, CapabilitiesOperator, OrientationOperator, ParametersOperator,
ConnectionOperator, SurfaceOperator, PreviewStreamOperator, RendererParametersOperator,
ExposureMeasurementOperator, AutoFocusOperator, AvailableLensPositionsProvider {
ExposureMeasurementOperator, AutoFocusOperator, AvailableLensPositionsProvider,
ZoomOperator {
@Override
void open(LensPosition lensPosition);
@@ -71,4 +75,7 @@ public interface CameraDevice extends CaptureOperator,
@Override
List<LensPosition> getAvailableLensPositions();
@Override
void setZoom(@FloatRange(from = 0f, to = 1f) float level);
}
@@ -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);
}
}
@@ -22,15 +22,18 @@ public class Capabilities {
private final Set<FocusMode> focusModes;
@NonNull
private final Set<Flash> flashModes;
private final boolean zoomSupported;
public Capabilities(@NonNull Set<Size> photoSizes,
@NonNull Set<Size> previewSizes,
@NonNull Set<FocusMode> focusModes,
@NonNull Set<Flash> flashModes) {
@NonNull Set<Flash> flashModes,
boolean zoomSupported) {
this.photoSizes = photoSizes;
this.previewSizes = previewSizes;
this.focusModes = focusModes;
this.flashModes = flashModes;
this.zoomSupported = zoomSupported;
}
/**
@@ -41,7 +44,8 @@ public class Capabilities {
Collections.<Size>emptySet(),
Collections.<Size>emptySet(),
Collections.<FocusMode>emptySet(),
Collections.<Flash>emptySet()
Collections.<Flash>emptySet(),
false
);
}
@@ -73,6 +77,13 @@ public class Capabilities {
return flashModes;
}
/**
* @return {@code true} if zoom feature is supported. {@code false} if it is not supported.
*/
public boolean isZoomSupported() {
return zoomSupported;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -80,7 +91,8 @@ public class Capabilities {
Capabilities that = (Capabilities) o;
return photoSizes.equals(that.photoSizes)
return zoomSupported == that.zoomSupported
&& photoSizes.equals(that.photoSizes)
&& previewSizes.equals(that.previewSizes)
&& focusModes.equals(that.focusModes)
&& flashModes.equals(that.flashModes);
@@ -93,6 +105,7 @@ public class Capabilities {
result = 31 * result + previewSizes.hashCode();
result = 31 * result + focusModes.hashCode();
result = 31 * result + flashModes.hashCode();
result = 31 * result + (zoomSupported ? 1 : 0);
return result;
}
@@ -103,6 +116,7 @@ public class Capabilities {
", previewSizes=" + previewSizes +
", focusModes=" + focusModes +
", flashModes=" + flashModes +
", zoomSupported=" + zoomSupported +
'}';
}
@@ -0,0 +1,17 @@
package io.fotoapparat.hardware.operators;
import android.support.annotation.FloatRange;
/**
* Modifies zoom level of the camera.
*/
public interface ZoomOperator {
/**
* Changes zoom level of the camera. Must be called only if zoom is supported.
*
* @param level normalized zoom level. Value in range [0..1].
*/
void setZoom(@FloatRange(from = 0f, to = 1f) float level);
}
@@ -1,22 +1,30 @@
package io.fotoapparat.hardware.v1;
import android.hardware.Camera;
import android.support.annotation.FloatRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.SurfaceView;
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,18 +40,25 @@ 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;
private int imageRotation;
@Nullable
private Capabilities cachedCapabilities = null;
@Nullable
private Camera.Parameters cachedZoomParameters = null;
public Camera1(Logger logger) {
this.capabilitiesFactory = new CapabilitiesFactory();
this.parametersConverter = new ParametersConverter();
@@ -51,6 +66,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 +79,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 +89,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 +130,9 @@ public class Camera1 implements CameraDevice {
public void close() {
recordMethod();
if (camera != null) {
cachedCapabilities = null;
if (isCameraOpened()) {
camera.release();
}
}
@@ -113,14 +141,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 +171,7 @@ public class Camera1 implements CameraDevice {
try {
trySetDisplaySurface(displaySurface);
} catch (IOException e) {
throw new CameraException(e);
throwOnFailSetDisplaySurface(displaySurface, e);
}
}
@@ -138,6 +179,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,21 +215,46 @@ public class Camera1 implements CameraDevice {
public void updateParameters(Parameters parameters) {
recordMethod();
Camera.Parameters cameraParameters = parametersConverter.convert(
parameters,
camera.getParameters()
parametersOperator().updateParameters(parameters);
cachedZoomParameters = null;
}
@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
public Capabilities getCapabilities() {
if (cachedCapabilities != null) {
return cachedCapabilities;
}
recordMethod();
return capabilitiesFactory.fromParameters(
Capabilities capabilities = capabilitiesFactory.fromParameters(
camera.getParameters()
);
cachedCapabilities = capabilities;
return capabilities;
}
private void trySetDisplaySurface(Object displaySurface) throws IOException {
@@ -232,25 +302,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 +335,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 +358,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
@@ -284,6 +372,31 @@ public class Camera1 implements CameraDevice {
return availableLensPositionsProvider.getAvailableLensPositions();
}
@Override
public void setZoom(@FloatRange(from = 0f, to = 1f) float level) {
try {
setZoomUnsafe(level);
} catch (Exception e) {
logFailedZoomUpdate(level, e);
}
}
private void setZoomUnsafe(@FloatRange(from = 0f, to = 1f) float level) {
if (cachedZoomParameters == null) {
cachedZoomParameters = camera.getParameters();
}
cachedZoomParameters.setZoom(
(int) (cachedZoomParameters.getMaxZoom() * level)
);
camera.setParameters(cachedZoomParameters);
}
private void logFailedZoomUpdate(float level, Exception e) {
logger.log("Unable to change zoom level to " + level + " e: " + e.getMessage());
}
private Size previewSize() {
Camera.Size previewSize = camera.getParameters().getPreviewSize();
@@ -300,6 +413,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);
}
@@ -29,7 +29,8 @@ public class CapabilitiesFactory {
extractPictureSizes(parameters),
extractPreviewSizes(parameters),
extractFocusModes(parameters),
extractFlashModes(parameters)
extractFlashModes(parameters),
parameters.isZoomSupported()
);
}
@@ -84,7 +85,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);
}
}
@@ -1,6 +1,7 @@
package io.fotoapparat.hardware.v2;
import android.os.Build;
import android.support.annotation.FloatRange;
import android.support.annotation.RequiresApi;
import java.util.List;
@@ -172,6 +173,11 @@ public class Camera2 implements CameraDevice {
return availableLensPositionsProvider.getAvailableLensPositions();
}
@Override
public void setZoom(@FloatRange(from = 0f, to = 1f) float level) {
throw new UnsupportedOperationException("Not implemented. We do not actively support Camera2 at the moment.");
}
private void recordMethod() {
Exception lastStacktrace = new Exception();
@@ -23,78 +23,79 @@ 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(),
false
);
}
@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;
@@ -1,5 +1,6 @@
package io.fotoapparat.parameter;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -14,6 +15,20 @@ public class Parameters {
private final Map<Type, Object> values = new HashMap<>();
/**
* @return single {@link Parameters} which is a result of adding up all parameters in the list
* using {@link Parameters#putAll(Parameters)}.
*/
public static Parameters combineParameters(Collection<Parameters> parametersList) {
Parameters result = new Parameters();
for (Parameters parameters : parametersList) {
result.putAll(parameters);
}
return result;
}
/**
* Puts value of given type, rewriting existing one (if any). Note that given value must be of
* the type specified by {@link Type}.
@@ -21,10 +36,12 @@ public class Parameters {
* @param type type of the parameter to store.
* @param value value of the parameter.
*/
public void putValue(Type type, Object value) {
public Parameters putValue(Type type, Object value) {
ensureType(type, value);
values.put(type, value);
return this;
}
private void ensureType(Type type, Object value) {
@@ -64,6 +81,36 @@ public class Parameters {
return result;
}
/**
* Puts all parameters which are in input to current set of parameters. If there are duplicates,
* they are overwritten.
*/
public void putAll(Parameters input) {
values.putAll(input.values);
}
@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
}
@@ -0,0 +1,69 @@
package io.fotoapparat.parameter.factory;
import android.support.annotation.NonNull;
import io.fotoapparat.hardware.Capabilities;
import io.fotoapparat.parameter.Flash;
import io.fotoapparat.parameter.FocusMode;
import io.fotoapparat.parameter.Parameters;
import io.fotoapparat.parameter.Size;
import io.fotoapparat.parameter.selector.SelectorFunction;
/**
* Functions which build {@link Parameters} from given {@link Capabilities} and selector functions.
*/
public class ParametersFactory {
/**
* @return new parameters by selecting picture size from given capabilities.
*/
public static Parameters selectPictureSize(@NonNull Capabilities capabilities,
@NonNull SelectorFunction<Size> selector) {
return new Parameters().putValue(
Parameters.Type.PICTURE_SIZE,
selector.select(
capabilities.supportedPictureSizes()
)
);
}
/**
* @return new parameters by selecting preview size from given capabilities.
*/
public static Parameters selectPreviewSize(@NonNull Capabilities capabilities,
@NonNull SelectorFunction<Size> selector) {
return new Parameters().putValue(
Parameters.Type.PREVIEW_SIZE,
selector.select(
capabilities.supportedPreviewSizes()
)
);
}
/**
* @return new parameters by selecting focus mode from given capabilities.
*/
public static Parameters selectFocusMode(@NonNull Capabilities capabilities,
@NonNull SelectorFunction<FocusMode> selector) {
return new Parameters().putValue(
Parameters.Type.FOCUS_MODE,
selector.select(
capabilities.supportedFocusModes()
)
);
}
/**
* @return new parameters by selecting flash mode from given capabilities.
*/
public static Parameters selectFlashMode(@NonNull Capabilities capabilities,
@NonNull SelectorFunction<Flash> selector) {
return new Parameters().putValue(
Parameters.Type.FLASH,
selector.select(
capabilities.supportedFlashModes()
)
);
}
}
@@ -7,10 +7,13 @@ import io.fotoapparat.parameter.Flash;
import io.fotoapparat.parameter.FocusMode;
import io.fotoapparat.parameter.Parameters;
import io.fotoapparat.parameter.Size;
import io.fotoapparat.parameter.factory.ParametersFactory;
import io.fotoapparat.parameter.selector.SelectorFunction;
import io.fotoapparat.parameter.selector.Selectors;
import static io.fotoapparat.parameter.Parameters.combineParameters;
import static io.fotoapparat.parameter.selector.AspectRatioSelectors.aspectRatio;
import static java.util.Arrays.asList;
/**
* Provides initial {@link Parameters} for {@link CameraDevice}.
@@ -38,49 +41,72 @@ public class InitialParametersProvider {
this.parametersValidator = parametersValidator;
}
/**
* @return function which selects a valid preview size based on current picture size.
*/
static SelectorFunction<Size> validPreviewSizeSelector(Size photoSize,
SelectorFunction<Size> original) {
return Selectors
.firstAvailable(
previewWithSameAspectRatio(photoSize, original),
original
);
}
private static SelectorFunction<Size> previewWithSameAspectRatio(Size photoSize,
SelectorFunction<Size> original) {
return aspectRatio(
photoSize.getAspectRatio(),
original
);
}
/**
* @return {@link Parameters} which will be used by {@link CameraDevice} on start-up.
*/
public Parameters initialParameters() {
Capabilities capabilities = capabilitiesOperator.getCapabilities();
Parameters parameters = new Parameters();
putPictureSize(capabilities, parameters);
putPreviewSize(capabilities, parameters);
putFocusMode(capabilities, parameters);
putFlash(capabilities, parameters);
Parameters parameters = combineParameters(asList(
pictureSizeParameters(capabilities),
previewSizeParameters(capabilities),
focusModeParameters(capabilities),
flashModeParameters(capabilities)
));
parametersValidator.validate(parameters);
return parameters;
}
private void putPreviewSize(Capabilities capabilities, Parameters parameters) {
Size photoSize = photoSize(capabilities);
parameters.putValue(
Parameters.Type.PREVIEW_SIZE,
Selectors
.firstAvailable(
previewWithSameAspectRatio(photoSize),
previewSizeSelector
)
.select(capabilities.supportedPreviewSizes())
private Parameters flashModeParameters(Capabilities capabilities) {
return ParametersFactory.selectFlashMode(
capabilities,
flashSelector
);
}
private SelectorFunction<Size> previewWithSameAspectRatio(Size photoSize) {
return aspectRatio(
photoSize.getAspectRatio(),
previewSizeSelector
private Parameters focusModeParameters(Capabilities capabilities) {
return ParametersFactory.selectFocusMode(
capabilities,
focusModeSelector
);
}
private void putPictureSize(Capabilities capabilities, Parameters parameters) {
parameters.putValue(
Parameters.Type.PICTURE_SIZE,
photoSize(capabilities)
private Parameters previewSizeParameters(Capabilities capabilities) {
return ParametersFactory.selectPreviewSize(
capabilities,
validPreviewSizeSelector(
photoSize(capabilities),
previewSizeSelector
)
);
}
private Parameters pictureSizeParameters(Capabilities capabilities) {
return ParametersFactory.selectPictureSize(
capabilities,
photoSizeSelector
);
}
@@ -90,22 +116,4 @@ public class InitialParametersProvider {
);
}
private void putFocusMode(Capabilities capabilities, Parameters parameters) {
parameters.putValue(
Parameters.Type.FOCUS_MODE,
focusModeSelector.select(
capabilities.supportedFocusModes()
)
);
}
private void putFlash(Capabilities capabilities, Parameters parameters) {
parameters.putValue(
Parameters.Type.FLASH,
flashSelector.select(
capabilities.supportedFlashModes()
)
);
}
}
@@ -0,0 +1,79 @@
package io.fotoapparat.parameter.update;
import android.support.annotation.Nullable;
import io.fotoapparat.parameter.Flash;
import io.fotoapparat.parameter.FocusMode;
import io.fotoapparat.parameter.selector.SelectorFunction;
/**
* Request for updating some of the parameters of the {@link io.fotoapparat.Fotoapparat}.
* <p>
* Use {@link UpdateRequest#builder()} to create a new instance.
* <p>
* Fields with {@code null} values are ignored and not updated.
*/
public class UpdateRequest {
/**
* Selects flash mode from list of available modes.
* <p>
* {@code null} if no update is required.
*/
@Nullable
public final SelectorFunction<Flash> flashSelector;
/**
* Selects focus mode from list of available modes.
* <p>
* {@code null} if no update is required.
*/
@Nullable
public final SelectorFunction<FocusMode> focusModeSelector;
private UpdateRequest(Builder builder) {
this.flashSelector = builder.flashSelector;
this.focusModeSelector = builder.focusModeSelector;
}
/**
* @return builder for {@link UpdateRequest}.
*/
public static Builder builder() {
return new Builder();
}
/**
* Builder for {@link UpdateRequest}.
*/
public static class Builder {
SelectorFunction<Flash> flashSelector = null;
SelectorFunction<FocusMode> focusModeSelector = null;
/**
* @param selector selects focus mode from list of available modes.
*/
public Builder focusMode(@Nullable SelectorFunction<FocusMode> selector) {
this.focusModeSelector = selector;
return this;
}
/**
* @param selector selects flash mode from list of available modes.
*/
public Builder flash(@Nullable SelectorFunction<Flash> selector) {
this.flashSelector = selector;
return this;
}
/**
* @return a new instance of {@link UpdateRequest} which uses values from current builder.
*/
public UpdateRequest build() {
return new UpdateRequest(this);
}
}
}
@@ -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;
}
}
@@ -0,0 +1,63 @@
package io.fotoapparat.routine.parameter;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import io.fotoapparat.hardware.CameraDevice;
import io.fotoapparat.hardware.Capabilities;
import io.fotoapparat.parameter.Parameters;
import io.fotoapparat.parameter.factory.ParametersFactory;
import io.fotoapparat.parameter.selector.SelectorFunction;
import io.fotoapparat.parameter.selector.Selectors;
import io.fotoapparat.parameter.update.UpdateRequest;
import static io.fotoapparat.parameter.Parameters.combineParameters;
import static java.util.Arrays.asList;
/**
* Updates {@link CameraDevice} parameters.
*/
public class UpdateParametersRoutine {
private final CameraDevice cameraDevice;
public UpdateParametersRoutine(CameraDevice cameraDevice) {
this.cameraDevice = cameraDevice;
}
/**
* Updates {@link CameraDevice} parameters.
*/
public void updateParameters(@NonNull UpdateRequest request) {
Capabilities capabilities = cameraDevice.getCapabilities();
cameraDevice.updateParameters(
combineParameters(asList(
flashModeParameters(request, capabilities),
focusModeParameters(request, capabilities)
))
);
}
private Parameters focusModeParameters(@NonNull UpdateRequest request, Capabilities capabilities) {
return ParametersFactory.selectFocusMode(
capabilities,
optional(request.focusModeSelector)
);
}
private Parameters flashModeParameters(@NonNull UpdateRequest request, Capabilities capabilities) {
return ParametersFactory.selectFlashMode(
capabilities,
optional(request.flashSelector)
);
}
@NonNull
private <T> SelectorFunction<T> optional(@Nullable SelectorFunction<T> selector) {
return selector != null
? selector
: Selectors.<T>nothing();
}
}
@@ -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
}
}
}
@@ -0,0 +1,48 @@
package io.fotoapparat.routine.zoom;
import android.support.annotation.FloatRange;
import io.fotoapparat.hardware.CameraDevice;
/**
* Updates zoom level of the camera. If zoom is not supported - does nothing.
*/
public class UpdateZoomLevelRoutine {
private final CameraDevice cameraDevice;
public UpdateZoomLevelRoutine(CameraDevice cameraDevice) {
this.cameraDevice = cameraDevice;
}
/**
* Updates zoom level of the camera. If zoom is not supported - does nothing.
*
* @param zoomLevel zoom level of the camera. Value between 0 and 1.
*/
public void updateZoomLevel(@FloatRange(from = 0f, to = 1f) float zoomLevel) {
ensureInBounds(zoomLevel);
if (cameraDevice.getCapabilities().isZoomSupported()) {
cameraDevice.setZoom(zoomLevel);
}
}
private void ensureInBounds(float zoomLevel) {
if (zoomLevel < 0f || zoomLevel > 1f) {
throw new LevelOutOfRangeException(zoomLevel);
}
}
/**
* Thrown when zoom level is outside of [0..1] range.
*/
static class LevelOutOfRangeException extends RuntimeException {
public LevelOutOfRangeException(float zoomLevel) {
super(zoomLevel + " is out of range [0..1]");
}
}
}
@@ -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);
@@ -75,14 +77,30 @@ class TextureRendererView extends FrameLayout implements CameraRenderer {
}
@Override
public void attachCamera(CameraDevice camera) {
awaitSurfaceTexture();
updateLayout(camera);
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
camera.setDisplaySurface(textureView);
textureLatch.countDown();
}
private void updateLayout(CameraDevice camera) {
@Override
public void setScaleType(ScaleType scaleType) {
this.scaleType = scaleType;
}
@Override
public void attachCamera(CameraDevice camera) {
try {
awaitSurfaceTexture();
updateLayout(camera);
camera.setDisplaySurface(textureView);
} catch (InterruptedException e) {
// Do nothing
}
}
private void updateLayout(final CameraDevice camera) {
final Size previewSize = toPreviewSize(
camera.getRendererParameters()
);
@@ -103,25 +121,53 @@ class TextureRendererView extends FrameLayout implements CameraRenderer {
: rendererParameters.previewSize.flip();
}
private void awaitSurfaceTexture() {
private void awaitSurfaceTexture() throws InterruptedException {
if (surfaceTexture != null) {
return;
}
try {
textureLatch.await();
} catch (InterruptedException e) {
// Do nothing
textureLatch.await();
if (surfaceTexture == null) {
throw new InterruptedException("No surface became available.");
}
}
@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
@@ -157,7 +203,6 @@ class TextureRendererView extends FrameLayout implements CameraRenderer {
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
// Do nothing
return true;
}
@@ -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);
}
}
@@ -9,16 +9,21 @@ import org.mockito.junit.MockitoJUnitRunner;
import io.fotoapparat.hardware.Capabilities;
import io.fotoapparat.parameter.provider.CapabilitiesProvider;
import io.fotoapparat.parameter.update.UpdateRequest;
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.parameter.UpdateParametersRoutine;
import io.fotoapparat.routine.picture.TakePictureRoutine;
import io.fotoapparat.routine.zoom.UpdateZoomLevelRoutine;
import io.fotoapparat.test.ImmediateExecutor;
import static io.fotoapparat.test.TestUtils.immediateFuture;
@@ -37,6 +42,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()
@@ -59,6 +71,10 @@ public class FotoapparatTest {
AutoFocusRoutine autoFocusRoutine;
@Mock
CheckAvailabilityRoutine checkAvailabilityRoutine;
@Mock
UpdateParametersRoutine updateParametersRoutine;
@Mock
UpdateZoomLevelRoutine updateZoomLevelRoutine;
Fotoapparat testee;
@@ -73,6 +89,8 @@ public class FotoapparatTest {
takePictureRoutine,
autoFocusRoutine,
checkAvailabilityRoutine,
updateParametersRoutine,
updateZoomLevelRoutine,
new ImmediateExecutor()
);
}
@@ -196,7 +214,7 @@ public class FotoapparatTest {
testee.autoFocus();
// Then
verify(autoFocusRoutine).run();
verify(autoFocusRoutine).autoFocus();
}
@Test(expected = IllegalStateException.class)
@@ -208,6 +226,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
@@ -245,4 +291,53 @@ public class FotoapparatTest {
assertFalse(result);
}
@Test
public void updateParameters() throws Exception {
// Given
UpdateRequest updateRequest = UpdateRequest.builder()
.build();
testee.start();
// When
testee.updateParameters(updateRequest);
// Then
verify(updateParametersRoutine).updateParameters(updateRequest);
}
@Test(expected = IllegalStateException.class)
public void updateParameters_NotStartedYet() throws Exception {
// Given
UpdateRequest updateRequest = UpdateRequest.builder()
.build();
// When
testee.updateParameters(updateRequest);
// Then
// Expect exception
}
@Test
public void setZoom() throws Exception {
// Given
testee.start();
// When
testee.setZoom(0.5f);
// Then
verify(updateZoomLevelRoutine).updateZoomLevel(0.5f);
}
@Test(expected = IllegalStateException.class)
public void setZoom_NotStartedYet() throws Exception {
// When
testee.setZoom(1f);
// Then
// Expect exception
}
}
@@ -1,5 +1,7 @@
package io.fotoapparat.hardware;
import junit.framework.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -9,6 +11,8 @@ import io.fotoapparat.parameter.FocusMode;
import io.fotoapparat.parameter.Parameters;
import io.fotoapparat.parameter.Size;
import static io.fotoapparat.parameter.Parameters.Type.FOCUS_MODE;
import static io.fotoapparat.parameter.Parameters.Type.PICTURE_SIZE;
import static io.fotoapparat.test.TestUtils.asSet;
import static java.util.Collections.emptySet;
import static org.junit.Assert.assertEquals;
@@ -110,4 +114,64 @@ public class ParametersTest {
);
}
@Test
public void putAll() throws Exception {
// Given
Parameters input = new Parameters();
input.putValue(Parameters.Type.FOCUS_MODE, FocusMode.AUTO);
input.putValue(Parameters.Type.PICTURE_SIZE, new Size(100, 100));
// When
testee.putAll(input);
// Then
assertEquals(
input,
testee
);
}
@Test
public void putAll_KeepOldValues() throws Exception {
// Given
Parameters input = new Parameters();
input.putValue(Parameters.Type.FOCUS_MODE, FocusMode.AUTO);
testee.putValue(Parameters.Type.PICTURE_SIZE, new Size(100, 100));
// When
testee.putAll(input);
// Then
Parameters expected = new Parameters();
expected.putValue(Parameters.Type.FOCUS_MODE, FocusMode.AUTO);
expected.putValue(Parameters.Type.PICTURE_SIZE, new Size(100, 100));
assertEquals(
expected,
testee
);
}
@Test
public void combineParameters() throws Exception {
// Given
Parameters parametersA = new Parameters().putValue(PICTURE_SIZE, new Size(100, 100));
Parameters parametersB = new Parameters().putValue(FOCUS_MODE, FocusMode.AUTO);
// When
Parameters result = Parameters.combineParameters(asSet(
parametersA,
parametersB
));
// Then
Assert.assertEquals(
new Parameters()
.putValue(PICTURE_SIZE, new Size(100, 100))
.putValue(FOCUS_MODE, FocusMode.AUTO),
result
);
}
}
@@ -21,6 +21,7 @@ import static io.fotoapparat.test.TestUtils.asSet;
import static java.util.Arrays.asList;
import static java.util.Collections.singleton;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import static org.mockito.BDDMockito.given;
@RunWith(MockitoJUnitRunner.class)
@@ -44,6 +45,8 @@ public class CapabilitiesFactoryTest {
.willReturn(Collections.<Camera.Size>emptyList());
given(parameters.getSupportedPreviewSizes())
.willReturn(Collections.<Camera.Size>emptyList());
given(parameters.isZoomSupported())
.willReturn(false);
testee = new CapabilitiesFactory();
}
@@ -181,6 +184,19 @@ public class CapabilitiesFactoryTest {
);
}
@Test
public void zoomSupported() throws Exception {
// Given
given(parameters.isZoomSupported())
.willReturn(true);
// When
Capabilities capabilities = testee.fromParameters(parameters);
// Then
assertTrue(capabilities.isZoomSupported());
}
@NonNull
private Camera.Size makeSize(int width, int height) {
Camera.Size size = camera.new Size(0, 0);
@@ -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);
}
}
@@ -156,7 +156,8 @@ public class Camera2Test {
Collections.<Size>emptySet(),
Collections.<Size>emptySet(),
singleton(FocusMode.MACRO),
Collections.<Flash>emptySet()
Collections.<Flash>emptySet(),
false
);
given(capabilitiesOperator.getCapabilities())
.willReturn(capabilities);
@@ -0,0 +1,78 @@
package io.fotoapparat.parameter.factory;
import org.junit.Test;
import io.fotoapparat.hardware.Capabilities;
import io.fotoapparat.parameter.Flash;
import io.fotoapparat.parameter.FocusMode;
import io.fotoapparat.parameter.Parameters;
import io.fotoapparat.parameter.Size;
import static io.fotoapparat.util.TestSelectors.select;
import static junit.framework.Assert.assertEquals;
public class ParametersFactoryTest {
static final Capabilities CAPABILITIES = Capabilities.empty();
@Test
public void selectPictureSize() throws Exception {
// Given
Size size = new Size(100, 100);
// When
Parameters result = ParametersFactory.selectPictureSize(CAPABILITIES, select(size));
// Then
assertEquals(
new Parameters().putValue(Parameters.Type.PICTURE_SIZE, size),
result
);
}
@Test
public void selectPreviewSize() throws Exception {
// Given
Size size = new Size(100, 100);
// When
Parameters result = ParametersFactory.selectPreviewSize(CAPABILITIES, select(size));
// Then
assertEquals(
new Parameters().putValue(Parameters.Type.PREVIEW_SIZE, size),
result
);
}
@Test
public void selectFocusMode() throws Exception {
// Given
FocusMode focusMode = FocusMode.AUTO;
// When
Parameters result = ParametersFactory.selectFocusMode(CAPABILITIES, select(focusMode));
// Then
assertEquals(
new Parameters().putValue(Parameters.Type.FOCUS_MODE, focusMode),
result
);
}
@Test
public void selectFlashMode() throws Exception {
// Given
Flash flash = Flash.AUTO;
// When
Parameters result = ParametersFactory.selectFlashMode(CAPABILITIES, select(flash));
// Then
assertEquals(
new Parameters().putValue(Parameters.Type.FLASH, flash),
result
);
}
}
@@ -1,6 +1,5 @@
package io.fotoapparat.parameter.provider;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -8,18 +7,19 @@ import org.mockito.junit.MockitoJUnitRunner;
import java.util.Set;
import io.fotoapparat.hardware.CameraDevice;
import io.fotoapparat.hardware.Capabilities;
import io.fotoapparat.hardware.operators.CapabilitiesOperator;
import io.fotoapparat.parameter.Flash;
import io.fotoapparat.parameter.FocusMode;
import io.fotoapparat.parameter.Parameters;
import io.fotoapparat.parameter.Size;
import io.fotoapparat.parameter.selector.SelectorFunction;
import io.fotoapparat.parameter.selector.SizeSelectors;
import static io.fotoapparat.parameter.selector.FlashSelectors.torch;
import static io.fotoapparat.parameter.selector.FocusModeSelectors.autoFocus;
import static io.fotoapparat.test.TestUtils.asSet;
import static java.util.Collections.singleton;
import static io.fotoapparat.util.TestSelectors.select;
import static junit.framework.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.verify;
@@ -30,148 +30,100 @@ public class InitialParametersProviderTest {
static final Size PREVIEW_SIZE = new Size(2000, 1500);
static final Size PREVIEW_SIZE_WRONG_ASPECT_RATIO = new Size(1000, 1000);
static final Set<FocusMode> FOCUS_MODES = asSet(FocusMode.FIXED);
static final Set<Flash> FLASH = asSet(Flash.AUTO_RED_EYE);
static final Set<Size> PHOTO_SIZES = asSet(PHOTO_SIZE);
static final Set<Size> VALID_PREVIEW_SIZES = asSet(PREVIEW_SIZE);
static final Set<Size> ALL_PREVIEW_SIZES = asSet(
PREVIEW_SIZE,
PREVIEW_SIZE_WRONG_ASPECT_RATIO
);
@Mock
CameraDevice cameraDevice;
@Mock
SelectorFunction<Size> photoSizeSelector;
@Mock
SelectorFunction<Size> previewSizeSelector;
@Mock
SelectorFunction<FocusMode> focusModeSelector;
@Mock
SelectorFunction<Flash> flashModeSelector;
@Mock
InitialParametersValidator initialParametersValidator;
@Mock
CapabilitiesOperator capabilitiesOperator;
InitialParametersProvider testee;
@Test
public void validPreviewSizeSelector_WithValidAspectRatio() throws Exception {
// When
Size result = InitialParametersProvider
.validPreviewSizeSelector(
PHOTO_SIZE,
select(PREVIEW_SIZE)
)
.select(ALL_PREVIEW_SIZES);
@Before
public void setUp() throws Exception {
testee = new InitialParametersProvider(
cameraDevice,
photoSizeSelector,
previewSizeSelector,
focusModeSelector,
flashModeSelector,
// Then
assertEquals(
PREVIEW_SIZE,
result
);
}
@Test
public void validPreviewSizeSelector_NoPreviewSizeWithSameAspectRatio() throws Exception {
// Given
Size photoSize = new Size(10000, 100);
// When
Size result = InitialParametersProvider
.validPreviewSizeSelector(
photoSize,
select(PREVIEW_SIZE)
)
.select(ALL_PREVIEW_SIZES);
// Then
assertEquals(
PREVIEW_SIZE,
result
);
}
@Test
public void initialParameters() throws Exception {
// Given
given(capabilitiesOperator.getCapabilities())
.willReturn(new Capabilities(
asSet(PHOTO_SIZE),
ALL_PREVIEW_SIZES,
asSet(FocusMode.AUTO),
asSet(Flash.TORCH),
true
));
InitialParametersProvider testee = new InitialParametersProvider(
capabilitiesOperator,
SizeSelectors.biggestSize(),
SizeSelectors.biggestSize(),
autoFocus(),
torch(),
initialParametersValidator
);
given(cameraDevice.getCapabilities())
.willReturn(new Capabilities(
PHOTO_SIZES,
ALL_PREVIEW_SIZES,
FOCUS_MODES,
FLASH
));
given(photoSizeSelector.select(PHOTO_SIZES))
.willReturn(PHOTO_SIZE);
given(previewSizeSelector.select(VALID_PREVIEW_SIZES))
.willReturn(PREVIEW_SIZE);
given(focusModeSelector.select(FOCUS_MODES))
.willReturn(FocusMode.FIXED);
given(flashModeSelector.select(FLASH))
.willReturn(Flash.AUTO_RED_EYE);
}
@Test
public void selectFocusMode() throws Exception {
// When
Parameters parameters = testee.initialParameters();
// Then
assertEquals(
FocusMode.FIXED,
parameters.getValue(Parameters.Type.FOCUS_MODE)
new Parameters()
.putValue(
Parameters.Type.PICTURE_SIZE,
PHOTO_SIZE
)
.putValue(
Parameters.Type.PREVIEW_SIZE,
PREVIEW_SIZE
)
.putValue(
Parameters.Type.FOCUS_MODE,
FocusMode.AUTO
)
.putValue(
Parameters.Type.FLASH,
Flash.TORCH
),
parameters
);
verify(initialParametersValidator).validate(parameters);
}
@Test
public void selectFlashMode() throws Exception {
// When
Parameters parameters = testee.initialParameters();
// Then
assertEquals(
Flash.AUTO_RED_EYE,
parameters.getValue(Parameters.Type.FLASH)
);
}
@Test
public void selectPhotoSize() throws Exception {
// When
Parameters parameters = testee.initialParameters();
// Then
assertEquals(
PHOTO_SIZE,
parameters.getValue(Parameters.Type.PICTURE_SIZE)
);
}
@Test
public void selectPreviewSize_WithValidAspectRatio() throws Exception {
// When
Parameters parameters = testee.initialParameters();
// Then
verify(previewSizeSelector).select(VALID_PREVIEW_SIZES);
assertEquals(
PREVIEW_SIZE,
parameters.getValue(Parameters.Type.PREVIEW_SIZE)
);
}
@Test
public void selectPreviewSize_SameAspectRatioNotAvailable() throws Exception {
// Given
Size photoSize = new Size(10000, 100);
Set<Size> photoSizes = singleton(photoSize);
given(photoSizeSelector.select(photoSizes))
.willReturn(photoSize);
given(cameraDevice.getCapabilities())
.willReturn(new Capabilities(
photoSizes,
ALL_PREVIEW_SIZES,
FOCUS_MODES,
FLASH
));
given(previewSizeSelector.select(ALL_PREVIEW_SIZES))
.willReturn(PREVIEW_SIZE);
// When
Parameters parameters = testee.initialParameters();
// Then
assertEquals(
PREVIEW_SIZE,
parameters.getValue(Parameters.Type.PREVIEW_SIZE)
);
}
@Test
public void parameterValidation() throws Exception {
// Given
// When
testee.initialParameters();
// Then
verify(initialParametersValidator).validate(any(Parameters.class));
}
}
@@ -0,0 +1,35 @@
package io.fotoapparat.parameter.update;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import io.fotoapparat.parameter.Flash;
import io.fotoapparat.parameter.FocusMode;
import io.fotoapparat.parameter.selector.SelectorFunction;
import static org.junit.Assert.assertSame;
@RunWith(MockitoJUnitRunner.class)
public class UpdateRequestTest {
@Mock
SelectorFunction<Flash> flashSelector;
@Mock
SelectorFunction<FocusMode> focusModeSelector;
@Test
public void build() throws Exception {
// When
UpdateRequest updateRequest = UpdateRequest.builder()
.flash(flashSelector)
.focusMode(focusModeSelector)
.build();
// Then
assertSame(flashSelector, updateRequest.flashSelector);
assertSame(focusModeSelector, updateRequest.focusModeSelector);
}
}
@@ -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);
}
}
@@ -0,0 +1,72 @@
package io.fotoapparat.routine.parameter;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Collections;
import io.fotoapparat.hardware.CameraDevice;
import io.fotoapparat.hardware.Capabilities;
import io.fotoapparat.parameter.Flash;
import io.fotoapparat.parameter.FocusMode;
import io.fotoapparat.parameter.Parameters;
import io.fotoapparat.parameter.Size;
import io.fotoapparat.parameter.update.UpdateRequest;
import static io.fotoapparat.parameter.selector.FlashSelectors.torch;
import static io.fotoapparat.parameter.selector.FocusModeSelectors.autoFocus;
import static io.fotoapparat.test.TestUtils.asSet;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.verify;
@RunWith(MockitoJUnitRunner.class)
public class UpdateParametersRoutineTest {
@Mock
CameraDevice cameraDevice;
@InjectMocks
UpdateParametersRoutine testee;
@Before
public void setUp() throws Exception {
given(cameraDevice.getCapabilities())
.willReturn(new Capabilities(
Collections.<Size>emptySet(),
Collections.<Size>emptySet(),
asSet(FocusMode.AUTO),
asSet(Flash.TORCH),
false
));
}
@Test
public void updateParameters() throws Exception {
// Given
UpdateRequest request = UpdateRequest.builder()
.flash(torch())
.focusMode(autoFocus())
.build();
// When
testee.updateParameters(request);
// Then
verify(cameraDevice).updateParameters(
new Parameters()
.putValue(
Parameters.Type.FLASH,
Flash.TORCH
)
.putValue(
Parameters.Type.FOCUS_MODE,
FocusMode.AUTO
)
);
}
}
@@ -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);
}
}
@@ -0,0 +1,95 @@
package io.fotoapparat.routine.zoom;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Collections;
import io.fotoapparat.hardware.CameraDevice;
import io.fotoapparat.hardware.Capabilities;
import io.fotoapparat.parameter.Flash;
import io.fotoapparat.parameter.FocusMode;
import io.fotoapparat.parameter.Size;
import io.fotoapparat.routine.zoom.UpdateZoomLevelRoutine.LevelOutOfRangeException;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@RunWith(MockitoJUnitRunner.class)
public class UpdateZoomLevelRoutineTest {
@Mock
CameraDevice cameraDevice;
@InjectMocks
UpdateZoomLevelRoutine testee;
@SuppressWarnings("Range")
@Test(expected = LevelOutOfRangeException.class)
public void outOfRange_Higher() throws Exception {
// When
testee.updateZoomLevel(1.1f);
// Then
// Expect exception
}
@SuppressWarnings("Range")
@Test(expected = LevelOutOfRangeException.class)
public void outOfRange_Lower() throws Exception {
// When
testee.updateZoomLevel(-0.1f);
// Then
// Expect exception
}
@Test
public void updateZoomLevel() throws Exception {
// Given
givenZoomSupported();
// When
testee.updateZoomLevel(0.5f);
// Then
verify(cameraDevice).setZoom(0.5f);
}
@Test
public void zoomNotSupported() throws Exception {
// Given
givenZoomNotSupported();
// When
testee.updateZoomLevel(0.5f);
// Then
verify(cameraDevice, never()).setZoom(anyFloat());
}
private void givenZoomNotSupported() {
givenZoom(false);
}
private void givenZoomSupported() {
givenZoom(true);
}
private void givenZoom(boolean supported) {
given(cameraDevice.getCapabilities())
.willReturn(new Capabilities(
Collections.<Size>emptySet(),
Collections.<Size>emptySet(),
Collections.<FocusMode>emptySet(),
Collections.<Flash>emptySet(),
supported
));
}
}
@@ -28,7 +28,8 @@ public class GetCapabilitiesTaskTest {
Collections.singleton(new Size(1400, 1080)),
Collections.singleton(new Size(1400, 1080)),
Collections.singleton(FocusMode.CONTINUOUS_FOCUS),
Collections.singleton(Flash.OFF)
Collections.singleton(Flash.OFF),
false
);
@Mock
@@ -0,0 +1,24 @@
package io.fotoapparat.util;
import java.util.Collection;
import io.fotoapparat.parameter.selector.SelectorFunction;
/**
* Selectors which are convenient for tests.
*/
public class TestSelectors {
/**
* @return selector which always returns given object as a result of selection.
*/
public static <T> SelectorFunction<T> select(final T object) {
return new SelectorFunction<T>() {
@Override
public T select(Collection<T> items) {
return object;
}
};
}
}
+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
@@ -3,14 +3,22 @@ package io.fotoapparat.sample;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.SwitchCompat;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.SeekBar;
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.parameter.update.UpdateRequest;
import io.fotoapparat.photo.BitmapPhoto;
import io.fotoapparat.preview.Frame;
import io.fotoapparat.preview.FrameProcessor;
@@ -58,24 +66,77 @@ public class MainActivity extends AppCompatActivity {
permissionsDelegate.requestCameraPermission();
}
setupFotoapparat();
takePictureOnClick();
focusOnLongClick();
switchCameraOnClick();
toggleTorchOnSwitch();
zoomSeekBar();
}
private void setupFotoapparat() {
frontFotoapparat = createFotoapparat(LensPosition.FRONT);
backFotoapparat = createFotoapparat(LensPosition.BACK);
fotoapparatSwitcher = FotoapparatSwitcher.withDefault(backFotoapparat);
}
cameraView.setOnClickListener(new View.OnClickListener() {
private void zoomSeekBar() {
SeekBar seekBar = (SeekBar) findViewById(R.id.zoomSeekBar);
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onClick(View v) {
takePicture();
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
fotoapparatSwitcher
.getCurrentFotoapparat()
.setZoom(progress / (float) seekBar.getMax());
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// Do nothing
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// Do nothing
}
});
}
private void toggleTorchOnSwitch() {
SwitchCompat torchSwitch = (SwitchCompat) findViewById(R.id.torchSwitch);
torchSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
fotoapparatSwitcher
.getCurrentFotoapparat()
.updateParameters(
UpdateRequest.builder()
.flash(
isChecked
? torch()
: off()
)
.build()
);
}
});
}
private void switchCameraOnClick() {
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 +144,26 @@ public class MainActivity extends AppCompatActivity {
});
}
private void focusOnLongClick() {
cameraView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
fotoapparatSwitcher.getCurrentFotoapparat().autoFocus();
return true;
}
});
}
private void takePictureOnClick() {
cameraView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
takePicture();
}
});
}
private boolean canSwitchCameras() {
return frontFotoapparat.isAvailable() == backFotoapparat.isAvailable();
}
@@ -91,6 +172,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 +191,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();
}
@@ -39,4 +39,19 @@
android:layout_margin="8dp"
android:text="Switch camera"/>
<android.support.v7.widget.SwitchCompat
android:id="@+id/torchSwitch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|left"
android:layout_margin="8dp"/>
<SeekBar
android:id="@+id/zoomSeekBar"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginBottom="8dp"
android:max="30"/>
</FrameLayout>
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