Compare commits
67 Commits
slow_frames
...
2.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 2513e42830 | |||
| 4ed72421aa | |||
| dddd81600a | |||
| 2c80a903c1 | |||
| 3f6ecbd37e | |||
| d9114d4824 | |||
| 1768d73e59 | |||
| 51aa9eb9dc | |||
| 1cfbd2216f | |||
| bde4c27f8b | |||
| f4b60a445a | |||
| 64186ec825 | |||
| 96f9add77d | |||
| b6a6f56e38 | |||
| a694830c1c | |||
| 352a96a13c | |||
| 33c5054150 | |||
| ba02497fbd | |||
| 95196e046b | |||
| cb00abc810 | |||
| 5e9a6afff7 | |||
| 75d20c2f37 | |||
| d0da61bfb8 | |||
| 92f099500d | |||
| 15b4f35555 | |||
| c1b293da58 | |||
| 09cca52111 | |||
| 9d6f4cb9e8 | |||
| 2ea813a257 | |||
| 8dbe53d261 | |||
| 4bd05b679f | |||
| 01dc60df24 | |||
| 2b05601405 | |||
| 1ca2462f07 | |||
| e553699335 | |||
| 1d6d2d0977 | |||
| 94268a339a | |||
| 03170f6b26 | |||
| fe719e8bf3 | |||
| d7db6fa196 | |||
| 29cc23e047 | |||
| 89a172dbb1 | |||
| e4acda6aea | |||
| 3fc55404df | |||
| 16fb6bd94c | |||
| df59b3996e | |||
| b8e43e836a | |||
| 6e15327f6c | |||
| 6aef37b3f8 | |||
| 21e4b89bf7 | |||
| e3f0aa7421 | |||
| 35062675d9 | |||
| aff03c17c8 | |||
| b4e6db0a04 | |||
| 7517702165 | |||
| 56d32eee60 | |||
| 132736e4f7 | |||
| 2ac387d6ff | |||
| f4ff8b91b9 | |||
| 7b047501a6 | |||
| 80f43fcf63 | |||
| b7417650c6 | |||
| e7e264f522 | |||
| e501d0d25f | |||
| 39765cdb07 | |||
| 71480f4fa8 | |||
| a6028d56f3 |
+6
-2
@@ -5,9 +5,13 @@ android:
|
||||
components:
|
||||
- tools
|
||||
- platform-tools
|
||||
- build-tools-25.0.2
|
||||
- android-25
|
||||
- tools
|
||||
- build-tools-27.0.3
|
||||
- android-27
|
||||
- extra-android-m2repository
|
||||
|
||||
before_install:
|
||||
- yes | sdkmanager "platforms;android-27"
|
||||
|
||||
script:
|
||||
- ./gradlew build test
|
||||
|
||||
@@ -12,22 +12,23 @@ What it provides:
|
||||
- Simple yet powerful parameters customization.
|
||||
- Standalone custom `CameraView` which can be integrated into any `Activity`.
|
||||
- Fixes and workarounds for device-specific problems.
|
||||
- Both Kotlin and Java friendly configurations.
|
||||
- Last, but not least, non 0% test coverage.
|
||||
|
||||
|
||||
Taking picture becomes as simple as:
|
||||
|
||||
```java
|
||||
Fotoapparat fotoapparat = Fotoapparat
|
||||
.with(context)
|
||||
.into(cameraView)
|
||||
.build();
|
||||
|
||||
fotoapparat.start();
|
||||
```kotlin
|
||||
val fotoapparat = Fotoapparat(
|
||||
context = this,
|
||||
view = cameraView
|
||||
)
|
||||
|
||||
fotoapparat.start()
|
||||
|
||||
fotoapparat
|
||||
.takePicture()
|
||||
.saveToFile(someFile);
|
||||
.saveToFile(someFile)
|
||||
```
|
||||
|
||||
## How it works
|
||||
@@ -45,48 +46,40 @@ Add `CameraView` to your layout
|
||||
|
||||
### Step Two
|
||||
|
||||
Configure `Fotoapparat` instance
|
||||
Configure `Fotoapparat` instance.
|
||||
|
||||
```kotlin
|
||||
Fotoapparat(
|
||||
context = this,
|
||||
view = cameraView, // view which will draw the camera preview
|
||||
scaleType = ScaleType.CenterCrop, // (optional) we want the preview to fill the view
|
||||
lensPosition = back(), // (optional) we want back camera
|
||||
cameraConfiguration = configuration, // (optional) define an advanced configuration
|
||||
logger = loggers( // (optional) we want to log camera events in 2 places at once
|
||||
logcat(), // ... in logcat
|
||||
fileLogger(this) // ... and to file
|
||||
),
|
||||
cameraErrorCallback = { error -> } // (optional) log fatal errors
|
||||
)
|
||||
```
|
||||
Check the [wiki for the `configuration` options e.g. change iso](https://github.com/Fotoapparat/Fotoapparat/wiki/Configuration-Kotlin)
|
||||
|
||||
Are you using Java only? See our [wiki for the java-friendly configuration](https://github.com/Fotoapparat/Fotoapparat/wiki/Configuration-Java).
|
||||
|
||||
```java
|
||||
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
|
||||
continuousFocus(),
|
||||
autoFocus(), // in case if continuous focus is not available on device, auto focus will be used
|
||||
fixed() // if even auto focus is not available - fixed focus mode will be used
|
||||
))
|
||||
.flash(firstAvailable( // (optional) similar to how it is done for focus mode, this time for flash
|
||||
autoRedEye(),
|
||||
autoFlash(),
|
||||
torch()
|
||||
))
|
||||
.frameProcessor(myFrameProcessor) // (optional) receives each frame from preview stream
|
||||
.logger(loggers( // (optional) we want to log camera events in 2 places at once
|
||||
logcat(), // ... in logcat
|
||||
fileLogger(this) // ... and to file
|
||||
))
|
||||
.build();
|
||||
```
|
||||
|
||||
### Step Three
|
||||
|
||||
Call `start()` and `stop()`. No rocket science here.
|
||||
|
||||
```java
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
fotoapparat.start();
|
||||
```kotlin
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
fotoapparat.start()
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
fotoapparat.stop();
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
fotoapparat.stop()
|
||||
}
|
||||
```
|
||||
|
||||
@@ -94,60 +87,74 @@ protected void onStop() {
|
||||
|
||||
Finally we are ready to take picture. You have various options.
|
||||
|
||||
```java
|
||||
PhotoResult photoResult = fotoapparat.takePicture();
|
||||
```kotlin
|
||||
val photoResult = fotoapparat.takePicture()
|
||||
|
||||
// Asynchronously saves photo to file
|
||||
photoResult.saveToFile(someFile);
|
||||
photoResult.saveToFile(someFile)
|
||||
|
||||
// Asynchronously converts photo to bitmap and returns result on main thread
|
||||
photoResult
|
||||
.toBitmap()
|
||||
.whenAvailable(new PendingResult.Callback<BitmapPhoto>() {
|
||||
@Override
|
||||
public void onResult(BitmapPhoto result) {
|
||||
ImageView imageView = (ImageView) findViewById(R.id.result);
|
||||
|
||||
imageView.setImageBitmap(result.bitmap);
|
||||
imageView.setRotation(-result.rotationDegrees);
|
||||
}
|
||||
});
|
||||
.whenAvailable { bitmapPhoto ->
|
||||
val imageView = (ImageView) findViewById(R.id.result)
|
||||
|
||||
imageView.setImageBitmap(bitmapPhoto.bitmap)
|
||||
imageView.setRotation(-bitmapPhoto.rotationDegrees)
|
||||
}
|
||||
|
||||
// Of course you can also get a photo in a blocking way. Do not do it on main thread though.
|
||||
BitmapPhoto result = photoResult.toBitmap().await();
|
||||
val result = photoResult.toBitmap().await()
|
||||
|
||||
// Convert asynchronous events to RxJava 1.x/2.x types. See /fotoapparat-adapters/ module
|
||||
// Convert asynchronous events to RxJava 1.x/2.x types.
|
||||
// See /fotoapparat-adapters/ module
|
||||
photoResult
|
||||
.toBitmap()
|
||||
.adapt(SingleAdapter.<BitmapPhoto>toSingle())
|
||||
.subscribe(bitmapPhoto -> {
|
||||
.toSingle()
|
||||
.subscribe { bitmapPhoto ->
|
||||
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## 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()
|
||||
```kotlin
|
||||
fotoapparat.updateConfiguration(
|
||||
UpdateConfiguration(
|
||||
flashMode = if (isChecked) torch() else off()
|
||||
// ...
|
||||
// all the parameters available in CameraConfiguration
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
Or alternatively you may provide updates on an existing full configuration.
|
||||
|
||||
```kotlin
|
||||
val configuration = CameraConfiguration(
|
||||
// A full configuration
|
||||
// ...
|
||||
)
|
||||
|
||||
fotoapparat.updateConfiguration(
|
||||
configuration.copy(
|
||||
flashMode = if (isChecked) torch() else off()
|
||||
// all the parameters available in CameraConfiguration
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
## Switch cameras
|
||||
|
||||
In order to switch between multiple instances of Fotoapparat, for example an instance using the device's front camera and another using the back, `FotoapparatSwitcher` can be used:
|
||||
In order to switch between cameras, `Fotoapparat.switchTo()` can be used with the new desired `lensPosition` and its `cameraConfiguration`.
|
||||
|
||||
```java
|
||||
FotoapparatSwitcher fotoapparatSwitcher = FotoapparatSwitcher.withDefault(fotoapparatFront);
|
||||
fotoapparatSwitcher.switchTo(fotoapparatBack);
|
||||
```kotlin
|
||||
fotoapparat.switchTo(
|
||||
lensPosition = front(),
|
||||
cameraConfiguration = newConfigurationForFrontCamera
|
||||
)
|
||||
```
|
||||
|
||||
## Set up
|
||||
@@ -159,7 +166,7 @@ repositories {
|
||||
maven { url 'https://jitpack.io' }
|
||||
}
|
||||
|
||||
compile 'io.fotoapparat.fotoapparat:library:1.5.0'
|
||||
compile 'io.fotoapparat.fotoapparat:library:2.0.2'
|
||||
```
|
||||
|
||||
Camera permission will be automatically added to your `AndroidManifest.xml`. Do not forget to request this permission on Marshmallow and higher.
|
||||
|
||||
+32
-2
@@ -1,17 +1,47 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext {
|
||||
versions = [
|
||||
gradle : '4.4.1',
|
||||
kotlin : '1.2.10',
|
||||
code : 1,
|
||||
name : '1.0.0',
|
||||
sdk : [
|
||||
minimum: 16,
|
||||
target : 27
|
||||
],
|
||||
android: [
|
||||
buildTools: '27.0.3',
|
||||
support : '27.0.2'
|
||||
],
|
||||
rx : [
|
||||
rxJava1: '1.2.9',
|
||||
rxJava2: '2.0.8'
|
||||
],
|
||||
test : [
|
||||
junit : '4.12',
|
||||
mockito: '2.13.0'
|
||||
],
|
||||
util : [
|
||||
commons: '2.5'
|
||||
]
|
||||
]
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath "com.android.tools.build:gradle:${project.androidBuildToolsVersion}"
|
||||
classpath "com.android.tools.build:gradle:3.1.0-alpha06"
|
||||
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
@@ -21,5 +51,5 @@ task clean(type: Delete) {
|
||||
}
|
||||
|
||||
task wrapper(type: Wrapper) {
|
||||
gradleVersion = project.gradleVersion
|
||||
gradleVersion = versions.gradle
|
||||
}
|
||||
@@ -5,6 +5,18 @@ The child modules contained herein are additional adapters for other popular exe
|
||||
|
||||
To use, supply an instance of your desired adapter when performing a task of a PendingResult.
|
||||
|
||||
Kotlin:
|
||||
```java
|
||||
fotoapparat.takePicture()
|
||||
.toBitmap()
|
||||
.toObservable()
|
||||
.subscribe { bitmapPhoto ->
|
||||
// Do something with the photo
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Java:
|
||||
```java
|
||||
fotoapparat.takePicture()
|
||||
.toBitmap()
|
||||
@@ -16,3 +28,12 @@ fotoapparat.takePicture()
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
|
||||
Supported types:
|
||||
|
||||
* `Observable<T>` : RxJava 1/2
|
||||
* `Flowable<T>` : RxJava 2
|
||||
* `Single<T>` : RxJava 1/2
|
||||
* `Completable` : RxJava 1/2
|
||||
@@ -1,23 +1,27 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'com.github.dcendents.android-maven'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
group = 'io.fotoapparat'
|
||||
|
||||
android {
|
||||
buildToolsVersion project.buildToolsVersion
|
||||
compileSdkVersion Integer.parseInt(project.compileSdkVersion)
|
||||
buildToolsVersion versions.android.buildTools
|
||||
compileSdkVersion versions.sdk.target
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion Integer.parseInt(project.minSdkVersion)
|
||||
targetSdkVersion Integer.parseInt(project.targetSdkVersion)
|
||||
minSdkVersion versions.sdk.minimum
|
||||
targetSdkVersion versions.sdk.target
|
||||
|
||||
archivesBaseName = 'adapter-rxjava'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':fotoapparat')
|
||||
provided "io.reactivex:rxjava:${project.rxjava1Version}"
|
||||
compileOnly project(':fotoapparat')
|
||||
compileOnly "io.reactivex:rxjava:${versions.rx.rxJava1}"
|
||||
|
||||
testCompile "junit:junit:${project.junitVersion}"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:${versions.kotlin}"
|
||||
|
||||
testImplementation "io.reactivex:rxjava:${versions.rx.rxJava1}"
|
||||
testImplementation "junit:junit:${versions.test.junit}"
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
package io.fotoapparat.result.adapter.rxjava;
|
||||
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import io.fotoapparat.result.adapter.Adapter;
|
||||
import rx.Completable;
|
||||
|
||||
/**
|
||||
* Adapter for {@link Completable}.
|
||||
*/
|
||||
public class CompletableAdapter<T> implements Adapter<T, Completable> {
|
||||
|
||||
private CompletableAdapter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link Adapter} which adapts result to {@link Completable}.
|
||||
*/
|
||||
public static <R> CompletableAdapter<R> toCompletable() {
|
||||
return new CompletableAdapter<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Completable adapt(Future<T> future) {
|
||||
return Completable.fromFuture(future);
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package io.fotoapparat.result.adapter.rxjava
|
||||
|
||||
import io.fotoapparat.result.PendingResult
|
||||
import rx.Completable
|
||||
import java.util.concurrent.Future
|
||||
|
||||
/**
|
||||
* Adapter for [Completable].
|
||||
*/
|
||||
object CompletableAdapter {
|
||||
|
||||
/**
|
||||
* @return Adapter which adapts result to [Completable].
|
||||
*/
|
||||
@JvmStatic
|
||||
fun <T> toCompletable(): Function1<Future<T>, Completable> {
|
||||
return { future -> Completable.fromFuture(future) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A [Completable] from the given [PendingResult].
|
||||
*/
|
||||
fun <T> PendingResult<T>.toCompletable(): Completable {
|
||||
return adapt { future -> Completable.fromFuture(future) }
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
package io.fotoapparat.result.adapter.rxjava;
|
||||
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import io.fotoapparat.result.adapter.Adapter;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* Adapter for {@link Observable}.
|
||||
*/
|
||||
public class ObservableAdapter<T> implements Adapter<T, Observable<T>> {
|
||||
|
||||
private ObservableAdapter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link Adapter} which adapts result to {@link Observable}.
|
||||
*/
|
||||
public static <R> ObservableAdapter<R> toObservable() {
|
||||
return new ObservableAdapter<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Observable<T> adapt(Future<T> future) {
|
||||
return Observable.from(future);
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package io.fotoapparat.result.adapter.rxjava
|
||||
|
||||
import io.fotoapparat.result.PendingResult
|
||||
import rx.Observable
|
||||
import java.util.concurrent.Future
|
||||
|
||||
/**
|
||||
* Adapter for [Observable].
|
||||
*/
|
||||
object ObservableAdapter {
|
||||
|
||||
/**
|
||||
* @return Adapter which adapts result to [Observable].
|
||||
*/
|
||||
@JvmStatic
|
||||
fun <T> toObservable(): Function1<Future<T>, Observable<T>> {
|
||||
return { future -> Observable.from(future) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A [Observable] from the given [PendingResult].
|
||||
*/
|
||||
fun <T> PendingResult<T>.toObservable(): Observable<T> {
|
||||
return adapt { future -> Observable.from(future) }
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
package io.fotoapparat.result.adapter.rxjava;
|
||||
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import io.fotoapparat.result.adapter.Adapter;
|
||||
import rx.Single;
|
||||
|
||||
/**
|
||||
* Adapter for {@link Single}.
|
||||
*/
|
||||
public class SingleAdapter<T> implements Adapter<T, Single<T>> {
|
||||
|
||||
private SingleAdapter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link Adapter} which adapts result to {@link Single}.
|
||||
*/
|
||||
public static <R> SingleAdapter<R> toSingle() {
|
||||
return new SingleAdapter<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Single<T> adapt(Future<T> future) {
|
||||
return Single.from(future);
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package io.fotoapparat.result.adapter.rxjava
|
||||
|
||||
import io.fotoapparat.result.PendingResult
|
||||
import rx.Single
|
||||
import java.util.concurrent.Future
|
||||
|
||||
/**
|
||||
* Adapter for [Single].
|
||||
*/
|
||||
object SingleAdapter {
|
||||
|
||||
/**
|
||||
* @return Adapter which adapts result to [Single].
|
||||
*/
|
||||
@JvmStatic
|
||||
fun <T> toSingle(): (Future<T>) -> Single<T> {
|
||||
return { future -> Single.from(future) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A [Single] from the given [PendingResult].
|
||||
*/
|
||||
fun <T> PendingResult<T>.toSingle(): Single<T> {
|
||||
return adapt { future -> Single.from(future) }
|
||||
}
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
package io.fotoapparat.result.adapter.rxjava;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* Simply returns the value of the callable.
|
||||
*/
|
||||
public class CallableFuture<T> implements Future<T> {
|
||||
|
||||
private final CountDownLatch latch = new CountDownLatch(1);
|
||||
private final Callable<T> callable;
|
||||
|
||||
public CallableFuture(Callable<T> callable) {
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
return latch.getCount() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() throws InterruptedException, ExecutionException {
|
||||
return callCallable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(long timeout,
|
||||
@NonNull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
|
||||
return callCallable();
|
||||
}
|
||||
|
||||
private T callCallable() throws ExecutionException {
|
||||
latch.countDown();
|
||||
try {
|
||||
return callable.call();
|
||||
} catch (Exception e) {
|
||||
throw new ExecutionException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package io.fotoapparat.result.adapter.rxjava
|
||||
|
||||
import java.util.concurrent.*
|
||||
|
||||
/**
|
||||
* Simply returns the value of the callable.
|
||||
*/
|
||||
class CallableFuture<T>(private val callable: Callable<T>) : Future<T> {
|
||||
|
||||
private val latch = CountDownLatch(1)
|
||||
|
||||
override fun cancel(mayInterruptIfRunning: Boolean): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isCancelled(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isDone(): Boolean {
|
||||
return latch.count == 0L
|
||||
}
|
||||
|
||||
@Throws(InterruptedException::class, ExecutionException::class)
|
||||
override fun get(): T {
|
||||
return callCallable()
|
||||
}
|
||||
|
||||
@Throws(InterruptedException::class, ExecutionException::class, TimeoutException::class)
|
||||
override fun get(timeout: Long, unit: TimeUnit): T {
|
||||
return callCallable()
|
||||
}
|
||||
|
||||
@Throws(ExecutionException::class)
|
||||
private fun callCallable(): T {
|
||||
latch.countDown()
|
||||
try {
|
||||
return callable.call()
|
||||
} catch (e: Exception) {
|
||||
throw ExecutionException(e)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -24,7 +24,7 @@ public class CompletableAdapterTest {
|
||||
|
||||
// When
|
||||
CompletableAdapter.<String>toCompletable()
|
||||
.adapt(future)
|
||||
.invoke(future)
|
||||
.subscribe(subscriber);
|
||||
|
||||
// Then
|
||||
@@ -45,7 +45,7 @@ public class CompletableAdapterTest {
|
||||
|
||||
// When
|
||||
CompletableAdapter.<String>toCompletable()
|
||||
.adapt(future)
|
||||
.invoke(future)
|
||||
.subscribe(subscriber);
|
||||
|
||||
// Then
|
||||
|
||||
+2
-2
@@ -24,7 +24,7 @@ public class ObservableAdapterTest {
|
||||
|
||||
// When
|
||||
ObservableAdapter.<String>toObservable()
|
||||
.adapt(future)
|
||||
.invoke(future)
|
||||
.subscribe(subscriber);
|
||||
|
||||
// Then
|
||||
@@ -45,7 +45,7 @@ public class ObservableAdapterTest {
|
||||
|
||||
// When
|
||||
ObservableAdapter.<String>toObservable()
|
||||
.adapt(future)
|
||||
.invoke(future)
|
||||
.subscribe(subscriber);
|
||||
|
||||
// Then
|
||||
|
||||
+2
-2
@@ -24,7 +24,7 @@ public class SingleAdapterTest {
|
||||
|
||||
// When
|
||||
SingleAdapter.<String>toSingle()
|
||||
.adapt(future)
|
||||
.invoke(future)
|
||||
.subscribe(subscriber);
|
||||
|
||||
// Then
|
||||
@@ -45,7 +45,7 @@ public class SingleAdapterTest {
|
||||
|
||||
// When
|
||||
SingleAdapter.<String>toSingle()
|
||||
.adapt(future)
|
||||
.invoke(future)
|
||||
.subscribe(subscriber);
|
||||
|
||||
// Then
|
||||
|
||||
@@ -5,6 +5,7 @@ An `Adapter` for adapting RxJava 2.x types.
|
||||
Available types:
|
||||
|
||||
* `Observable<T>`
|
||||
* `Flowable<T>`
|
||||
* `Single<T>`
|
||||
* `Completable`
|
||||
|
||||
|
||||
@@ -1,23 +1,27 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'com.github.dcendents.android-maven'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
group = 'io.fotoapparat'
|
||||
|
||||
android {
|
||||
buildToolsVersion project.buildToolsVersion
|
||||
compileSdkVersion Integer.parseInt(project.compileSdkVersion)
|
||||
buildToolsVersion versions.android.buildTools
|
||||
compileSdkVersion versions.sdk.target
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion Integer.parseInt(project.minSdkVersion)
|
||||
targetSdkVersion Integer.parseInt(project.targetSdkVersion)
|
||||
minSdkVersion versions.sdk.minimum
|
||||
targetSdkVersion versions.sdk.target
|
||||
|
||||
archivesBaseName = 'adapter-rxjava2'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':fotoapparat')
|
||||
provided "io.reactivex.rxjava2:rxjava:${project.rxjava2Version}"
|
||||
compileOnly project(':fotoapparat')
|
||||
compileOnly "io.reactivex.rxjava2:rxjava:${versions.rx.rxJava2}"
|
||||
|
||||
testCompile "junit:junit:${project.junitVersion}"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:${versions.kotlin}"
|
||||
|
||||
testImplementation "io.reactivex.rxjava2:rxjava:${versions.rx.rxJava2}"
|
||||
testImplementation "junit:junit:${versions.test.junit}"
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
package io.fotoapparat.result.adapter.rxjava2;
|
||||
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import io.fotoapparat.result.adapter.Adapter;
|
||||
import io.reactivex.Completable;
|
||||
|
||||
/**
|
||||
* Adapter for {@link Completable}.
|
||||
*/
|
||||
public class CompletableAdapter<T> implements Adapter<T, Completable> {
|
||||
|
||||
private CompletableAdapter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link Adapter} which adapts result to {@link Completable}.
|
||||
*/
|
||||
public static <R> CompletableAdapter<R> toCompletable() {
|
||||
return new CompletableAdapter<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Completable adapt(Future<T> future) {
|
||||
return Completable.fromFuture(future);
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package io.fotoapparat.result.adapter.rxjava2
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import io.fotoapparat.result.PendingResult
|
||||
import io.reactivex.Completable
|
||||
import java.util.concurrent.Future
|
||||
|
||||
/**
|
||||
* Adapter for [Completable].
|
||||
*/
|
||||
object CompletableAdapter {
|
||||
|
||||
/**
|
||||
* @return Adapter which adapts result to [Completable].
|
||||
*/
|
||||
@JvmStatic
|
||||
@SuppressLint("CheckResult")
|
||||
fun <R> toCompletable(): (Future<R>) -> Completable {
|
||||
return { future -> Completable.fromFuture(future) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A [Completable] from the given [PendingResult].
|
||||
*/
|
||||
@SuppressLint("CheckResult")
|
||||
fun <T> PendingResult<T>.toCompletable(): Completable {
|
||||
return adapt { future -> Completable.fromFuture(future) }
|
||||
}
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package io.fotoapparat.result.adapter.rxjava2
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import io.fotoapparat.result.PendingResult
|
||||
import io.reactivex.Flowable
|
||||
import java.util.concurrent.Future
|
||||
|
||||
/**
|
||||
* Adapter for [Flowable].
|
||||
*/
|
||||
object FlowableAdapter {
|
||||
|
||||
/**
|
||||
* @return Adapter which adapts result to [Flowable].
|
||||
*/
|
||||
@JvmStatic
|
||||
@SuppressLint("CheckResult")
|
||||
fun <T> toFlowable(): Function1<Future<T>, Flowable<T>> {
|
||||
return { future -> Flowable.fromFuture(future) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A [Flowable] from the given [PendingResult].
|
||||
*/
|
||||
@SuppressLint("CheckResult")
|
||||
fun <T> PendingResult<T>.toFlowable(): Flowable<T> {
|
||||
return adapt { future -> Flowable.fromFuture(future) }
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
package io.fotoapparat.result.adapter.rxjava2;
|
||||
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import io.fotoapparat.result.adapter.Adapter;
|
||||
import io.reactivex.Observable;
|
||||
|
||||
/**
|
||||
* Adapter for {@link Observable}.
|
||||
*/
|
||||
public class ObservableAdapter<T> implements Adapter<T, Observable<T>> {
|
||||
|
||||
private ObservableAdapter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link Adapter} which adapts result to {@link Observable}.
|
||||
*/
|
||||
public static <R> ObservableAdapter<R> toObservable() {
|
||||
return new ObservableAdapter<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Observable<T> adapt(Future<T> future) {
|
||||
return Observable.fromFuture(future);
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package io.fotoapparat.result.adapter.rxjava2
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import io.fotoapparat.result.PendingResult
|
||||
import io.reactivex.Observable
|
||||
import java.util.concurrent.Future
|
||||
|
||||
/**
|
||||
* Adapter for [Observable].
|
||||
*/
|
||||
object ObservableAdapter {
|
||||
|
||||
/**
|
||||
* @return Adapter which adapts result to [Observable].
|
||||
*/
|
||||
@JvmStatic
|
||||
@SuppressLint("CheckResult")
|
||||
fun <T> toObservable(): Function1<Future<T>, Observable<T>> {
|
||||
return { future -> Observable.fromFuture(future) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A [Observable] from the given [PendingResult].
|
||||
*/
|
||||
@SuppressLint("CheckResult")
|
||||
fun <T> PendingResult<T>.toObservable(): Observable<T> {
|
||||
return adapt { future -> Observable.fromFuture(future) }
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
package io.fotoapparat.result.adapter.rxjava2;
|
||||
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import io.fotoapparat.result.adapter.Adapter;
|
||||
import io.reactivex.Single;
|
||||
|
||||
/**
|
||||
* Adapter for {@link Single}.
|
||||
*/
|
||||
public class SingleAdapter<T> implements Adapter<T, Single<T>> {
|
||||
|
||||
private SingleAdapter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link Adapter} which adapts result to {@link Single}.
|
||||
*/
|
||||
public static <R> SingleAdapter<R> toSingle() {
|
||||
return new SingleAdapter<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Single<T> adapt(Future<T> future) {
|
||||
return Single.fromFuture(future);
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package io.fotoapparat.result.adapter.rxjava2
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import io.fotoapparat.result.PendingResult
|
||||
import io.reactivex.Single
|
||||
import java.util.concurrent.Future
|
||||
|
||||
/**
|
||||
* Adapter for [Single].
|
||||
*/
|
||||
object SingleAdapter {
|
||||
|
||||
/**
|
||||
* @return Adapter which adapts result to [Single].
|
||||
*/
|
||||
@JvmStatic
|
||||
@SuppressLint("CheckResult")
|
||||
fun <T> toSingle(): (Future<T>) -> Single<T> {
|
||||
return { future -> Single.fromFuture(future) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A [Single] from the given [PendingResult].
|
||||
*/
|
||||
@SuppressLint("CheckResult")
|
||||
fun <T> PendingResult<T>.toSingle(): Single<T> {
|
||||
return adapt { future -> Single.fromFuture(future) }
|
||||
}
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
package io.fotoapparat.result.adapter.rxjava2;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* Simply returns the value of the callable.
|
||||
*/
|
||||
public class CallableFuture<T> implements Future<T> {
|
||||
|
||||
private final CountDownLatch latch = new CountDownLatch(1);
|
||||
private final Callable<T> callable;
|
||||
|
||||
public CallableFuture(Callable<T> callable) {
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
return latch.getCount() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() throws InterruptedException, ExecutionException {
|
||||
return callCallable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(long timeout,
|
||||
@NonNull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
|
||||
return callCallable();
|
||||
}
|
||||
|
||||
private T callCallable() throws ExecutionException {
|
||||
latch.countDown();
|
||||
try {
|
||||
return callable.call();
|
||||
} catch (Exception e) {
|
||||
throw new ExecutionException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package io.fotoapparat.result.adapter.rxjava2
|
||||
|
||||
import java.util.concurrent.*
|
||||
|
||||
/**
|
||||
* Simply returns the value of the callable.
|
||||
*/
|
||||
class CallableFuture<T>(private val callable: Callable<T>) : Future<T> {
|
||||
|
||||
private val latch = CountDownLatch(1)
|
||||
|
||||
override fun cancel(mayInterruptIfRunning: Boolean): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isCancelled(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isDone(): Boolean {
|
||||
return latch.count == 0L
|
||||
}
|
||||
|
||||
@Throws(InterruptedException::class, ExecutionException::class)
|
||||
override fun get(): T {
|
||||
return callCallable()
|
||||
}
|
||||
|
||||
@Throws(InterruptedException::class, ExecutionException::class, TimeoutException::class)
|
||||
override fun get(timeout: Long, unit: TimeUnit): T {
|
||||
return callCallable()
|
||||
}
|
||||
|
||||
@Throws(ExecutionException::class)
|
||||
private fun callCallable(): T {
|
||||
latch.countDown()
|
||||
try {
|
||||
return callable.call()
|
||||
} catch (e: Exception) {
|
||||
throw ExecutionException(e)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -10,7 +10,7 @@ import io.reactivex.observers.TestObserver;
|
||||
|
||||
public class CompletableAdapterTest {
|
||||
|
||||
private TestObserver<Object> observer = new TestObserver<>();
|
||||
private TestObserver<String> observer = new TestObserver<>();
|
||||
|
||||
@Test
|
||||
public void completed() throws Exception {
|
||||
@@ -24,7 +24,7 @@ public class CompletableAdapterTest {
|
||||
|
||||
// When
|
||||
CompletableAdapter.<String>toCompletable()
|
||||
.adapt(future)
|
||||
.invoke(future)
|
||||
.subscribe(observer);
|
||||
|
||||
// Then
|
||||
@@ -44,7 +44,7 @@ public class CompletableAdapterTest {
|
||||
|
||||
// When
|
||||
CompletableAdapter.<String>toCompletable()
|
||||
.adapt(future)
|
||||
.invoke(future)
|
||||
.subscribe(observer);
|
||||
|
||||
// Then
|
||||
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package io.fotoapparat.result.adapter.rxjava2;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import io.reactivex.subscribers.TestSubscriber;
|
||||
|
||||
public class FlowableAdapterTest {
|
||||
|
||||
private TestSubscriber<String> observer = new TestSubscriber<>();
|
||||
|
||||
@Test
|
||||
public void completed() throws Exception {
|
||||
// Given
|
||||
Future<String> future = new CallableFuture<>(new Callable<String>() {
|
||||
@Override
|
||||
public String call() throws Exception {
|
||||
return "Hello";
|
||||
}
|
||||
});
|
||||
|
||||
// When
|
||||
FlowableAdapter.<String>toFlowable()
|
||||
.invoke(future)
|
||||
.subscribe(observer);
|
||||
|
||||
// Then
|
||||
observer.assertValue("Hello");
|
||||
observer.assertNoErrors();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void error() throws Exception {
|
||||
// Given
|
||||
Future<String> future = new CallableFuture<>(new Callable<String>() {
|
||||
@Override
|
||||
public String call() throws Exception {
|
||||
throw new RuntimeException("What a failure");
|
||||
}
|
||||
});
|
||||
|
||||
// When
|
||||
FlowableAdapter.<String>toFlowable()
|
||||
.invoke(future)
|
||||
.subscribe(observer);
|
||||
|
||||
// Then
|
||||
observer.assertNoValues();
|
||||
observer.assertError(ExecutionException.class);
|
||||
}
|
||||
|
||||
}
|
||||
+3
-3
@@ -10,7 +10,7 @@ import io.reactivex.observers.TestObserver;
|
||||
|
||||
public class ObservableAdapterTest {
|
||||
|
||||
private TestObserver<Object> observer = new TestObserver<>();
|
||||
private TestObserver<String> observer = new TestObserver<>();
|
||||
|
||||
@Test
|
||||
public void completed() throws Exception {
|
||||
@@ -24,7 +24,7 @@ public class ObservableAdapterTest {
|
||||
|
||||
// When
|
||||
ObservableAdapter.<String>toObservable()
|
||||
.adapt(future)
|
||||
.invoke(future)
|
||||
.subscribe(observer);
|
||||
|
||||
// Then
|
||||
@@ -44,7 +44,7 @@ public class ObservableAdapterTest {
|
||||
|
||||
// When
|
||||
ObservableAdapter.<String>toObservable()
|
||||
.adapt(future)
|
||||
.invoke(future)
|
||||
.subscribe(observer);
|
||||
|
||||
// Then
|
||||
|
||||
+3
-3
@@ -10,7 +10,7 @@ import io.reactivex.observers.TestObserver;
|
||||
|
||||
public class SingleAdapterTest {
|
||||
|
||||
private TestObserver<Object> observer = new TestObserver<>();
|
||||
private TestObserver<String> observer = new TestObserver<>();
|
||||
|
||||
@Test
|
||||
public void completed() throws Exception {
|
||||
@@ -24,7 +24,7 @@ public class SingleAdapterTest {
|
||||
|
||||
// When
|
||||
SingleAdapter.<String>toSingle()
|
||||
.adapt(future)
|
||||
.invoke(future)
|
||||
.subscribe(observer);
|
||||
|
||||
// Then
|
||||
@@ -44,7 +44,7 @@ public class SingleAdapterTest {
|
||||
|
||||
// When
|
||||
SingleAdapter.<String>toSingle()
|
||||
.adapt(future)
|
||||
.invoke(future)
|
||||
.subscribe(observer);
|
||||
|
||||
// Then
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'com.github.dcendents.android-maven'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
group = 'io.fotoapparat'
|
||||
|
||||
kotlin.experimental.coroutines 'enable'
|
||||
|
||||
android {
|
||||
buildToolsVersion project.buildToolsVersion
|
||||
compileSdkVersion Integer.parseInt(project.compileSdkVersion)
|
||||
buildToolsVersion versions.android.buildTools
|
||||
compileSdkVersion versions.sdk.target
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion Integer.parseInt(project.minSdkVersion)
|
||||
targetSdkVersion Integer.parseInt(project.targetSdkVersion)
|
||||
minSdkVersion versions.sdk.minimum
|
||||
targetSdkVersion versions.sdk.target
|
||||
|
||||
archivesBaseName = 'library'
|
||||
}
|
||||
@@ -26,9 +29,12 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile "com.android.support:support-annotations:${project.appCompatVersion}"
|
||||
compile "com.android.support:support-annotations:${versions.android.support}"
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib:${versions.kotlin}"
|
||||
compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.21'
|
||||
|
||||
testCompile "junit:junit:${project.junitVersion}"
|
||||
testCompile "org.mockito:mockito-core:${project.mockitoVersion}"
|
||||
testCompile 'commons-io:commons-io:2.5'
|
||||
testImplementation "junit:junit:${versions.test.junit}"
|
||||
testImplementation "org.jetbrains.kotlin:kotlin-test-junit:${versions.kotlin}"
|
||||
testImplementation "org.mockito:mockito-core:${versions.test.mockito}"
|
||||
testImplementation "commons-io:commons-io:${versions.util.commons}"
|
||||
}
|
||||
@@ -1,342 +0,0 @@
|
||||
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;
|
||||
import io.fotoapparat.hardware.orientation.ScreenOrientationProvider;
|
||||
import io.fotoapparat.parameter.provider.CapabilitiesProvider;
|
||||
import io.fotoapparat.parameter.provider.CurrentParametersProvider;
|
||||
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.ParametersResult;
|
||||
import io.fotoapparat.result.PendingResult;
|
||||
import io.fotoapparat.result.PhotoResult;
|
||||
import io.fotoapparat.routine.CheckAvailabilityRoutine;
|
||||
import io.fotoapparat.routine.ConfigurePreviewStreamRoutine;
|
||||
import io.fotoapparat.routine.StartCameraRoutine;
|
||||
import io.fotoapparat.routine.StopCameraRoutine;
|
||||
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.
|
||||
*/
|
||||
public class Fotoapparat {
|
||||
|
||||
private static final Executor SERIAL_EXECUTOR = Executors.newSingleThreadExecutor();
|
||||
|
||||
private final StartCameraRoutine startCameraRoutine;
|
||||
private final StopCameraRoutine stopCameraRoutine;
|
||||
private final UpdateOrientationRoutine updateOrientationRoutine;
|
||||
private final ConfigurePreviewStreamRoutine configurePreviewStreamRoutine;
|
||||
private final CapabilitiesProvider capabilitiesProvider;
|
||||
private final CurrentParametersProvider currentParametersProvider;
|
||||
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;
|
||||
|
||||
Fotoapparat(StartCameraRoutine startCameraRoutine,
|
||||
StopCameraRoutine stopCameraRoutine,
|
||||
UpdateOrientationRoutine updateOrientationRoutine,
|
||||
ConfigurePreviewStreamRoutine configurePreviewStreamRoutine,
|
||||
CapabilitiesProvider capabilitiesProvider,
|
||||
CurrentParametersProvider parametersProvider,
|
||||
TakePictureRoutine takePictureRoutine,
|
||||
AutoFocusRoutine autoFocusRoutine,
|
||||
CheckAvailabilityRoutine checkAvailabilityRoutine,
|
||||
UpdateParametersRoutine updateParametersRoutine,
|
||||
UpdateZoomLevelRoutine updateZoomLevelRoutine,
|
||||
Executor executor) {
|
||||
this.startCameraRoutine = startCameraRoutine;
|
||||
this.stopCameraRoutine = stopCameraRoutine;
|
||||
this.updateOrientationRoutine = updateOrientationRoutine;
|
||||
this.configurePreviewStreamRoutine = configurePreviewStreamRoutine;
|
||||
this.capabilitiesProvider = capabilitiesProvider;
|
||||
this.currentParametersProvider = parametersProvider;
|
||||
this.takePictureRoutine = takePictureRoutine;
|
||||
this.autoFocusRoutine = autoFocusRoutine;
|
||||
this.checkAvailabilityRoutine = checkAvailabilityRoutine;
|
||||
this.updateParametersRoutine = updateParametersRoutine;
|
||||
this.updateZoomLevelRoutine = updateZoomLevelRoutine;
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
public static FotoapparatBuilder with(Context context) {
|
||||
if (context == null) {
|
||||
throw new IllegalStateException("Context is null.");
|
||||
}
|
||||
|
||||
return new FotoapparatBuilder(context);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
InitialParametersValidator parametersValidator = new InitialParametersValidator();
|
||||
InitialParametersProvider initialParametersProvider = new InitialParametersProvider(
|
||||
cameraDevice,
|
||||
builder.photoSizeSelector,
|
||||
builder.previewSizeSelector,
|
||||
builder.focusModeSelector,
|
||||
builder.flashSelector,
|
||||
builder.previewFpsRangeSelector,
|
||||
builder.sensorSensitivitySelector,
|
||||
builder.jpegQuality,
|
||||
parametersValidator
|
||||
);
|
||||
|
||||
StartCameraRoutine startCameraRoutine = new StartCameraRoutine(
|
||||
cameraDevice,
|
||||
builder.renderer,
|
||||
builder.scaleType,
|
||||
builder.lensPositionSelector,
|
||||
screenOrientationProvider,
|
||||
initialParametersProvider,
|
||||
cameraErrorCallback
|
||||
);
|
||||
|
||||
StopCameraRoutine stopCameraRoutine = new StopCameraRoutine(cameraDevice);
|
||||
|
||||
OrientationSensor orientationSensor = new OrientationSensor(
|
||||
rotationListener,
|
||||
screenOrientationProvider
|
||||
);
|
||||
|
||||
UpdateOrientationRoutine updateOrientationRoutine = new UpdateOrientationRoutine(
|
||||
cameraDevice,
|
||||
orientationSensor,
|
||||
SERIAL_EXECUTOR,
|
||||
builder.logger
|
||||
);
|
||||
|
||||
ConfigurePreviewStreamRoutine configurePreviewStreamRoutine = new ConfigurePreviewStreamRoutine(
|
||||
cameraDevice,
|
||||
builder.frameProcessor
|
||||
);
|
||||
|
||||
CapabilitiesProvider capabilitiesProvider = new CapabilitiesProvider(
|
||||
cameraDevice,
|
||||
SERIAL_EXECUTOR
|
||||
);
|
||||
|
||||
CurrentParametersProvider currentParametersProvider = new CurrentParametersProvider(
|
||||
cameraDevice,
|
||||
SERIAL_EXECUTOR
|
||||
);
|
||||
|
||||
TakePictureRoutine takePictureRoutine = new TakePictureRoutine(
|
||||
cameraDevice,
|
||||
SERIAL_EXECUTOR
|
||||
);
|
||||
|
||||
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,
|
||||
updateOrientationRoutine,
|
||||
configurePreviewStreamRoutine,
|
||||
capabilitiesProvider,
|
||||
currentParametersProvider,
|
||||
takePictureRoutine,
|
||||
autoFocusRoutine,
|
||||
checkAvailabilityRoutine,
|
||||
updateParametersRoutine,
|
||||
updateZoomLevelRoutine,
|
||||
SERIAL_EXECUTOR
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true} if camera for this {@link Fotoapparat} is available. {@code false} if
|
||||
* it is not available.
|
||||
*/
|
||||
public boolean isAvailable() {
|
||||
return checkAvailabilityRoutine.isAvailable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides camera capabilities asynchronously, returns immediately.
|
||||
*
|
||||
* @return {@link CapabilitiesResult} which will deliver result asynchronously.
|
||||
*/
|
||||
public CapabilitiesResult getCapabilities() {
|
||||
ensureStarted();
|
||||
|
||||
return capabilitiesProvider.getCapabilities();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides current camera parameters asynchronously, returns immediately.
|
||||
*
|
||||
* @return {@link ParametersResult} which will deliver result asynchronously.
|
||||
*/
|
||||
public ParametersResult getCurrentParameters() {
|
||||
ensureStarted();
|
||||
|
||||
return currentParametersProvider.getParameters();
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes picture. Returns immediately.
|
||||
*
|
||||
* @return {@link PhotoResult} which will deliver result asynchronously.
|
||||
*/
|
||||
public PhotoResult takePicture() {
|
||||
ensureStarted();
|
||||
|
||||
return takePictureRoutine.takePicture();
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs auto focus. If it is not available or not enabled, does nothing.
|
||||
*/
|
||||
public Fotoapparat autoFocus() {
|
||||
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.
|
||||
*
|
||||
* @throws IllegalStateException if camera was already started.
|
||||
*/
|
||||
public void start() {
|
||||
ensureNotStarted();
|
||||
started = true;
|
||||
|
||||
startCamera();
|
||||
configurePreviewStream();
|
||||
updateOrientationRoutine.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops camera.
|
||||
*
|
||||
* @throws IllegalStateException if camera is not started.
|
||||
*/
|
||||
public void stop() {
|
||||
ensureStarted();
|
||||
started = false;
|
||||
|
||||
updateOrientationRoutine.stop();
|
||||
stopCamera();
|
||||
}
|
||||
|
||||
private void startCamera() {
|
||||
executor.execute(
|
||||
startCameraRoutine
|
||||
);
|
||||
}
|
||||
|
||||
private void stopCamera() {
|
||||
executor.execute(
|
||||
stopCameraRoutine
|
||||
);
|
||||
}
|
||||
|
||||
private void configurePreviewStream() {
|
||||
executor.execute(
|
||||
configurePreviewStreamRoutine
|
||||
);
|
||||
}
|
||||
|
||||
private void ensureStarted() {
|
||||
if (!started) {
|
||||
throw new IllegalStateException("Camera is not started!");
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureNotStarted() {
|
||||
if (started) {
|
||||
throw new IllegalStateException("Camera is already started!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
package io.fotoapparat
|
||||
|
||||
import android.content.Context
|
||||
import android.support.annotation.FloatRange
|
||||
import io.fotoapparat.capability.Capabilities
|
||||
import io.fotoapparat.characteristic.LensPosition
|
||||
import io.fotoapparat.configuration.CameraConfiguration
|
||||
import io.fotoapparat.configuration.Configuration
|
||||
import io.fotoapparat.error.onMainThread
|
||||
import io.fotoapparat.exception.camera.CameraException
|
||||
import io.fotoapparat.hardware.Device
|
||||
import io.fotoapparat.hardware.display.Display
|
||||
import io.fotoapparat.hardware.execute
|
||||
import io.fotoapparat.hardware.executeTask
|
||||
import io.fotoapparat.hardware.orientation.OrientationSensor
|
||||
import io.fotoapparat.log.Logger
|
||||
import io.fotoapparat.log.none
|
||||
import io.fotoapparat.parameter.ScaleType
|
||||
import io.fotoapparat.parameter.camera.CameraParameters
|
||||
import io.fotoapparat.result.*
|
||||
import io.fotoapparat.routine.camera.bootStart
|
||||
import io.fotoapparat.routine.camera.shutDown
|
||||
import io.fotoapparat.routine.camera.switchCamera
|
||||
import io.fotoapparat.routine.camera.updateDeviceConfiguration
|
||||
import io.fotoapparat.routine.capability.getCapabilities
|
||||
import io.fotoapparat.routine.focus.focus
|
||||
import io.fotoapparat.routine.parameter.getCurrentParameters
|
||||
import io.fotoapparat.routine.photo.takePhoto
|
||||
import io.fotoapparat.routine.zoom.updateZoomLevel
|
||||
import io.fotoapparat.selector.back
|
||||
import io.fotoapparat.selector.external
|
||||
import io.fotoapparat.selector.firstAvailable
|
||||
import io.fotoapparat.selector.front
|
||||
import io.fotoapparat.view.CameraRenderer
|
||||
import java.util.concurrent.FutureTask
|
||||
|
||||
/**
|
||||
* Camera. Takes pictures.
|
||||
*/
|
||||
class Fotoapparat
|
||||
@JvmOverloads constructor(
|
||||
context: Context,
|
||||
view: CameraRenderer,
|
||||
lensPosition: Collection<LensPosition>.() -> LensPosition? = firstAvailable(
|
||||
back(),
|
||||
front(),
|
||||
external()
|
||||
),
|
||||
scaleType: ScaleType = ScaleType.CenterCrop,
|
||||
cameraConfiguration: CameraConfiguration = CameraConfiguration.default(),
|
||||
cameraErrorCallback: ((CameraException) -> Unit) = {},
|
||||
private val logger: Logger = none()
|
||||
) {
|
||||
|
||||
private val mainThreadErrorCallback = cameraErrorCallback.onMainThread()
|
||||
|
||||
private val display = Display(context)
|
||||
|
||||
private val device = Device(
|
||||
cameraRenderer = view,
|
||||
logger = logger,
|
||||
display = display,
|
||||
scaleType = scaleType,
|
||||
initialLensPositionSelector = lensPosition,
|
||||
initialConfiguration = cameraConfiguration
|
||||
)
|
||||
|
||||
private val orientationSensor = OrientationSensor(
|
||||
context = context,
|
||||
device = device
|
||||
)
|
||||
|
||||
init {
|
||||
logger.recordMethod()
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts camera.
|
||||
*
|
||||
* @throws IllegalStateException If the camera has already started.
|
||||
*/
|
||||
fun start() {
|
||||
logger.recordMethod()
|
||||
|
||||
execute {
|
||||
device.bootStart(
|
||||
orientationSensor = orientationSensor,
|
||||
mainThreadErrorCallback = mainThreadErrorCallback
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops camera.
|
||||
*
|
||||
* @throws IllegalStateException If the camera has not started.
|
||||
*/
|
||||
fun stop() {
|
||||
logger.recordMethod()
|
||||
|
||||
execute {
|
||||
device.shutDown(
|
||||
orientationSensor = orientationSensor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes picture, returns immediately.
|
||||
*
|
||||
* @return [PhotoResult] which will deliver result asynchronously.
|
||||
*/
|
||||
fun takePicture(): PhotoResult {
|
||||
logger.recordMethod()
|
||||
|
||||
val takePictureTask = FutureTask<Photo> {
|
||||
device.takePhoto()
|
||||
}
|
||||
|
||||
executeTask(takePictureTask)
|
||||
|
||||
return PhotoResult.fromFuture(takePictureTask, logger)
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides camera capabilities asynchronously, returns immediately.
|
||||
*
|
||||
* @return [CapabilitiesResult] which will deliver result asynchronously.
|
||||
*/
|
||||
fun getCapabilities(): CapabilitiesResult {
|
||||
logger.recordMethod()
|
||||
|
||||
val getCapabilitiesTask = FutureTask<Capabilities> {
|
||||
device.getCapabilities()
|
||||
}
|
||||
|
||||
executeTask(getCapabilitiesTask)
|
||||
|
||||
return PendingResult.fromFuture(getCapabilitiesTask, logger)
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides current camera parameters asynchronously, returns immediately.
|
||||
*
|
||||
* @return [ParametersResult] which will deliver result asynchronously.
|
||||
*/
|
||||
fun getCurrentParameters(): ParametersResult {
|
||||
logger.recordMethod()
|
||||
|
||||
val getCameraParametersTask = FutureTask<CameraParameters> {
|
||||
device.getCurrentParameters()
|
||||
}
|
||||
|
||||
executeTask(getCameraParametersTask)
|
||||
|
||||
return PendingResult.fromFuture(getCameraParametersTask, logger)
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates current configuration.
|
||||
*
|
||||
* @throws IllegalStateException If the current camera has not started.
|
||||
*/
|
||||
fun updateConfiguration(newConfiguration: Configuration) = execute {
|
||||
logger.recordMethod()
|
||||
|
||||
device.updateDeviceConfiguration(newConfiguration)
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously updates zoom level of the camera.
|
||||
* If zoom is not supported by the device - does nothing.
|
||||
*
|
||||
* @param zoomLevel Zoom level of the camera. A value between 0 and 1.
|
||||
* @throws IllegalStateException If the current camera has not started.
|
||||
*/
|
||||
fun setZoom(@FloatRange(from = 0.0, to = 1.0) zoomLevel: Float) = execute {
|
||||
logger.recordMethod()
|
||||
|
||||
device.updateZoomLevel(
|
||||
zoomLevel = zoomLevel
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs auto focus. If it is not available or not enabled, does nothing.
|
||||
*
|
||||
* @see Fotoapparat.focus
|
||||
*/
|
||||
fun autoFocus(): Fotoapparat {
|
||||
logger.recordMethod()
|
||||
focus()
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to focus the camera asynchronously.
|
||||
*
|
||||
* @return the pending result of focus operation which will deliver result asynchronously.
|
||||
*
|
||||
* @see Fotoapparat.autoFocus
|
||||
*/
|
||||
fun focus(): PendingResult<FocusResult> {
|
||||
logger.recordMethod()
|
||||
|
||||
val focusTask = FutureTask<FocusResult> {
|
||||
device.focus()
|
||||
}
|
||||
executeTask(focusTask)
|
||||
|
||||
return PendingResult.fromFuture(focusTask, logger)
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches to another camera. If previous camera has already started then it will be
|
||||
* stopped automatically and new will start.
|
||||
*/
|
||||
fun switchTo(
|
||||
lensPosition: Collection<LensPosition>.() -> LensPosition?,
|
||||
cameraConfiguration: CameraConfiguration
|
||||
) {
|
||||
logger.recordMethod()
|
||||
execute {
|
||||
device.switchCamera(
|
||||
newLensPositionSelector = lensPosition,
|
||||
newConfiguration = cameraConfiguration,
|
||||
mainThreadErrorCallback = mainThreadErrorCallback
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return `true` if selected lens position is available. `false` if it is not available.
|
||||
*/
|
||||
fun isAvailable(
|
||||
selector: (Collection<LensPosition>) -> LensPosition?
|
||||
): Boolean {
|
||||
return device.canSelectCamera(selector)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun with(context: Context) = FotoapparatBuilder(context)
|
||||
}
|
||||
}
|
||||
@@ -1,220 +0,0 @@
|
||||
package io.fotoapparat;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.IntRange;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import io.fotoapparat.error.CameraErrorCallback;
|
||||
import java.util.Collection;
|
||||
|
||||
import io.fotoapparat.hardware.CameraDevice;
|
||||
import io.fotoapparat.hardware.provider.CameraProvider;
|
||||
import io.fotoapparat.log.Logger;
|
||||
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.range.Range;
|
||||
import io.fotoapparat.parameter.selector.FlashSelectors;
|
||||
import io.fotoapparat.parameter.selector.SelectorFunction;
|
||||
import io.fotoapparat.parameter.selector.Selectors;
|
||||
import io.fotoapparat.preview.FrameProcessor;
|
||||
import io.fotoapparat.view.CameraRenderer;
|
||||
import io.fotoapparat.view.CameraView;
|
||||
|
||||
import static io.fotoapparat.hardware.provider.CameraProviders.v1;
|
||||
import static io.fotoapparat.parameter.selector.FocusModeSelectors.autoFocus;
|
||||
import static io.fotoapparat.parameter.selector.FocusModeSelectors.continuousFocus;
|
||||
import static io.fotoapparat.parameter.selector.FocusModeSelectors.fixed;
|
||||
import static io.fotoapparat.parameter.selector.LensPositionSelectors.back;
|
||||
import static io.fotoapparat.parameter.selector.LensPositionSelectors.external;
|
||||
import static io.fotoapparat.parameter.selector.LensPositionSelectors.front;
|
||||
import static io.fotoapparat.parameter.selector.Selectors.firstAvailable;
|
||||
import static io.fotoapparat.parameter.selector.SizeSelectors.biggestSize;
|
||||
|
||||
/**
|
||||
* Builder for {@link Fotoapparat}.
|
||||
*/
|
||||
public class FotoapparatBuilder {
|
||||
|
||||
Context context;
|
||||
CameraProvider cameraProvider = v1();
|
||||
CameraRenderer renderer;
|
||||
|
||||
SelectorFunction<Collection<LensPosition>, LensPosition> lensPositionSelector = firstAvailable(
|
||||
back(),
|
||||
front(),
|
||||
external()
|
||||
);
|
||||
SelectorFunction<Collection<Size>, Size> photoSizeSelector = biggestSize();
|
||||
SelectorFunction<Collection<Size>, Size> previewSizeSelector = biggestSize();
|
||||
SelectorFunction<Collection<FocusMode>, FocusMode> focusModeSelector = firstAvailable(
|
||||
continuousFocus(),
|
||||
autoFocus(),
|
||||
fixed()
|
||||
);
|
||||
SelectorFunction<Collection<Flash>, Flash> flashSelector = FlashSelectors.off();
|
||||
SelectorFunction<Collection<Range<Integer>>, Range<Integer>> previewFpsRangeSelector = Selectors.nothing();
|
||||
SelectorFunction<Range<Integer>, Integer> sensorSensitivitySelector = Selectors.nothing();
|
||||
|
||||
Integer jpegQuality;
|
||||
|
||||
ScaleType scaleType = ScaleType.CENTER_CROP;
|
||||
|
||||
FrameProcessor frameProcessor = null;
|
||||
|
||||
Logger logger = Loggers.none();
|
||||
|
||||
CameraErrorCallback cameraErrorCallback = CameraErrorCallback.NULL;
|
||||
|
||||
FotoapparatBuilder(@NonNull Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cameraProvider decides which {@link CameraDevice} to use.
|
||||
*/
|
||||
public FotoapparatBuilder cameraProvider(@NonNull CameraProvider cameraProvider) {
|
||||
this.cameraProvider = cameraProvider;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param selector selects size of the photo (in pixels) from list of available sizes.
|
||||
*/
|
||||
public FotoapparatBuilder photoSize(@NonNull SelectorFunction<Collection<Size>, Size> selector) {
|
||||
photoSizeSelector = selector;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param selector selects size of preview stream (in pixels) from list of available sizes.
|
||||
*/
|
||||
public FotoapparatBuilder previewSize(@NonNull SelectorFunction<Collection<Size>, 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(@NonNull SelectorFunction<Collection<FocusMode>, FocusMode> selector) {
|
||||
focusModeSelector = selector;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param selector selects flash mode from list of available modes.
|
||||
*/
|
||||
public FotoapparatBuilder flash(@NonNull SelectorFunction<Collection<Flash>, Flash> selector) {
|
||||
flashSelector = selector;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param selector camera sensor position from list of available positions.
|
||||
*/
|
||||
public FotoapparatBuilder lensPosition(@NonNull SelectorFunction<Collection<LensPosition>, LensPosition> selector) {
|
||||
lensPositionSelector = selector;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param selector selects preview FPS range from list of available ranges.
|
||||
*/
|
||||
public FotoapparatBuilder previewFpsRange(@NonNull SelectorFunction<Collection<Range<Integer>>, Range<Integer>> selector) {
|
||||
previewFpsRangeSelector = selector;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param selector selects ISO value from range of available values.
|
||||
*/
|
||||
public FotoapparatBuilder sensorSensitivity(@NonNull SelectorFunction<Range<Integer>, Integer> selector) {
|
||||
sensorSensitivitySelector = selector;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param jpegQuality of the picture (1-100)
|
||||
*/
|
||||
public FotoapparatBuilder jpegQuality(@IntRange(from=0,to=100) @NonNull Integer jpegQuality) {
|
||||
this.jpegQuality = jpegQuality;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param frameProcessor receives preview frames for processing.
|
||||
* @see FrameProcessor
|
||||
*/
|
||||
public FotoapparatBuilder frameProcessor(@NonNull FrameProcessor frameProcessor) {
|
||||
this.frameProcessor = frameProcessor;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param logger logger which will print logs. No logger is set by default.
|
||||
* @see Loggers
|
||||
*/
|
||||
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(@NonNull CameraRenderer renderer) {
|
||||
this.renderer = renderer;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return set up instance of {@link Fotoapparat}.
|
||||
* @throws IllegalStateException if some mandatory parameters are not specified.
|
||||
*/
|
||||
public Fotoapparat build() {
|
||||
validate();
|
||||
|
||||
return Fotoapparat.create(this);
|
||||
}
|
||||
|
||||
private void validate() {
|
||||
if (cameraProvider == null) {
|
||||
throw new IllegalStateException("CameraProvider is mandatory.");
|
||||
}
|
||||
|
||||
if (renderer == null) {
|
||||
throw new IllegalStateException("CameraRenderer is mandatory.");
|
||||
}
|
||||
|
||||
if (lensPositionSelector == null) {
|
||||
throw new IllegalStateException("LensPosition selector is mandatory.");
|
||||
}
|
||||
|
||||
if (photoSizeSelector == null) {
|
||||
throw new IllegalStateException("Photo size selector is mandatory.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
package io.fotoapparat
|
||||
|
||||
import android.content.Context
|
||||
import io.fotoapparat.characteristic.LensPosition
|
||||
import io.fotoapparat.configuration.CameraConfiguration
|
||||
import io.fotoapparat.exception.camera.CameraException
|
||||
import io.fotoapparat.log.Logger
|
||||
import io.fotoapparat.log.none
|
||||
import io.fotoapparat.parameter.*
|
||||
import io.fotoapparat.preview.Frame
|
||||
import io.fotoapparat.selector.back
|
||||
import io.fotoapparat.selector.external
|
||||
import io.fotoapparat.selector.firstAvailable
|
||||
import io.fotoapparat.selector.front
|
||||
import io.fotoapparat.view.CameraRenderer
|
||||
import io.fotoapparat.view.CameraView
|
||||
|
||||
/**
|
||||
* Builder for [Fotoapparat].
|
||||
*/
|
||||
class FotoapparatBuilder internal constructor(private var context: Context) {
|
||||
|
||||
internal var lensPositionSelector: Iterable<LensPosition>.() -> LensPosition? = firstAvailable(
|
||||
back(),
|
||||
front(),
|
||||
external()
|
||||
)
|
||||
internal var cameraErrorCallback: (CameraException) -> Unit = {}
|
||||
internal var renderer: CameraRenderer? = null
|
||||
internal var scaleType: ScaleType = ScaleType.CenterCrop
|
||||
internal var logger: Logger = none()
|
||||
|
||||
internal var configuration = CameraConfiguration.default()
|
||||
|
||||
/**
|
||||
* @param selector camera sensor position from list of available positions.
|
||||
*/
|
||||
fun lensPosition(selector: Iterable<LensPosition>.() -> LensPosition?): FotoapparatBuilder {
|
||||
lensPositionSelector = selector
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* @param scaleType of preview inside the view.
|
||||
*/
|
||||
fun previewScaleType(scaleType: ScaleType): FotoapparatBuilder {
|
||||
this.scaleType = scaleType
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* @param selector selects resolution of the photo (in pixels) from list of available resolutions.
|
||||
*/
|
||||
fun photoResolution(selector: Iterable<Resolution>.() -> Resolution?): FotoapparatBuilder {
|
||||
configuration = configuration.copy(
|
||||
pictureResolution = selector
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* @param selector selects size of preview stream (in pixels) from list of available resolutions.
|
||||
*/
|
||||
fun previewResolution(selector: Iterable<Resolution>.() -> Resolution?): FotoapparatBuilder {
|
||||
configuration = configuration.copy(
|
||||
previewResolution = selector
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param selector selects focus mode from list of available modes.
|
||||
*/
|
||||
fun focusMode(selector: Iterable<FocusMode>.() -> FocusMode?): FotoapparatBuilder {
|
||||
configuration = configuration.copy(
|
||||
focusMode = selector
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* @param selector selects flash mode from list of available modes.
|
||||
*/
|
||||
fun flash(selector: Iterable<Flash>.() -> Flash?): FotoapparatBuilder {
|
||||
configuration = configuration.copy(
|
||||
flashMode = selector
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* @param selector selects preview FPS range from list of available ranges.
|
||||
*/
|
||||
fun previewFpsRange(selector: Iterable<FpsRange>.() -> FpsRange?): FotoapparatBuilder {
|
||||
configuration = configuration.copy(
|
||||
previewFpsRange = selector
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* @param selector selects ISO value from range of available values.
|
||||
*/
|
||||
fun sensorSensitivity(selector: Iterable<Int>.() -> Int?): FotoapparatBuilder {
|
||||
configuration = configuration.copy(
|
||||
sensorSensitivity = selector
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* @param selector of the Jpeg picture quality.
|
||||
*/
|
||||
fun jpegQuality(selector: IntRange.() -> Int?): FotoapparatBuilder {
|
||||
configuration = configuration.copy(
|
||||
jpegQuality = selector
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* @param frameProcessor receives preview frames for processing.
|
||||
* @see FrameProcessor
|
||||
*/
|
||||
fun frameProcessor(frameProcessor: (Frame) -> Unit): FotoapparatBuilder {
|
||||
configuration = configuration.copy(
|
||||
frameProcessor = frameProcessor
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* @param logger logger which will print logs. No logger is set by default.
|
||||
* @see io.fotoapparat.log.Loggers
|
||||
*/
|
||||
fun logger(logger: Logger): FotoapparatBuilder {
|
||||
this.logger = logger
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callback which will be notified when camera error happens in Fotoapparat.
|
||||
* @see CameraErrorCallback
|
||||
*/
|
||||
fun cameraErrorCallback(callback: (CameraException) -> Unit): FotoapparatBuilder {
|
||||
cameraErrorCallback = callback
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* @param renderer view which will draw the stream from the camera.
|
||||
* @see CameraView
|
||||
*/
|
||||
fun into(renderer: CameraRenderer): FotoapparatBuilder {
|
||||
this.renderer = renderer
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* @return set up instance of [Fotoapparat].
|
||||
* @throws IllegalStateException if some mandatory parameters are not specified.
|
||||
*/
|
||||
fun build() = buildInternal(
|
||||
renderer = renderer
|
||||
)
|
||||
|
||||
private fun buildInternal(
|
||||
renderer: CameraRenderer?
|
||||
): Fotoapparat {
|
||||
if (renderer == null) {
|
||||
throw IllegalStateException("CameraRenderer is mandatory.")
|
||||
}
|
||||
|
||||
return Fotoapparat(
|
||||
context = context,
|
||||
view = renderer,
|
||||
lensPosition = lensPositionSelector,
|
||||
cameraConfiguration = configuration,
|
||||
scaleType = scaleType,
|
||||
cameraErrorCallback = cameraErrorCallback,
|
||||
logger = logger
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
package io.fotoapparat;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Switches between different instances of {@link Fotoapparat}. Convenient when you want to allow
|
||||
* user to switch between different cameras or configurations.
|
||||
* <p>
|
||||
* This class is not thread safe. Consider using it from a single thread.
|
||||
*/
|
||||
public class FotoapparatSwitcher {
|
||||
|
||||
@NonNull
|
||||
private Fotoapparat fotoapparat;
|
||||
|
||||
private boolean started = false;
|
||||
|
||||
private FotoapparatSwitcher(@NonNull Fotoapparat fotoapparat) {
|
||||
this.fotoapparat = fotoapparat;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link FotoapparatSwitcher} with given {@link Fotoapparat} used by default.
|
||||
*/
|
||||
public static FotoapparatSwitcher withDefault(@NonNull Fotoapparat fotoapparat) {
|
||||
return new FotoapparatSwitcher(fotoapparat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts {@link Fotoapparat} associated with this switcher. Every new {@link Fotoapparat} will
|
||||
* be started automatically until {@link #stop()} is called.
|
||||
*
|
||||
* @throws IllegalStateException if switcher is already started.
|
||||
*/
|
||||
public void start() {
|
||||
fotoapparat.start();
|
||||
|
||||
started = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops currently used {@link Fotoapparat}.
|
||||
*
|
||||
* @throws IllegalStateException if switcher is already stopped.
|
||||
*/
|
||||
public void stop() {
|
||||
fotoapparat.stop();
|
||||
|
||||
started = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches to another {@link Fotoapparat}. If switcher is already started then previously used
|
||||
* {@link Fotoapparat} will be stopped automatically and new {@link Fotoapparat} will be
|
||||
* started.
|
||||
*
|
||||
* @param fotoapparat new {@link Fotoapparat} to use.
|
||||
* @throws NullPointerException if given {@link Fotoapparat} is {@code null}.
|
||||
*/
|
||||
public void switchTo(@NonNull Fotoapparat fotoapparat) {
|
||||
if (started) {
|
||||
this.fotoapparat.stop();
|
||||
fotoapparat.start();
|
||||
}
|
||||
|
||||
this.fotoapparat = fotoapparat;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return currently used instance of {@link Fotoapparat}.
|
||||
*/
|
||||
@NonNull
|
||||
public Fotoapparat getCurrentFotoapparat() {
|
||||
return fotoapparat;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package io.fotoapparat.capability
|
||||
|
||||
import io.fotoapparat.parameter.*
|
||||
import io.fotoapparat.util.lineSeparator
|
||||
import io.fotoapparat.util.wrap
|
||||
|
||||
/**
|
||||
* Capabilities of camera hardware.
|
||||
*
|
||||
* Sensor sensitivities is not guaranteed to always contain a value.
|
||||
*/
|
||||
data class Capabilities(
|
||||
val canZoom: Boolean,
|
||||
val flashModes: Set<Flash>,
|
||||
val focusModes: Set<FocusMode>,
|
||||
val canSmoothZoom: Boolean,
|
||||
val jpegQualityRange: IntRange,
|
||||
val previewFpsRanges: Set<FpsRange>,
|
||||
val antiBandingModes: Set<AntiBandingMode>,
|
||||
val pictureResolutions: Set<Resolution>,
|
||||
val previewResolutions: Set<Resolution>,
|
||||
val sensorSensitivities: Set<Int>
|
||||
) {
|
||||
|
||||
init {
|
||||
flashModes.ensureNotEmpty()
|
||||
focusModes.ensureNotEmpty()
|
||||
antiBandingModes.ensureNotEmpty()
|
||||
previewFpsRanges.ensureNotEmpty()
|
||||
pictureResolutions.ensureNotEmpty()
|
||||
previewResolutions.ensureNotEmpty()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "Capabilities" + lineSeparator +
|
||||
"canZoom:" + canZoom.wrap() +
|
||||
"flashModes:" + flashModes.wrap() +
|
||||
"focusModes:" + focusModes.wrap() +
|
||||
"canSmoothZoom:" + canSmoothZoom.wrap() +
|
||||
"jpegQualityRange:" + jpegQualityRange.wrap() +
|
||||
"antiBandingModes:" + antiBandingModes.wrap() +
|
||||
"previewFpsRanges:" + previewFpsRanges.wrap() +
|
||||
"pictureResolutions:" + pictureResolutions.wrap() +
|
||||
"previewResolutions:" + previewResolutions.wrap() +
|
||||
"sensorSensitivities:" + sensorSensitivities.wrap()
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <reified E> Set<E>.ensureNotEmpty() {
|
||||
if (isEmpty()) {
|
||||
throw IllegalArgumentException("Capabilities cannot have an empty Set<${E::class.java.simpleName}>.")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package io.fotoapparat.capability.provide
|
||||
|
||||
import android.hardware.Camera
|
||||
import io.fotoapparat.capability.Capabilities
|
||||
import io.fotoapparat.parameter.SupportedParameters
|
||||
import io.fotoapparat.parameter.camera.convert.*
|
||||
|
||||
/**
|
||||
* Returns the [io.fotoapparat.capability.Capabilities] of the given [Camera].
|
||||
*/
|
||||
internal fun Camera.getCapabilities() = SupportedParameters(parameters).getCapabilities()
|
||||
|
||||
private fun SupportedParameters.getCapabilities(): Capabilities {
|
||||
return Capabilities(
|
||||
canZoom = supportedZoom,
|
||||
flashModes = flashModes.extract { it.toFlash() },
|
||||
focusModes = focusModes.extract { it.toFocusMode() },
|
||||
canSmoothZoom = supportedSmoothZoom,
|
||||
jpegQualityRange = jpegQualityRange,
|
||||
antiBandingModes = supportedAutoBandingModes.extract { it.toAntiBandingMode() },
|
||||
sensorSensitivities = sensorSensitivities.toSet(),
|
||||
previewFpsRanges = supportedPreviewFpsRanges.extract { it.toFpsRange() },
|
||||
pictureResolutions = pictureResolutions.mapSizes(),
|
||||
previewResolutions = previewResolutions.mapSizes()
|
||||
)
|
||||
}
|
||||
|
||||
private fun <Parameter : Any, Code> List<Code>.extract(converter: (Code) -> Parameter?) = mapNotNull { converter(it) }.toSet()
|
||||
|
||||
private fun Collection<Camera.Size>.mapSizes() = map { it.toResolution() }.toSet()
|
||||
@@ -0,0 +1,18 @@
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package io.fotoapparat.characteristic
|
||||
|
||||
import android.hardware.Camera
|
||||
|
||||
/**
|
||||
* Returns the [Characteristics] for the given `cameraId`.
|
||||
*/
|
||||
internal fun getCharacteristics(cameraId: Int): Characteristics {
|
||||
val info = Camera.CameraInfo()
|
||||
Camera.getCameraInfo(cameraId, info)
|
||||
return Characteristics(
|
||||
cameraId,
|
||||
info.facing.toLensPosition(),
|
||||
info.orientation
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package io.fotoapparat.characteristic
|
||||
|
||||
/**
|
||||
* A camera characteristic.
|
||||
*/
|
||||
interface Characteristic
|
||||
@@ -0,0 +1,10 @@
|
||||
package io.fotoapparat.characteristic
|
||||
|
||||
/**
|
||||
* A set of information about the camera.
|
||||
*/
|
||||
internal data class Characteristics(
|
||||
val cameraId: Int,
|
||||
val lensPosition: LensPosition,
|
||||
val orientation: Int
|
||||
)
|
||||
+6
-6
@@ -1,23 +1,23 @@
|
||||
package io.fotoapparat.parameter;
|
||||
package io.fotoapparat.characteristic
|
||||
|
||||
/**
|
||||
* The camera position relatively to the screen of the device.
|
||||
*/
|
||||
public enum LensPosition {
|
||||
sealed class LensPosition : Characteristic {
|
||||
|
||||
/**
|
||||
* The back camera.
|
||||
*/
|
||||
BACK,
|
||||
object Back : LensPosition()
|
||||
|
||||
/**
|
||||
* The front camera.
|
||||
*/
|
||||
FRONT,
|
||||
object Front : LensPosition()
|
||||
|
||||
/**
|
||||
* An external camera.
|
||||
*/
|
||||
EXTERNAL
|
||||
object External : LensPosition()
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package io.fotoapparat.characteristic
|
||||
|
||||
import android.hardware.Camera
|
||||
import io.fotoapparat.exception.camera.CameraException
|
||||
|
||||
/**
|
||||
* Maps between [LensPosition] and Camera v1 lens position code id.
|
||||
*
|
||||
* @receiver Camera facing info id.
|
||||
* @return [LensPosition] from the given lens position code id.
|
||||
* `null` if position code id is not supported.
|
||||
*/
|
||||
internal fun Int.toLensPosition(): LensPosition {
|
||||
return when (this) {
|
||||
Camera.CameraInfo.CAMERA_FACING_FRONT -> LensPosition.Front
|
||||
Camera.CameraInfo.CAMERA_FACING_BACK -> LensPosition.Back
|
||||
else -> throw IllegalArgumentException("Lens position $this is not supported.")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps between [LensPosition] and Camera v1 code id.
|
||||
*
|
||||
* @receiver [LensPosition]
|
||||
* @return code of the camera as in [Camera.CameraInfo].
|
||||
*/
|
||||
fun LensPosition.toCameraId(): Int {
|
||||
return (0 until Camera.getNumberOfCameras())
|
||||
.find {
|
||||
this == getCharacteristics(it).lensPosition
|
||||
}
|
||||
?: throw CameraException("Device has no camera for the desired lens position(s).")
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package io.fotoapparat.configuration
|
||||
|
||||
import io.fotoapparat.parameter.*
|
||||
import io.fotoapparat.preview.Frame
|
||||
import io.fotoapparat.preview.FrameProcessor
|
||||
import io.fotoapparat.selector.*
|
||||
|
||||
|
||||
private const val DEFAULT_JPEG_QUALITY = 90
|
||||
|
||||
/**
|
||||
* A camera configuration which has all it's selectors defined.
|
||||
*/
|
||||
data class CameraConfiguration(
|
||||
override val flashMode: Iterable<Flash>.() -> Flash? = off(),
|
||||
override val focusMode: Iterable<FocusMode>.() -> FocusMode? = firstAvailable(
|
||||
continuousFocusPicture(),
|
||||
autoFocus(),
|
||||
fixed()
|
||||
),
|
||||
override val jpegQuality: (IntRange) -> Int? = manualJpegQuality(DEFAULT_JPEG_QUALITY),
|
||||
override val frameProcessor: (Frame) -> Unit = {},
|
||||
override val previewFpsRange: Iterable<FpsRange>.() -> FpsRange? = highestFps(),
|
||||
override val antiBandingMode: Iterable<AntiBandingMode>.() -> AntiBandingMode? = firstAvailable(
|
||||
auto(),
|
||||
hz50(),
|
||||
hz60(),
|
||||
none()
|
||||
),
|
||||
override val sensorSensitivity: (Iterable<Int>.() -> Int?)? = null,
|
||||
override val pictureResolution: Iterable<Resolution>.() -> Resolution? = highestResolution(),
|
||||
override val previewResolution: Iterable<Resolution>.() -> Resolution? = highestResolution()
|
||||
) : Configuration {
|
||||
|
||||
/**
|
||||
* Builder for [CameraConfiguration].
|
||||
*/
|
||||
class Builder internal constructor() {
|
||||
|
||||
private var cameraConfiguration: CameraConfiguration = default()
|
||||
|
||||
fun flash(selector: (Iterable<Flash>.() -> Flash?)): Builder {
|
||||
cameraConfiguration = cameraConfiguration.copy(
|
||||
flashMode = selector
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
fun focusMode(selector: (Iterable<FocusMode>.() -> FocusMode?)): Builder {
|
||||
cameraConfiguration = cameraConfiguration.copy(
|
||||
focusMode = selector
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
fun previewFpsRange(selector: (Iterable<FpsRange>.() -> FpsRange?)): Builder {
|
||||
cameraConfiguration = cameraConfiguration.copy(
|
||||
previewFpsRange = selector
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
fun sensorSensitivity(selector: (Iterable<Int>.() -> Int?)): Builder {
|
||||
cameraConfiguration = cameraConfiguration.copy(
|
||||
sensorSensitivity = selector
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
fun antiBandingMode(selector: Iterable<AntiBandingMode>.() -> AntiBandingMode?): Builder {
|
||||
cameraConfiguration.copy(
|
||||
antiBandingMode = selector
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
fun jpegQuality(selector: IntRange.() -> Int?): Builder {
|
||||
cameraConfiguration.copy(
|
||||
jpegQuality = selector
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
fun previewResolution(selector: (Iterable<Resolution>.() -> Resolution?)): Builder {
|
||||
cameraConfiguration = cameraConfiguration.copy(
|
||||
previewResolution = selector
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
fun photoResolution(selector: (Iterable<Resolution>.() -> Resolution?)): Builder {
|
||||
cameraConfiguration = cameraConfiguration.copy(
|
||||
pictureResolution = selector
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
fun frameProcessor(frameProcessor: FrameProcessor): Builder {
|
||||
cameraConfiguration = cameraConfiguration.copy(
|
||||
frameProcessor = { frameProcessor.process(it) }
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a new [CameraConfiguration].
|
||||
*/
|
||||
fun build(): CameraConfiguration = cameraConfiguration
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Alias for [CameraConfiguration.default]
|
||||
*/
|
||||
@JvmStatic
|
||||
fun standard() = default()
|
||||
|
||||
/**
|
||||
* Default [CameraConfiguration].
|
||||
*/
|
||||
@JvmStatic
|
||||
fun default() = CameraConfiguration()
|
||||
|
||||
/**
|
||||
* Creates a new [CameraConfiguration.Builder].
|
||||
*/
|
||||
@JvmStatic
|
||||
fun builder() = CameraConfiguration.Builder()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package io.fotoapparat.configuration
|
||||
|
||||
import io.fotoapparat.parameter.*
|
||||
import io.fotoapparat.preview.Frame
|
||||
|
||||
interface Configuration {
|
||||
val flashMode: (Iterable<Flash>.() -> Flash?)?
|
||||
val focusMode: (Iterable<FocusMode>.() -> FocusMode?)?
|
||||
val jpegQuality: ((IntRange) -> Int?)?
|
||||
val frameProcessor: ((Frame) -> Unit)?
|
||||
val previewFpsRange: (Iterable<FpsRange>.() -> FpsRange?)?
|
||||
val antiBandingMode: (Iterable<AntiBandingMode>.() -> AntiBandingMode?)?
|
||||
val sensorSensitivity: (Iterable<Int>.() -> Int?)?
|
||||
val previewResolution: (Iterable<Resolution>.() -> Resolution?)?
|
||||
val pictureResolution: (Iterable<Resolution>.() -> Resolution?)?
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package io.fotoapparat.configuration
|
||||
|
||||
import io.fotoapparat.parameter.*
|
||||
import io.fotoapparat.preview.Frame
|
||||
|
||||
/**
|
||||
* A camera update configuration.
|
||||
*/
|
||||
data class UpdateConfiguration(
|
||||
override val flashMode: (Iterable<Flash>.() -> Flash?)? = null,
|
||||
override val focusMode: (Iterable<FocusMode>.() -> FocusMode?)? = null,
|
||||
override val jpegQuality: ((IntRange) -> Int?)? = null,
|
||||
override val frameProcessor: ((Frame) -> Unit)? = null,
|
||||
override val previewFpsRange: (Iterable<FpsRange>.() -> FpsRange?)? = null,
|
||||
override val antiBandingMode: (Iterable<AntiBandingMode>.() -> AntiBandingMode?)? = null,
|
||||
override val sensorSensitivity: (Iterable<Int>.() -> Int?)? = null,
|
||||
override val previewResolution: (Iterable<Resolution>.() -> Resolution?)? = null,
|
||||
override val pictureResolution: (Iterable<Resolution>.() -> Resolution?)? = null
|
||||
) : Configuration {
|
||||
|
||||
/**
|
||||
* Builder for [UpdateConfiguration].
|
||||
*/
|
||||
class Builder internal constructor() {
|
||||
|
||||
private var configuration = UpdateConfiguration()
|
||||
|
||||
fun flash(selector: Iterable<Flash>.() -> Flash?): Builder {
|
||||
configuration.copy(
|
||||
flashMode = selector
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
fun focusMode(selector: Iterable<FocusMode>.() -> FocusMode?): Builder {
|
||||
configuration.copy(
|
||||
focusMode = selector
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
fun previewFpsRange(selector: Iterable<FpsRange>.() -> FpsRange?): Builder {
|
||||
configuration.copy(
|
||||
previewFpsRange = selector
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
fun sensorSensitivity(selector: Iterable<Int>.() -> Int?): Builder {
|
||||
configuration.copy(
|
||||
sensorSensitivity = selector
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
fun antiBandingMode(selector: Iterable<AntiBandingMode>.() -> AntiBandingMode?): Builder {
|
||||
configuration.copy(
|
||||
antiBandingMode = selector
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
fun jpegQuality(selector: IntRange.() -> Int?): Builder {
|
||||
configuration.copy(
|
||||
jpegQuality = selector
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
fun previewResolution(selector: Iterable<Resolution>.() -> Resolution?): Builder {
|
||||
configuration.copy(
|
||||
previewResolution = selector
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
fun photoResolution(selector: Iterable<Resolution>.() -> Resolution?): Builder {
|
||||
configuration.copy(
|
||||
pictureResolution = selector
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
fun frameProcessor(frameProcessor: (Frame) -> Unit): Builder {
|
||||
configuration.copy(
|
||||
frameProcessor = frameProcessor
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a new [UpdateConfiguration].
|
||||
*/
|
||||
fun build(): UpdateConfiguration = configuration
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Creates a new [UpdateConfiguration.Builder].
|
||||
*/
|
||||
@JvmStatic
|
||||
fun builder() = UpdateConfiguration.Builder()
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
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);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package io.fotoapparat.error
|
||||
|
||||
import android.os.Looper
|
||||
import io.fotoapparat.exception.camera.CameraException
|
||||
import io.fotoapparat.hardware.executeMainThread
|
||||
|
||||
/**
|
||||
* @return CameraErrorCallback which will always move execution to the main thread.
|
||||
*/
|
||||
fun ((CameraException) -> Unit).onMainThread(): (CameraException) -> Unit = { cameraException ->
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
this(cameraException)
|
||||
} else {
|
||||
executeMainThread { this(cameraException) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package io.fotoapparat.exception
|
||||
|
||||
/**
|
||||
* Thrown when there is a problem while saving the file.
|
||||
*/
|
||||
class FileSaveException(cause: Throwable) : RuntimeException(cause)
|
||||
@@ -0,0 +1,6 @@
|
||||
package io.fotoapparat.exception
|
||||
|
||||
/**
|
||||
* Thrown when zoom level is outside of [0..1] range.
|
||||
*/
|
||||
class LevelOutOfRangeException(zoomLevel: Float) : RuntimeException(zoomLevel.toString() + " is out of range [0..1]")
|
||||
@@ -0,0 +1,11 @@
|
||||
package io.fotoapparat.exception
|
||||
|
||||
/**
|
||||
* Exception which is not caused by developer and can be either ignored or recovered from.
|
||||
*/
|
||||
open class RecoverableRuntimeException : RuntimeException {
|
||||
|
||||
constructor(message: String) : super(message)
|
||||
|
||||
constructor(throwable: Throwable) : super(throwable)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package io.fotoapparat.exception
|
||||
|
||||
/**
|
||||
* Thrown when it is not possible to decode bitmap from byte array.
|
||||
*/
|
||||
class UnableToDecodeBitmapException : RecoverableRuntimeException("Unable to decode bitmap")
|
||||
@@ -0,0 +1,12 @@
|
||||
package io.fotoapparat.exception.camera
|
||||
|
||||
/**
|
||||
* A generic camera exception.
|
||||
*/
|
||||
open class CameraException(
|
||||
message: String,
|
||||
cause: Throwable? = null
|
||||
) : RuntimeException(
|
||||
message,
|
||||
cause
|
||||
)
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package io.fotoapparat.exception.camera
|
||||
|
||||
import io.fotoapparat.parameter.Parameter
|
||||
|
||||
/**
|
||||
* Thrown to indicate that a selected [Parameter] is not in the supported set.
|
||||
*
|
||||
* e.g. Camera supports flash `on`, `off` & `auto` and you ask for a `tomato`.
|
||||
*/
|
||||
internal class InvalidConfigurationException : CameraException {
|
||||
constructor(
|
||||
value: Any,
|
||||
klass: Class<out Parameter>,
|
||||
supportedParameters: Collection<Parameter>
|
||||
) : super(
|
||||
"${klass.simpleName} configuration selector selected value $value. " +
|
||||
"However it's not in the supported set of values. " +
|
||||
"Supported parameters: $supportedParameters"
|
||||
)
|
||||
|
||||
constructor(
|
||||
value: Any,
|
||||
klass: Class<out Comparable<*>>,
|
||||
supportedRange: ClosedRange<*>
|
||||
) : super(
|
||||
"${klass.simpleName} configuration selector selected value $value. " +
|
||||
"However it's not in the supported set of values. " +
|
||||
"Supported parameters from: ${supportedRange.start} to ${supportedRange.endInclusive}."
|
||||
)
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package io.fotoapparat.exception.camera
|
||||
|
||||
/**
|
||||
* Thrown when the preview surface didn't become available.
|
||||
*/
|
||||
class UnavailableSurfaceException : CameraException(
|
||||
"No preview surface became available before CameraView got detached from window. " +
|
||||
"Camera didn't start. You may ignore this exception."
|
||||
)
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package io.fotoapparat.exception.camera
|
||||
|
||||
import io.fotoapparat.parameter.Parameter
|
||||
|
||||
/**
|
||||
* Thrown to indicate that a configuration selector couldn't select a value.
|
||||
*/
|
||||
internal class UnsupportedConfigurationException : CameraException {
|
||||
constructor(
|
||||
klass: Class<out Parameter>,
|
||||
supportedParameters: Collection<Parameter>
|
||||
) : super(
|
||||
"${klass.simpleName} configuration selector couldn't select a value. " +
|
||||
"Supported parameters: $supportedParameters"
|
||||
)
|
||||
|
||||
constructor(
|
||||
configurationName: String,
|
||||
supportedRange: ClosedRange<*>
|
||||
) : super(
|
||||
"$configurationName configuration selector couldn't select a value. " +
|
||||
"Supported parameters from: ${supportedRange.start} to ${supportedRange.endInclusive}."
|
||||
)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package io.fotoapparat.exception.camera
|
||||
|
||||
/**
|
||||
* Thrown to indicate that the device has no camera for the desired lens position(s).
|
||||
*/
|
||||
class UnsupportedLensException : CameraException("Device has no camera for the desired lens position(s).")
|
||||
@@ -0,0 +1,19 @@
|
||||
package io.fotoapparat.exif
|
||||
|
||||
import io.fotoapparat.result.Photo
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Writes Exif orientation attributes.
|
||||
*/
|
||||
internal interface ExifOrientationWriter {
|
||||
|
||||
/**
|
||||
* Writes EXIF orientation tag into a file, overwriting it if it already exists.
|
||||
*
|
||||
* @param file File of the image.
|
||||
* @param photo Photo stored in the file.
|
||||
* @throws FileSaveException If writing has failed.
|
||||
*/
|
||||
fun writeExifOrientation(file: File, photo: Photo)
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package io.fotoapparat.exif
|
||||
|
||||
import android.media.ExifInterface
|
||||
import io.fotoapparat.exception.FileSaveException
|
||||
import io.fotoapparat.result.Photo
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* Writes Exif attributes.
|
||||
*/
|
||||
internal object ExifWriter : ExifOrientationWriter {
|
||||
|
||||
|
||||
@Throws(FileSaveException::class)
|
||||
override fun writeExifOrientation(file: File, photo: Photo) {
|
||||
try {
|
||||
val exifInterface = ExifInterface(file.path)
|
||||
exifInterface.setAttribute(
|
||||
ExifInterface.TAG_ORIENTATION,
|
||||
toExifOrientation(photo.rotationDegrees).toString()
|
||||
)
|
||||
exifInterface.saveAttributes()
|
||||
} catch (e: IOException) {
|
||||
throw FileSaveException(e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun toExifOrientation(rotationDegrees: Int): Int {
|
||||
val compensationRotationDegrees = (360 - rotationDegrees) % 360
|
||||
|
||||
return when (compensationRotationDegrees) {
|
||||
90 -> ExifInterface.ORIENTATION_ROTATE_90
|
||||
180 -> ExifInterface.ORIENTATION_ROTATE_180
|
||||
270 -> ExifInterface.ORIENTATION_ROTATE_270
|
||||
else -> ExifInterface.ORIENTATION_NORMAL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
package io.fotoapparat.hardware;
|
||||
|
||||
import android.support.annotation.FloatRange;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.fotoapparat.hardware.operators.AutoFocusOperator;
|
||||
import io.fotoapparat.hardware.operators.CapabilitiesOperator;
|
||||
import io.fotoapparat.hardware.operators.CaptureOperator;
|
||||
import io.fotoapparat.hardware.operators.ConnectionOperator;
|
||||
import io.fotoapparat.hardware.operators.ExposureMeasurementOperator;
|
||||
import io.fotoapparat.hardware.operators.OrientationOperator;
|
||||
import io.fotoapparat.hardware.operators.ParametersOperator;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Abstraction for camera hardware.
|
||||
*/
|
||||
public interface CameraDevice extends CaptureOperator,
|
||||
PreviewOperator, CapabilitiesOperator, OrientationOperator, ParametersOperator,
|
||||
ConnectionOperator, SurfaceOperator, PreviewStreamOperator, RendererParametersOperator,
|
||||
ExposureMeasurementOperator, AutoFocusOperator, AvailableLensPositionsProvider,
|
||||
ZoomOperator {
|
||||
|
||||
@Override
|
||||
void open(LensPosition lensPosition);
|
||||
|
||||
@Override
|
||||
void close();
|
||||
|
||||
@Override
|
||||
void startPreview();
|
||||
|
||||
@Override
|
||||
void stopPreview();
|
||||
|
||||
@Override
|
||||
void setDisplaySurface(Object displaySurface);
|
||||
|
||||
@Override
|
||||
void setDisplayOrientation(int degrees);
|
||||
|
||||
@Override
|
||||
void updateParameters(Parameters parameters);
|
||||
|
||||
@Override
|
||||
Capabilities getCapabilities();
|
||||
|
||||
@Override
|
||||
FocusResult autoFocus();
|
||||
|
||||
@Override
|
||||
void measureExposure();
|
||||
|
||||
@Override
|
||||
Photo takePicture();
|
||||
|
||||
@Override
|
||||
PreviewStream getPreviewStream();
|
||||
|
||||
@Override
|
||||
RendererParameters getRendererParameters();
|
||||
|
||||
@Override
|
||||
List<LensPosition> getAvailableLensPositions();
|
||||
|
||||
@Override
|
||||
void setZoom(@FloatRange(from = 0f, to = 1f) float level);
|
||||
|
||||
Parameters getCurrentParameters();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,380 @@
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package io.fotoapparat.hardware
|
||||
|
||||
import android.hardware.Camera
|
||||
import android.media.MediaRecorder
|
||||
import android.support.annotation.FloatRange
|
||||
import android.view.Surface
|
||||
import io.fotoapparat.capability.Capabilities
|
||||
import io.fotoapparat.capability.provide.getCapabilities
|
||||
import io.fotoapparat.characteristic.Characteristics
|
||||
import io.fotoapparat.characteristic.LensPosition
|
||||
import io.fotoapparat.characteristic.toCameraId
|
||||
import io.fotoapparat.exception.camera.CameraException
|
||||
import io.fotoapparat.hardware.orientation.computeDisplayOrientation
|
||||
import io.fotoapparat.hardware.orientation.computeImageOrientation
|
||||
import io.fotoapparat.log.Logger
|
||||
import io.fotoapparat.parameter.Resolution
|
||||
import io.fotoapparat.parameter.camera.CameraParameters
|
||||
import io.fotoapparat.parameter.camera.apply.applyNewParameters
|
||||
import io.fotoapparat.preview.Frame
|
||||
import io.fotoapparat.preview.PreviewStream
|
||||
import io.fotoapparat.result.FocusResult
|
||||
import io.fotoapparat.result.Photo
|
||||
import io.fotoapparat.view.Preview
|
||||
import java.io.IOException
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
typealias PreviewSize = io.fotoapparat.parameter.Resolution
|
||||
|
||||
/**
|
||||
* Camera.
|
||||
*/
|
||||
internal open class CameraDevice(
|
||||
private val logger: Logger,
|
||||
val characteristics: Characteristics
|
||||
) {
|
||||
|
||||
private lateinit var cameraParameters: CameraParameters
|
||||
private lateinit var previewStream: PreviewStream
|
||||
private lateinit var capabilities: Capabilities
|
||||
private lateinit var surface: Surface
|
||||
private lateinit var camera: Camera
|
||||
|
||||
private var cachedZoomParameters: Camera.Parameters? = null
|
||||
private var displayRotation = 0
|
||||
var imageRotation = 0
|
||||
|
||||
/**
|
||||
* Opens a connection to a camera.
|
||||
*/
|
||||
open fun open() {
|
||||
logger.recordMethod()
|
||||
|
||||
val lensPosition = characteristics.lensPosition
|
||||
val cameraId = lensPosition.toCameraId()
|
||||
|
||||
try {
|
||||
camera = Camera.open(cameraId)
|
||||
capabilities = camera.getCapabilities()
|
||||
previewStream = PreviewStream(camera)
|
||||
} catch (e: RuntimeException) {
|
||||
throw CameraException(
|
||||
message = "Failed to open camera with lens position: $lensPosition and id: $cameraId",
|
||||
cause = e
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the connection to a camera.
|
||||
*/
|
||||
open fun close() {
|
||||
logger.recordMethod()
|
||||
|
||||
camera.release()
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts preview.
|
||||
*/
|
||||
open fun startPreview() {
|
||||
logger.recordMethod()
|
||||
|
||||
try {
|
||||
camera.startPreview()
|
||||
} catch (e: RuntimeException) {
|
||||
throw CameraException(
|
||||
message = "Failed to start preview for camera with lens " +
|
||||
"position: ${characteristics.lensPosition} and id: ${characteristics.cameraId}",
|
||||
cause = e
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops preview.
|
||||
*/
|
||||
open fun stopPreview() {
|
||||
logger.recordMethod()
|
||||
|
||||
camera.stopPreview()
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock camera.
|
||||
*/
|
||||
open fun unlock() {
|
||||
logger.recordMethod()
|
||||
|
||||
camera.unlock()
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock camera.
|
||||
*/
|
||||
open fun lock() {
|
||||
logger.recordMethod()
|
||||
|
||||
camera.lock()
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes a still photo capture action.
|
||||
*
|
||||
* @return The captured photo.
|
||||
*/
|
||||
open fun takePhoto(): Photo {
|
||||
logger.recordMethod()
|
||||
|
||||
return camera.takePhoto(imageRotation)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Capabilities] of the camera.
|
||||
*/
|
||||
open fun getCapabilities(): Capabilities {
|
||||
logger.recordMethod()
|
||||
|
||||
return capabilities
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [CameraParameters] used.
|
||||
*/
|
||||
open fun getParameters(): CameraParameters {
|
||||
logger.recordMethod()
|
||||
|
||||
return cameraParameters
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the desired camera parameters.
|
||||
*/
|
||||
open fun updateParameters(cameraParameters: CameraParameters) {
|
||||
logger.recordMethod()
|
||||
|
||||
this.cameraParameters = cameraParameters
|
||||
|
||||
logger.log("New camera parameters are: $cameraParameters")
|
||||
|
||||
camera.updateParameters(cameraParameters)
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the frame processor.
|
||||
*/
|
||||
open fun updateFrameProcessor(frameProcessor: ((Frame) -> Unit)?) {
|
||||
logger.recordMethod()
|
||||
|
||||
previewStream.updateProcessorSafely(frameProcessor)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current orientation of the display.
|
||||
*/
|
||||
open fun setDisplayOrientation(degrees: Int) {
|
||||
logger.recordMethod()
|
||||
|
||||
imageRotation = computeImageOrientation(
|
||||
degrees = degrees,
|
||||
characteristics = characteristics
|
||||
)
|
||||
|
||||
displayRotation = computeDisplayOrientation(
|
||||
degrees = degrees,
|
||||
characteristics = characteristics
|
||||
)
|
||||
|
||||
logger.log("Image Rotation is: $imageRotation. Display rotation is: $displayRotation")
|
||||
|
||||
previewStream.frameOrientation = imageRotation
|
||||
camera.setDisplayOrientation(displayRotation)
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes zoom level of the camera. Must be called only if zoom is supported.
|
||||
*
|
||||
* @param level normalized zoom level. Value in range [0..1].
|
||||
*/
|
||||
open fun setZoom(@FloatRange(from = 0.0, to = 1.0) level: Float) {
|
||||
logger.recordMethod()
|
||||
|
||||
setZoomSafely(level)
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs auto focus. This is a blocking operation which returns the result of the operation
|
||||
* when auto focus completes.
|
||||
*/
|
||||
open fun autoFocus(): FocusResult {
|
||||
logger.recordMethod()
|
||||
|
||||
return camera.focusSafely()
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the desired surface on which the camera's preview will be displayed.
|
||||
*/
|
||||
open fun setDisplaySurface(preview: Preview) {
|
||||
logger.recordMethod()
|
||||
|
||||
surface = camera.setDisplaySurface(preview)
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches the camera to the [MediaRecorder].
|
||||
*/
|
||||
open fun attachRecordingCamera(mediaRecorder: MediaRecorder) {
|
||||
logger.recordMethod()
|
||||
|
||||
mediaRecorder.setCamera(camera)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [Resolution] of the displayed preview.
|
||||
*/
|
||||
open fun getPreviewResolution(): Resolution {
|
||||
logger.recordMethod()
|
||||
|
||||
val previewResolution = camera.getPreviewResolution(imageRotation)
|
||||
|
||||
logger.log("Preview resolution is: $previewResolution")
|
||||
|
||||
return previewResolution
|
||||
}
|
||||
|
||||
|
||||
private fun setZoomSafely(@FloatRange(from = 0.0, to = 1.0) level: Float) {
|
||||
try {
|
||||
setZoomUnsafe(level)
|
||||
} catch (e: Exception) {
|
||||
logger.log("Unable to change zoom level to " + level + " e: " + e.message)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setZoomUnsafe(@FloatRange(from = 0.0, to = 1.0) level: Float) {
|
||||
(cachedZoomParameters ?: camera.parameters)
|
||||
.apply {
|
||||
zoom = (maxZoom * level).toInt()
|
||||
}
|
||||
.let {
|
||||
cachedZoomParameters = it
|
||||
camera.parameters = it
|
||||
}
|
||||
}
|
||||
|
||||
private fun Camera.focusSafely(): FocusResult {
|
||||
val latch = CountDownLatch(1)
|
||||
|
||||
try {
|
||||
autoFocus { _, _ -> latch.countDown() }
|
||||
} catch (e: Exception) {
|
||||
logger.log("Failed to perform autofocus using device ${characteristics.cameraId} e: ${e.message}")
|
||||
|
||||
return FocusResult.UnableToFocus
|
||||
}
|
||||
|
||||
try {
|
||||
latch.await(AUTOFOCUS_TIMEOUT_SECONDS, TimeUnit.SECONDS)
|
||||
} catch (e: InterruptedException) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
return FocusResult.Focused
|
||||
}
|
||||
}
|
||||
|
||||
private const val AUTOFOCUS_TIMEOUT_SECONDS = 3L
|
||||
|
||||
private fun Camera.takePhoto(imageRotation: Int): Photo {
|
||||
val latch = CountDownLatch(1)
|
||||
val photoReference = AtomicReference<Photo>()
|
||||
|
||||
takePicture(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
Camera.PictureCallback { data, _ ->
|
||||
photoReference.set(
|
||||
Photo(data, imageRotation)
|
||||
)
|
||||
|
||||
latch.countDown()
|
||||
}
|
||||
)
|
||||
|
||||
latch.await()
|
||||
|
||||
return photoReference.get()
|
||||
}
|
||||
|
||||
private fun computeImageOrientation(
|
||||
degrees: Int,
|
||||
characteristics: Characteristics
|
||||
) = computeImageOrientation(
|
||||
screenRotationDegrees = degrees,
|
||||
cameraRotationDegrees = characteristics.orientation,
|
||||
cameraIsMirrored = characteristics.lensPosition == LensPosition.Front
|
||||
)
|
||||
|
||||
private fun computeDisplayOrientation(
|
||||
degrees: Int,
|
||||
characteristics: Characteristics
|
||||
) = computeDisplayOrientation(
|
||||
screenRotationDegrees = degrees,
|
||||
cameraRotationDegrees = characteristics.orientation,
|
||||
cameraIsMirrored = characteristics.lensPosition == LensPosition.Front
|
||||
)
|
||||
|
||||
private fun Camera.updateParameters(newParameters: CameraParameters) {
|
||||
parameters = parameters.applyNewParameters(newParameters)
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun Camera.setDisplaySurface(
|
||||
preview: Preview
|
||||
): Surface = when (preview) {
|
||||
is Preview.Texture -> preview.surfaceTexture
|
||||
.also {
|
||||
setPreviewTexture(it)
|
||||
}
|
||||
.let {
|
||||
Surface(it)
|
||||
}
|
||||
is Preview.Surface -> preview.surfaceHolder
|
||||
.also {
|
||||
setPreviewDisplay(it)
|
||||
}
|
||||
.surface
|
||||
}
|
||||
|
||||
private fun Camera.getPreviewResolution(imageRotation: Int): Resolution {
|
||||
val previewSize = parameters.previewSize
|
||||
|
||||
val size = PreviewSize(
|
||||
previewSize.width,
|
||||
previewSize.height
|
||||
)
|
||||
|
||||
return size.run {
|
||||
when (imageRotation) {
|
||||
0, 180 -> this
|
||||
else -> flipDimensions()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun PreviewStream.updateProcessorSafely(frameProcessor: ((Frame) -> Unit)?) {
|
||||
clearProcessors()
|
||||
when (frameProcessor) {
|
||||
null -> stop()
|
||||
else -> {
|
||||
addProcessor(frameProcessor)
|
||||
start()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package io.fotoapparat.hardware;
|
||||
|
||||
/**
|
||||
* A generic camera exception.
|
||||
*/
|
||||
public class CameraException extends RuntimeException {
|
||||
|
||||
public CameraException(Exception e) {
|
||||
super(e);
|
||||
}
|
||||
|
||||
public CameraException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CameraException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
package io.fotoapparat.hardware;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import io.fotoapparat.parameter.Flash;
|
||||
import io.fotoapparat.parameter.FocusMode;
|
||||
import io.fotoapparat.parameter.Size;
|
||||
import io.fotoapparat.parameter.range.Range;
|
||||
import io.fotoapparat.parameter.range.Ranges;
|
||||
|
||||
/**
|
||||
* Capabilities of camera hardware.
|
||||
*/
|
||||
public class Capabilities {
|
||||
|
||||
@NonNull
|
||||
private final Set<Size> photoSizes;
|
||||
@NonNull
|
||||
private final Set<Size> previewSizes;
|
||||
@NonNull
|
||||
private final Set<FocusMode> focusModes;
|
||||
@NonNull
|
||||
private final Set<Flash> flashModes;
|
||||
@NonNull
|
||||
private final Set<Range<Integer>> previewFpsRanges;
|
||||
@NonNull
|
||||
private final Range<Integer> sensorSensitivityRange;
|
||||
|
||||
private final boolean zoomSupported;
|
||||
|
||||
public Capabilities(@NonNull Set<Size> photoSizes,
|
||||
@NonNull Set<Size> previewSizes,
|
||||
@NonNull Set<FocusMode> focusModes,
|
||||
@NonNull Set<Flash> flashModes,
|
||||
@NonNull Set<Range<Integer>> previewFpsRanges,
|
||||
@NonNull Range<Integer> sensorSensitivityRange,
|
||||
boolean zoomSupported) {
|
||||
this.photoSizes = photoSizes;
|
||||
this.previewSizes = previewSizes;
|
||||
this.focusModes = focusModes;
|
||||
this.flashModes = flashModes;
|
||||
this.previewFpsRanges = previewFpsRanges;
|
||||
this.sensorSensitivityRange = sensorSensitivityRange;
|
||||
this.zoomSupported = zoomSupported;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Empty {@link Capabilities}.
|
||||
*/
|
||||
public static Capabilities empty() {
|
||||
return new Capabilities(
|
||||
Collections.<Size>emptySet(),
|
||||
Collections.<Size>emptySet(),
|
||||
Collections.<FocusMode>emptySet(),
|
||||
Collections.<Flash>emptySet(),
|
||||
Collections.<Range<Integer>>emptySet(),
|
||||
Ranges.<Integer>emptyRange(),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list of supported picture sizes.
|
||||
*/
|
||||
public Set<Size> supportedPictureSizes() {
|
||||
return photoSizes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list of supported preview sizes;
|
||||
*/
|
||||
public Set<Size> supportedPreviewSizes() {
|
||||
return previewSizes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list of supported focus modes.
|
||||
*/
|
||||
public Set<FocusMode> supportedFocusModes() {
|
||||
return focusModes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list of supported flash firing modes.
|
||||
*/
|
||||
public Set<Flash> supportedFlashModes() {
|
||||
return flashModes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list of supported preview fps ranges.
|
||||
*/
|
||||
public Set<Range<Integer>> supportedPreviewFpsRanges() {
|
||||
return previewFpsRanges;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return supported range of the sensor's sensitivity.
|
||||
*/
|
||||
public Range<Integer> supportedSensorSensitivityRange() {
|
||||
return sensorSensitivityRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
if (!(o instanceof Capabilities)) return false;
|
||||
|
||||
Capabilities that = (Capabilities) o;
|
||||
|
||||
return zoomSupported == that.zoomSupported
|
||||
&& photoSizes.equals(that.photoSizes)
|
||||
&& previewSizes.equals(that.previewSizes)
|
||||
&& focusModes.equals(that.focusModes)
|
||||
&& flashModes.equals(that.flashModes)
|
||||
&& previewFpsRanges.equals(that.previewFpsRanges)
|
||||
&& sensorSensitivityRange.equals(that.sensorSensitivityRange);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = photoSizes.hashCode();
|
||||
result = 31 * result + previewSizes.hashCode();
|
||||
result = 31 * result + focusModes.hashCode();
|
||||
result = 31 * result + flashModes.hashCode();
|
||||
result = 31 * result + previewFpsRanges.hashCode();
|
||||
result = 31 * result + sensorSensitivityRange.hashCode();
|
||||
result = 31 * result + (isZoomSupported() ? 1 : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Capabilities{" +
|
||||
"photoSizes=" + photoSizes +
|
||||
", previewSizes=" + previewSizes +
|
||||
", focusModes=" + focusModes +
|
||||
", flashModes=" + flashModes +
|
||||
", previewFpsRanges=" + previewFpsRanges +
|
||||
", supportedSensorSensitivityRange=" + sensorSensitivityRange +
|
||||
", zoomSupported=" + zoomSupported +
|
||||
'}';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package io.fotoapparat.hardware
|
||||
|
||||
import android.hardware.Camera
|
||||
import io.fotoapparat.characteristic.LensPosition
|
||||
import io.fotoapparat.characteristic.getCharacteristics
|
||||
import io.fotoapparat.configuration.CameraConfiguration
|
||||
import io.fotoapparat.configuration.Configuration
|
||||
import io.fotoapparat.exception.camera.UnsupportedLensException
|
||||
import io.fotoapparat.hardware.display.Display
|
||||
import io.fotoapparat.log.Logger
|
||||
import io.fotoapparat.parameter.ScaleType
|
||||
import io.fotoapparat.parameter.camera.CameraParameters
|
||||
import io.fotoapparat.parameter.camera.provide.getCameraParameters
|
||||
import io.fotoapparat.preview.Frame
|
||||
import io.fotoapparat.view.CameraRenderer
|
||||
import kotlinx.coroutines.experimental.CompletableDeferred
|
||||
|
||||
|
||||
/**
|
||||
* Phone.
|
||||
*/
|
||||
internal open class Device(
|
||||
private val logger: Logger,
|
||||
private val display: Display,
|
||||
open val scaleType: ScaleType,
|
||||
open val cameraRenderer: CameraRenderer,
|
||||
numberOfCameras: Int = Camera.getNumberOfCameras(),
|
||||
initialConfiguration: CameraConfiguration,
|
||||
initialLensPositionSelector: Collection<LensPosition>.() -> LensPosition?
|
||||
) {
|
||||
|
||||
private val cameras = (0 until numberOfCameras).map { cameraId ->
|
||||
CameraDevice(
|
||||
logger = logger,
|
||||
characteristics = getCharacteristics(cameraId)
|
||||
)
|
||||
}
|
||||
|
||||
private var lensPositionSelector: Collection<LensPosition>.() -> LensPosition? = initialLensPositionSelector
|
||||
private var selectedCameraDevice = CompletableDeferred<CameraDevice>()
|
||||
private var savedConfiguration = CameraConfiguration.default()
|
||||
|
||||
init {
|
||||
updateLensPositionSelector(initialLensPositionSelector)
|
||||
savedConfiguration = initialConfiguration
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a camera.
|
||||
*/
|
||||
open fun canSelectCamera(lensPositionSelector: (Collection<LensPosition>) -> LensPosition?): Boolean {
|
||||
val selectedCameraDevice = selectCamera(
|
||||
availableCameras = cameras,
|
||||
lensPositionSelector = lensPositionSelector
|
||||
)
|
||||
return selectedCameraDevice != null
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a camera. Will do nothing if camera cannot be selected.
|
||||
*/
|
||||
open fun selectCamera() {
|
||||
logger.recordMethod()
|
||||
|
||||
selectCamera(
|
||||
availableCameras = cameras,
|
||||
lensPositionSelector = lensPositionSelector
|
||||
)
|
||||
?.let {
|
||||
selectedCameraDevice.complete(it)
|
||||
}
|
||||
?: selectedCameraDevice.completeExceptionally(UnsupportedLensException())
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the selected camera.
|
||||
*/
|
||||
open fun clearSelectedCamera() {
|
||||
selectedCameraDevice = CompletableDeferred()
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits and returns the selected camera.
|
||||
*/
|
||||
open suspend fun awaitSelectedCamera(): CameraDevice {
|
||||
return selectedCameraDevice.await()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the selected camera.
|
||||
*
|
||||
* @throws IllegalStateException If no camera has been yet selected.
|
||||
* @throws UnsupportedLensException If no camera could get selected.
|
||||
*/
|
||||
open fun getSelectedCamera(): CameraDevice {
|
||||
return try {
|
||||
selectedCameraDevice.getCompleted()
|
||||
} catch (e: IllegalStateException) {
|
||||
throw IllegalStateException("Camera has not started!")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return `true` if a camera has been selected.
|
||||
*/
|
||||
open fun hasSelectedCamera() = selectedCameraDevice.isCompleted
|
||||
|
||||
/**
|
||||
* @return rotation of the screen in degrees.
|
||||
*/
|
||||
open fun getScreenRotation(): Int {
|
||||
return display.getRotation()
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the desired from the user camera lens position.
|
||||
*/
|
||||
open fun updateLensPositionSelector(newLensPosition: Collection<LensPosition>.() -> LensPosition?) {
|
||||
logger.recordMethod()
|
||||
|
||||
lensPositionSelector = newLensPosition
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the desired from the user selectors.
|
||||
*/
|
||||
open fun updateConfiguration(newConfiguration: Configuration) {
|
||||
logger.recordMethod()
|
||||
|
||||
savedConfiguration = updateConfiguration(
|
||||
savedConfiguration = savedConfiguration,
|
||||
newConfiguration = newConfiguration
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The desired from the user selectors.
|
||||
*/
|
||||
open fun getConfiguration(): CameraConfiguration {
|
||||
return savedConfiguration
|
||||
}
|
||||
|
||||
open fun getCameraParameters(cameraDevice: CameraDevice): CameraParameters {
|
||||
return getCameraParameters(
|
||||
cameraConfiguration = savedConfiguration,
|
||||
capabilities = cameraDevice.getCapabilities()
|
||||
)
|
||||
}
|
||||
|
||||
open fun getFrameProcessor(): (Frame) -> Unit {
|
||||
return savedConfiguration.frameProcessor
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The desired from the user camera lens position.
|
||||
*/
|
||||
open fun getLensPositionSelector(): Collection<LensPosition>.() -> LensPosition? {
|
||||
return lensPositionSelector
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the device's configuration.
|
||||
*/
|
||||
internal fun updateConfiguration(
|
||||
savedConfiguration: CameraConfiguration,
|
||||
newConfiguration: Configuration
|
||||
) = CameraConfiguration(
|
||||
flashMode = newConfiguration.flashMode ?: savedConfiguration.flashMode,
|
||||
focusMode = newConfiguration.focusMode ?: savedConfiguration.focusMode,
|
||||
frameProcessor = newConfiguration.frameProcessor ?: savedConfiguration.frameProcessor,
|
||||
previewFpsRange = newConfiguration.previewFpsRange ?: savedConfiguration.previewFpsRange,
|
||||
sensorSensitivity = newConfiguration.sensorSensitivity ?: savedConfiguration.sensorSensitivity,
|
||||
pictureResolution = newConfiguration.pictureResolution ?: savedConfiguration.pictureResolution,
|
||||
previewResolution = newConfiguration.previewResolution ?: savedConfiguration.previewResolution
|
||||
)
|
||||
|
||||
/**
|
||||
* Selects a camera from the set of available ones.
|
||||
*/
|
||||
internal fun selectCamera(
|
||||
availableCameras: List<CameraDevice>,
|
||||
lensPositionSelector: Collection<LensPosition>.() -> LensPosition?
|
||||
): CameraDevice? {
|
||||
|
||||
val lensPositions = availableCameras.map { it.characteristics.lensPosition }.toSet()
|
||||
val desiredPosition = lensPositionSelector(lensPositions)
|
||||
|
||||
return availableCameras.find { it.characteristics.lensPosition == desiredPosition }
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package io.fotoapparat.hardware
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
|
||||
private var taskExecutor = Executors.newSingleThreadExecutor()
|
||||
get() = field.takeUnless { it.isShutdown } ?: Executors.newSingleThreadExecutor().also { field = it }
|
||||
|
||||
private val cameraExecutor = Executors.newSingleThreadExecutor()
|
||||
private val loggingExecutor = Executors.newSingleThreadExecutor()
|
||||
private val mainThreadHandler = Handler(Looper.getMainLooper())
|
||||
/**
|
||||
* [java.util.concurrent.Executor] operating the [io.fotoapparat.result.PendingResult].
|
||||
*/
|
||||
internal val pendingResultExecutor = Executors.newSingleThreadExecutor()
|
||||
/**
|
||||
* [java.util.concurrent.Executor] operating the [io.fotoapparat.preview.PreviewStream].
|
||||
*/
|
||||
internal val frameProcessingExecutor = Executors.newSingleThreadExecutor()
|
||||
|
||||
/**
|
||||
* Shuts down all pending camera tasks.
|
||||
*/
|
||||
internal fun shutdownPendingTasks() {
|
||||
taskExecutor.shutdownNow()
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a camera task.
|
||||
*/
|
||||
internal fun executeTask(function: Runnable) = taskExecutor.execute(function)
|
||||
|
||||
/**
|
||||
* Executes a camera operation.
|
||||
*/
|
||||
internal fun execute(function: () -> Unit) = cameraExecutor.execute(function)
|
||||
|
||||
/**
|
||||
* Executes an operation in the main thread.
|
||||
*/
|
||||
internal fun executeLoggingThread(function: () -> Unit) = loggingExecutor.execute { function() }
|
||||
|
||||
/**
|
||||
* Executes an operation in the main thread.
|
||||
*/
|
||||
internal fun executeMainThread(function: () -> Unit) = mainThreadHandler.post { function() }
|
||||
@@ -0,0 +1,27 @@
|
||||
package io.fotoapparat.hardware.display
|
||||
|
||||
import android.content.Context
|
||||
import android.view.Surface
|
||||
import android.view.WindowManager
|
||||
|
||||
/**
|
||||
* A phone's display.
|
||||
*/
|
||||
open internal class Display(context: Context) {
|
||||
|
||||
private val display = context.getDisplay()
|
||||
|
||||
/**
|
||||
* Returns the rotation of the screen from its "natural" orientation in degrees.
|
||||
*/
|
||||
open fun getRotation(): Int = when (display.rotation) {
|
||||
Surface.ROTATION_90 -> 90
|
||||
Surface.ROTATION_180 -> 180
|
||||
Surface.ROTATION_270 -> 270
|
||||
Surface.ROTATION_0 -> 0
|
||||
else -> 0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun Context.getDisplay() = (getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay
|
||||
@@ -1,16 +0,0 @@
|
||||
package io.fotoapparat.hardware.operators;
|
||||
|
||||
import io.fotoapparat.lens.FocusResult;
|
||||
|
||||
/**
|
||||
* Performs auto focus.
|
||||
*/
|
||||
public interface AutoFocusOperator {
|
||||
|
||||
/**
|
||||
* Performs auto focus. This is a blocking operation which returns the result of the operation
|
||||
* when auto focus completes.
|
||||
*/
|
||||
FocusResult autoFocus();
|
||||
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package io.fotoapparat.hardware.operators;
|
||||
|
||||
import io.fotoapparat.hardware.Capabilities;
|
||||
|
||||
/**
|
||||
* An interface which indicates that the class can
|
||||
* provide the camera capabilities.
|
||||
*/
|
||||
public interface CapabilitiesOperator {
|
||||
|
||||
/**
|
||||
* Returns the {@link Capabilities} of the opened camera.
|
||||
*
|
||||
* @return The {@link Capabilities} of the camera.
|
||||
*/
|
||||
Capabilities getCapabilities();
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package io.fotoapparat.hardware.operators;
|
||||
|
||||
import io.fotoapparat.photo.Photo;
|
||||
|
||||
/**
|
||||
* An interface which indicates that the class can
|
||||
* capture still pictures.
|
||||
*/
|
||||
public interface CaptureOperator {
|
||||
|
||||
/**
|
||||
* Invokes a still picture capture action.
|
||||
*
|
||||
* @return The captured photo.
|
||||
*/
|
||||
Photo takePicture();
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package io.fotoapparat.hardware.operators;
|
||||
|
||||
import io.fotoapparat.parameter.LensPosition;
|
||||
|
||||
/**
|
||||
* An interface which indicates that the class can
|
||||
* open and close connections to a camera.
|
||||
*/
|
||||
public interface ConnectionOperator {
|
||||
|
||||
/**
|
||||
* Opens a connection to a camera.
|
||||
*
|
||||
* @param lensPosition The camera position relatively to the screen of the device,
|
||||
* which will determine which camera to open.
|
||||
*/
|
||||
void open(LensPosition lensPosition);
|
||||
|
||||
/**
|
||||
* Closes the connection to a camera.
|
||||
*/
|
||||
void close();
|
||||
}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
package io.fotoapparat.hardware.operators;
|
||||
|
||||
/**
|
||||
* Measures the exposure.
|
||||
*/
|
||||
public interface ExposureMeasurementOperator {
|
||||
|
||||
/**
|
||||
* Measures the exposure. This is a blocking operation which returns when measurement completes.
|
||||
*/
|
||||
void measureExposure();
|
||||
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package io.fotoapparat.hardware.operators;
|
||||
|
||||
/**
|
||||
* An interface which indicates that the class can
|
||||
* handle the orientation updates.
|
||||
*/
|
||||
public interface OrientationOperator {
|
||||
|
||||
/**
|
||||
* Sets the current orientation of the display.
|
||||
*/
|
||||
void setDisplayOrientation(int degrees);
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package io.fotoapparat.hardware.operators;
|
||||
|
||||
import io.fotoapparat.parameter.Parameters;
|
||||
|
||||
/**
|
||||
* An interface which indicates that the class can
|
||||
* support parameter updates.
|
||||
*/
|
||||
public interface ParametersOperator {
|
||||
|
||||
/**
|
||||
* Updates the desired parameters for the preview and the photo capture actions.
|
||||
*/
|
||||
void updateParameters(Parameters parameters);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package io.fotoapparat.hardware.operators;
|
||||
|
||||
/**
|
||||
* An interface which indicates that the class can
|
||||
* start and stop the capture preview.
|
||||
*/
|
||||
public interface PreviewOperator {
|
||||
|
||||
/**
|
||||
* Starts the preview to the surface.
|
||||
* <p>
|
||||
* {@link #stopPreview()} should be called
|
||||
* to stop the operation.
|
||||
*/
|
||||
void startPreview();
|
||||
|
||||
/**
|
||||
* Stops the preview from the surface.
|
||||
*/
|
||||
void stopPreview();
|
||||
}
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
package io.fotoapparat.hardware.operators;
|
||||
|
||||
import io.fotoapparat.preview.PreviewStream;
|
||||
|
||||
/**
|
||||
* Provides {@link PreviewStream} controlled by camera.
|
||||
*/
|
||||
public interface PreviewStreamOperator {
|
||||
|
||||
/**
|
||||
* @return {@link PreviewStream} associated with camera.
|
||||
*/
|
||||
PreviewStream getPreviewStream();
|
||||
|
||||
}
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
package io.fotoapparat.hardware.operators;
|
||||
|
||||
import io.fotoapparat.parameter.RendererParameters;
|
||||
|
||||
/**
|
||||
* Builds {@link RendererParameters}.
|
||||
*/
|
||||
public interface RendererParametersOperator {
|
||||
|
||||
/**
|
||||
* @return {@link RendererParameters}.
|
||||
*/
|
||||
RendererParameters getRendererParameters();
|
||||
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package io.fotoapparat.hardware.operators;
|
||||
|
||||
/**
|
||||
* An interface which indicates that the class can
|
||||
* set a preview surface.
|
||||
*/
|
||||
public interface SurfaceOperator {
|
||||
|
||||
/**
|
||||
* Sets the desired surface on which the camera preview will be displayed.
|
||||
*/
|
||||
void setDisplaySurface(Object displaySurface);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
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);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package io.fotoapparat.hardware.orientation
|
||||
|
||||
|
||||
/**
|
||||
* @param screenRotationDegrees rotation of the display (as provided by system) in degrees.
|
||||
* @param cameraRotationDegrees rotation of the camera sensor relatively to device natural
|
||||
* orientation.
|
||||
* @param cameraIsMirrored `true` if camera is mirrored (typically that is the case for
|
||||
* front cameras). `false` if it is not mirrored.
|
||||
*
|
||||
* @return clockwise rotation of the image relatively to current device orientation.
|
||||
*/
|
||||
internal fun computeImageOrientation(
|
||||
screenRotationDegrees: Int,
|
||||
cameraRotationDegrees: Int,
|
||||
cameraIsMirrored: Boolean
|
||||
): Int {
|
||||
val rotation = if (cameraIsMirrored) {
|
||||
-(screenRotationDegrees + cameraRotationDegrees)
|
||||
} else {
|
||||
screenRotationDegrees - cameraRotationDegrees
|
||||
}
|
||||
|
||||
return (rotation + 720) % 360
|
||||
}
|
||||
|
||||
/**
|
||||
* @param screenRotationDegrees rotation of the display (as provided by system) in degrees.
|
||||
* @param cameraRotationDegrees rotation of the camera sensor relatively to device natural
|
||||
* orientation.
|
||||
* @param cameraIsMirrored `true` if camera is mirrored (typically that is the case for
|
||||
* front cameras). `false` if it is not mirrored.
|
||||
*
|
||||
* @return display orientation in which user will see the output camera in a correct rotation.
|
||||
*/
|
||||
internal fun computeDisplayOrientation(
|
||||
screenRotationDegrees: Int,
|
||||
cameraRotationDegrees: Int,
|
||||
cameraIsMirrored: Boolean
|
||||
): Int {
|
||||
var degrees = toClosestRightAngle(screenRotationDegrees)
|
||||
|
||||
return if (cameraIsMirrored) {
|
||||
degrees = (cameraRotationDegrees + degrees) % 360
|
||||
(360 - degrees) % 360
|
||||
} else {
|
||||
(cameraRotationDegrees - degrees + 360) % 360
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return closest right angle to given value. That is: 0, 90, 180, 270.
|
||||
*/
|
||||
internal fun toClosestRightAngle(degrees: Int): Int {
|
||||
val roundUp = degrees % 90 > 45
|
||||
|
||||
val roundAppModifier = if (roundUp) 1 else 0
|
||||
|
||||
return (degrees / 90 + roundAppModifier) * 90 % 360
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package io.fotoapparat.hardware.orientation;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Monitors orientation of the device.
|
||||
*/
|
||||
public class OrientationSensor implements RotationListener.Listener {
|
||||
|
||||
private final RotationListener rotationListener;
|
||||
private final ScreenOrientationProvider screenOrientationProvider;
|
||||
|
||||
private int lastKnownRotation;
|
||||
private Listener listener;
|
||||
|
||||
public OrientationSensor(@NonNull final RotationListener rotationListener,
|
||||
@NonNull final ScreenOrientationProvider screenOrientationProvider) {
|
||||
this.rotationListener = rotationListener;
|
||||
this.screenOrientationProvider = screenOrientationProvider;
|
||||
|
||||
rotationListener.setRotationListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts monitoring device's orientation.
|
||||
*/
|
||||
public void start(Listener listener) {
|
||||
this.listener = listener;
|
||||
rotationListener.enable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops monitoring device's orientation.
|
||||
*/
|
||||
public void stop() {
|
||||
rotationListener.disable();
|
||||
listener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRotationChanged() {
|
||||
if (listener != null) {
|
||||
int rotation = screenOrientationProvider.getScreenRotation();
|
||||
if (rotation != lastKnownRotation) {
|
||||
listener.onOrientationChanged(rotation);
|
||||
lastKnownRotation = rotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notified when orientation of the device is updated.
|
||||
*/
|
||||
public interface Listener {
|
||||
|
||||
/**
|
||||
* Called when orientation of the device is updated.
|
||||
*/
|
||||
void onOrientationChanged(int degrees);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package io.fotoapparat.hardware.orientation
|
||||
|
||||
import android.content.Context
|
||||
import io.fotoapparat.hardware.Device
|
||||
|
||||
|
||||
/**
|
||||
* Monitors orientation of the device.
|
||||
*/
|
||||
open internal class OrientationSensor(
|
||||
private val rotationListener: RotationListener,
|
||||
private val device: Device
|
||||
) {
|
||||
|
||||
constructor(context: Context,
|
||||
device: Device
|
||||
) : this(
|
||||
RotationListener(context),
|
||||
device
|
||||
)
|
||||
|
||||
private val onOrientationChanged = {
|
||||
device.getScreenRotation().let {
|
||||
if (it != lastKnownRotation) {
|
||||
listener(it)
|
||||
lastKnownRotation = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
rotationListener.orientationChanged = onOrientationChanged
|
||||
}
|
||||
|
||||
private lateinit var listener: (Int) -> Unit
|
||||
private var lastKnownRotation: Int = 0
|
||||
|
||||
/**
|
||||
* Starts monitoring device's orientation.
|
||||
*/
|
||||
open fun start(listener: (Int) -> Unit) {
|
||||
this.listener = listener
|
||||
rotationListener.enable()
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops monitoring device's orientation.
|
||||
*/
|
||||
open fun stop() {
|
||||
rotationListener.disable()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package io.fotoapparat.hardware.orientation;
|
||||
|
||||
/**
|
||||
* Utilities for working with device orientation.
|
||||
*/
|
||||
public class OrientationUtils {
|
||||
|
||||
/**
|
||||
* @return closest right angle to given value. That is: 0, 90, 180, 270.
|
||||
*/
|
||||
public static int toClosestRightAngle(int degrees) {
|
||||
boolean roundUp = degrees % 90 > 45;
|
||||
|
||||
int roundAppModifier = roundUp ? 1 : 0;
|
||||
|
||||
return (((degrees / 90) + roundAppModifier) * 90) % 360;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param screenRotationDegrees rotation of the display (as provided by system) in degrees.
|
||||
* @param cameraRotationDegrees rotation of the camera sensor relatively to device natural
|
||||
* orientation.
|
||||
* @param cameraIsMirrored {@code true} if camera is mirrored (typically that is the case
|
||||
* for front cameras). {@code false} if it is not mirrored.
|
||||
* @return display orientation in which user will see the output camera in a correct rotation.
|
||||
*/
|
||||
public static int computeDisplayOrientation(int screenRotationDegrees,
|
||||
int cameraRotationDegrees,
|
||||
boolean cameraIsMirrored) {
|
||||
int degrees = OrientationUtils.toClosestRightAngle(screenRotationDegrees);
|
||||
|
||||
if (cameraIsMirrored) {
|
||||
degrees = (cameraRotationDegrees + degrees) % 360;
|
||||
degrees = (360 - degrees) % 360;
|
||||
} else {
|
||||
degrees = (cameraRotationDegrees - degrees + 360) % 360;
|
||||
}
|
||||
|
||||
return degrees;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param screenRotationDegrees rotation of the display (as provided by system) in degrees.
|
||||
* @param cameraRotationDegrees rotation of the camera sensor relatively to device natural
|
||||
* orientation.
|
||||
* @param cameraIsMirrored {@code true} if camera is mirrored (typically that is the case
|
||||
* for front cameras). {@code false} if it is not mirrored.
|
||||
* @return clockwise rotation of the image relatively to current device orientation.
|
||||
*/
|
||||
public static int computeImageOrientation(int screenRotationDegrees,
|
||||
int cameraRotationDegrees,
|
||||
boolean cameraIsMirrored) {
|
||||
int rotation;
|
||||
|
||||
if (cameraIsMirrored) {
|
||||
rotation = -(screenRotationDegrees + cameraRotationDegrees);
|
||||
} else {
|
||||
rotation = screenRotationDegrees - cameraRotationDegrees;
|
||||
}
|
||||
|
||||
return (rotation + 360 + 360) % 360;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package io.fotoapparat.hardware.orientation;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.OrientationEventListener;
|
||||
|
||||
/**
|
||||
* Wrapper around {@link OrientationEventListener} to notify when the device's rotation has changed.
|
||||
*/
|
||||
public class RotationListener extends OrientationEventListener {
|
||||
|
||||
private Listener listener;
|
||||
|
||||
public RotationListener(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOrientationChanged(int orientation) {
|
||||
if (listener != null && canDetectOrientation()) {
|
||||
listener.onRotationChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a listener to this class to notify future rotation events.
|
||||
*
|
||||
* @param listener The new listener
|
||||
*/
|
||||
void setRotationListener(@NonNull Listener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notified when the rotation of the device is updated.
|
||||
*/
|
||||
interface Listener {
|
||||
|
||||
/**
|
||||
* Called when the rotation of the device has changed.
|
||||
*/
|
||||
void onRotationChanged();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package io.fotoapparat.hardware.orientation
|
||||
|
||||
import android.content.Context
|
||||
import android.view.OrientationEventListener
|
||||
|
||||
|
||||
/**
|
||||
* Wrapper around [OrientationEventListener] to notify when the device's rotation has changed.
|
||||
*/
|
||||
open internal class RotationListener(
|
||||
context: Context
|
||||
) : OrientationEventListener(context) {
|
||||
|
||||
lateinit var orientationChanged: () -> Unit
|
||||
|
||||
override fun onOrientationChanged(orientation: Int) {
|
||||
if (canDetectOrientation()) {
|
||||
orientationChanged()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
package io.fotoapparat.hardware.orientation;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.Display;
|
||||
import android.view.Surface;
|
||||
import android.view.WindowManager;
|
||||
|
||||
/**
|
||||
* Provides orientation of the screen.
|
||||
*/
|
||||
public class ScreenOrientationProvider {
|
||||
|
||||
private final Display display;
|
||||
|
||||
public ScreenOrientationProvider(@NonNull Context context) {
|
||||
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
display = windowManager.getDefaultDisplay();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return rotation of the screen in degrees.
|
||||
*/
|
||||
public int getScreenRotation() {
|
||||
int rotation = display.getRotation();
|
||||
|
||||
switch (rotation) {
|
||||
case Surface.ROTATION_90:
|
||||
return 90;
|
||||
case Surface.ROTATION_180:
|
||||
return 180;
|
||||
case Surface.ROTATION_270:
|
||||
return 270;
|
||||
case Surface.ROTATION_0:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
package io.fotoapparat.hardware.provider;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.fotoapparat.parameter.LensPosition;
|
||||
|
||||
/**
|
||||
* Provides list of {@link LensPosition} which are available on the device.
|
||||
*/
|
||||
public interface AvailableLensPositionsProvider {
|
||||
|
||||
/**
|
||||
* @return list of {@link LensPosition} which are available on the device.
|
||||
*/
|
||||
List<LensPosition> getAvailableLensPositions();
|
||||
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package io.fotoapparat.hardware.provider;
|
||||
|
||||
import io.fotoapparat.hardware.CameraDevice;
|
||||
import io.fotoapparat.log.Logger;
|
||||
|
||||
/**
|
||||
* Abstraction for providing camera.
|
||||
*/
|
||||
public interface CameraProvider {
|
||||
|
||||
/**
|
||||
* @return a {@link CameraDevice}.
|
||||
*/
|
||||
CameraDevice get(Logger logger);
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package io.fotoapparat.hardware.provider;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
/**
|
||||
* Static factory for {@link CameraProvider}
|
||||
*/
|
||||
public class CameraProviders {
|
||||
|
||||
private CameraProviders() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return provider which uses Camera v2 on devices newer than Lollipop and falls back to Camera
|
||||
* v1 on older devices.
|
||||
*/
|
||||
public static CameraProvider defaultProvider(Context context) {
|
||||
return new DefaultProvider(
|
||||
v1(),
|
||||
v2(context)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return provider for Camera v1.
|
||||
*/
|
||||
public static CameraProvider v1() {
|
||||
return new V1Provider();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return provider for Camera v2.
|
||||
*/
|
||||
public static CameraProvider v2(Context context) {
|
||||
return new V2Provider(context);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package io.fotoapparat.hardware.provider;
|
||||
|
||||
import io.fotoapparat.hardware.CameraDevice;
|
||||
import io.fotoapparat.log.Logger;
|
||||
import io.fotoapparat.util.SDKInfo;
|
||||
|
||||
/**
|
||||
* Selects correct version of providers for a device.
|
||||
*/
|
||||
public class DefaultProvider implements CameraProvider {
|
||||
|
||||
private final CameraProvider v1Provider;
|
||||
private final CameraProvider v2Provider;
|
||||
private final SDKInfo sdkInfo;
|
||||
|
||||
public DefaultProvider(CameraProvider v1Provider,
|
||||
CameraProvider v2Provider) {
|
||||
this(v1Provider, v2Provider, SDKInfo.getInstance());
|
||||
}
|
||||
|
||||
DefaultProvider(CameraProvider v1Provider,
|
||||
CameraProvider v2Provider,
|
||||
SDKInfo sdkInfo) {
|
||||
this.v1Provider = v1Provider;
|
||||
this.v2Provider = v2Provider;
|
||||
this.sdkInfo = sdkInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraDevice get(Logger logger) {
|
||||
return sdkInfo.isBellowLollipop()
|
||||
? v1Provider.get(logger)
|
||||
: v2Provider.get(logger);
|
||||
}
|
||||
}
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
package io.fotoapparat.hardware.provider;
|
||||
|
||||
import android.hardware.Camera;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import io.fotoapparat.parameter.LensPosition;
|
||||
|
||||
/**
|
||||
* Provides available {@link LensPosition} using Camera v1 API.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class V1AvailableLensPositionProvider implements AvailableLensPositionsProvider {
|
||||
|
||||
@Override
|
||||
public List<LensPosition> getAvailableLensPositions() {
|
||||
HashSet<LensPosition> positions = new HashSet<>();
|
||||
|
||||
final int numberOfCameras = Camera.getNumberOfCameras();
|
||||
|
||||
for (int i = 0; i < numberOfCameras; i++) {
|
||||
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
|
||||
Camera.getCameraInfo(i, cameraInfo);
|
||||
|
||||
positions.add(
|
||||
cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT
|
||||
? LensPosition.FRONT
|
||||
: LensPosition.BACK
|
||||
);
|
||||
}
|
||||
|
||||
return new ArrayList<>(positions);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package io.fotoapparat.hardware.provider;
|
||||
|
||||
import io.fotoapparat.hardware.CameraDevice;
|
||||
import io.fotoapparat.hardware.v1.Camera1;
|
||||
import io.fotoapparat.log.Logger;
|
||||
|
||||
/**
|
||||
* Always provides {@link Camera1}.
|
||||
*/
|
||||
public class V1Provider implements CameraProvider {
|
||||
|
||||
@Override
|
||||
public CameraDevice get(Logger logger) {
|
||||
return new Camera1(logger);
|
||||
}
|
||||
}
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
package io.fotoapparat.hardware.provider;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.camera2.CameraAccessException;
|
||||
import android.hardware.camera2.CameraCharacteristics;
|
||||
import android.hardware.camera2.CameraManager;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.RequiresApi;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import io.fotoapparat.hardware.CameraException;
|
||||
import io.fotoapparat.parameter.LensPosition;
|
||||
|
||||
import static io.fotoapparat.hardware.v2.parameters.converters.LensPositionConverter.toLensPosition;
|
||||
|
||||
/**
|
||||
* Provides available {@link LensPosition} using Camera v2 API.
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
public class V2AvailableLensPositionProvider implements AvailableLensPositionsProvider {
|
||||
|
||||
private final CameraManager manager;
|
||||
|
||||
public V2AvailableLensPositionProvider(Context context) {
|
||||
manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LensPosition> getAvailableLensPositions() {
|
||||
Set<LensPosition> positions = new HashSet<>();
|
||||
|
||||
String[] cameraIdList = getCameraIdListUnsafe();
|
||||
|
||||
for (String cameraId : cameraIdList) {
|
||||
Integer lensFacingConstant = getLensPositionUnsafe(cameraId);
|
||||
positions.add(toLensPosition(lensFacingConstant));
|
||||
}
|
||||
|
||||
return new ArrayList<>(positions);
|
||||
}
|
||||
|
||||
private Integer getLensPositionUnsafe(String cameraId) {
|
||||
try {
|
||||
return manager
|
||||
.getCameraCharacteristics(cameraId)
|
||||
.get(CameraCharacteristics.LENS_FACING);
|
||||
} catch (CameraAccessException e) {
|
||||
throw new CameraException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String[] getCameraIdListUnsafe() {
|
||||
try {
|
||||
return manager.getCameraIdList();
|
||||
} catch (CameraAccessException e) {
|
||||
throw new CameraException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
package io.fotoapparat.hardware.provider;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.camera2.CameraManager;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.RequiresApi;
|
||||
|
||||
import io.fotoapparat.hardware.CameraDevice;
|
||||
import io.fotoapparat.hardware.v2.Camera2;
|
||||
import io.fotoapparat.hardware.v2.CameraThread;
|
||||
import io.fotoapparat.hardware.v2.capabilities.CapabilitiesFactory;
|
||||
import io.fotoapparat.hardware.v2.connection.CameraConnection;
|
||||
import io.fotoapparat.hardware.v2.lens.executors.CaptureOperatorImpl;
|
||||
import io.fotoapparat.hardware.v2.lens.executors.ExposureGatheringExecutor;
|
||||
import io.fotoapparat.hardware.v2.lens.executors.FocusExecutor;
|
||||
import io.fotoapparat.hardware.v2.lens.operations.LensOperationsFactory;
|
||||
import io.fotoapparat.hardware.v2.orientation.OrientationManager;
|
||||
import io.fotoapparat.hardware.v2.parameters.CaptureRequestFactory;
|
||||
import io.fotoapparat.hardware.v2.parameters.ParametersProvider;
|
||||
import io.fotoapparat.hardware.v2.parameters.RendererParametersProvider;
|
||||
import io.fotoapparat.hardware.v2.readers.ContinuousSurfaceReader;
|
||||
import io.fotoapparat.hardware.v2.readers.StillSurfaceReader;
|
||||
import io.fotoapparat.hardware.v2.selection.CameraSelector;
|
||||
import io.fotoapparat.hardware.v2.session.SessionManager;
|
||||
import io.fotoapparat.hardware.v2.session.SessionProvider;
|
||||
import io.fotoapparat.hardware.v2.stream.PreviewStream2;
|
||||
import io.fotoapparat.hardware.v2.surface.TextureManager;
|
||||
import io.fotoapparat.log.Logger;
|
||||
|
||||
/**
|
||||
* Always provides {@link Camera2}.
|
||||
*/
|
||||
public class V2Provider implements CameraProvider {
|
||||
|
||||
private static final CameraThread CAMERA_THREAD = new CameraThread();
|
||||
private final Context context;
|
||||
|
||||
public V2Provider(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
@Override
|
||||
public CameraDevice get(Logger logger) {
|
||||
|
||||
CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
|
||||
AvailableLensPositionsProvider availableLensPositionsProvider = new V2AvailableLensPositionProvider(
|
||||
context
|
||||
);
|
||||
|
||||
CameraSelector cameraSelector = new CameraSelector(manager);
|
||||
|
||||
CameraConnection cameraConnection = new CameraConnection(
|
||||
cameraSelector,
|
||||
manager,
|
||||
CAMERA_THREAD
|
||||
);
|
||||
|
||||
ParametersProvider parametersProvider = new ParametersProvider();
|
||||
|
||||
OrientationManager orientationManager = new OrientationManager(
|
||||
cameraConnection
|
||||
);
|
||||
|
||||
StillSurfaceReader stillSurfaceReader = new StillSurfaceReader(
|
||||
parametersProvider,
|
||||
CAMERA_THREAD
|
||||
);
|
||||
ContinuousSurfaceReader continuousSurfaceReader = new ContinuousSurfaceReader(
|
||||
parametersProvider,
|
||||
CAMERA_THREAD
|
||||
);
|
||||
TextureManager textureManager = new TextureManager(
|
||||
orientationManager,
|
||||
parametersProvider
|
||||
);
|
||||
|
||||
CaptureRequestFactory captureRequestFactory = new CaptureRequestFactory(
|
||||
cameraConnection,
|
||||
stillSurfaceReader,
|
||||
textureManager,
|
||||
parametersProvider
|
||||
);
|
||||
|
||||
SessionProvider sessionProvider = new SessionProvider(
|
||||
stillSurfaceReader,
|
||||
cameraConnection,
|
||||
captureRequestFactory,
|
||||
textureManager,
|
||||
CAMERA_THREAD
|
||||
);
|
||||
|
||||
SessionManager sessionManager = new SessionManager(
|
||||
cameraConnection,
|
||||
sessionProvider
|
||||
);
|
||||
|
||||
CapabilitiesFactory capabilitiesOperator = new CapabilitiesFactory(cameraConnection);
|
||||
|
||||
PreviewStream2 previewStream = new PreviewStream2(
|
||||
continuousSurfaceReader,
|
||||
parametersProvider,
|
||||
logger
|
||||
);
|
||||
|
||||
RendererParametersProvider rendererParametersOperator = new RendererParametersProvider(
|
||||
parametersProvider,
|
||||
orientationManager
|
||||
);
|
||||
|
||||
LensOperationsFactory lensOperationsFactory = new LensOperationsFactory(
|
||||
sessionManager,
|
||||
captureRequestFactory,
|
||||
CAMERA_THREAD
|
||||
);
|
||||
|
||||
FocusExecutor focusExecutor = new FocusExecutor(
|
||||
parametersProvider,
|
||||
lensOperationsFactory
|
||||
);
|
||||
ExposureGatheringExecutor exposureGatheringExecutor = new ExposureGatheringExecutor(
|
||||
lensOperationsFactory
|
||||
);
|
||||
CaptureOperatorImpl captureExecutor = new CaptureOperatorImpl(
|
||||
lensOperationsFactory,
|
||||
stillSurfaceReader,
|
||||
orientationManager
|
||||
);
|
||||
|
||||
return new Camera2(
|
||||
logger,
|
||||
cameraConnection,
|
||||
sessionManager,
|
||||
textureManager,
|
||||
orientationManager,
|
||||
parametersProvider,
|
||||
capabilitiesOperator,
|
||||
previewStream,
|
||||
rendererParametersOperator,
|
||||
focusExecutor,
|
||||
exposureGatheringExecutor,
|
||||
captureExecutor,
|
||||
availableLensPositionsProvider
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,436 +0,0 @@
|
||||
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;
|
||||
import io.fotoapparat.parameter.Parameters;
|
||||
import io.fotoapparat.parameter.RendererParameters;
|
||||
import io.fotoapparat.parameter.Size;
|
||||
import io.fotoapparat.photo.Photo;
|
||||
import io.fotoapparat.preview.PreviewStream;
|
||||
|
||||
/**
|
||||
* Camera hardware driver for v1 {@link Camera} API.
|
||||
*/
|
||||
@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 = -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();
|
||||
this.availableLensPositionsProvider = new V1AvailableLensPositionProvider();
|
||||
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();
|
||||
|
||||
try {
|
||||
cameraId = cameraIdForLensPosition(lensPosition);
|
||||
camera = Camera.open(cameraId);
|
||||
previewStream = new PreviewStream1(camera);
|
||||
} catch (RuntimeException e) {
|
||||
throwOnFailedToOpenCamera(lensPosition, e);
|
||||
}
|
||||
|
||||
camera.setErrorCallback(new Camera.ErrorCallback() {
|
||||
@Override
|
||||
public void onError(int error, Camera camera) {
|
||||
if (lastStacktrace != null) {
|
||||
lastStacktrace.printStackTrace();
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
for (int i = 0; i < numberOfCameras; i++) {
|
||||
Camera.CameraInfo info = getCameraInfo(i);
|
||||
|
||||
if (info.facing == facingForLensPosition(lensPosition)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int facingForLensPosition(LensPosition lensPosition) {
|
||||
switch (lensPosition) {
|
||||
case FRONT:
|
||||
return Camera.CameraInfo.CAMERA_FACING_FRONT;
|
||||
case BACK:
|
||||
return Camera.CameraInfo.CAMERA_FACING_BACK;
|
||||
default:
|
||||
throw new IllegalArgumentException("Camera is not supported: " + lensPosition);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
recordMethod();
|
||||
|
||||
cachedCapabilities = null;
|
||||
|
||||
if (isCameraOpened()) {
|
||||
camera.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startPreview() {
|
||||
recordMethod();
|
||||
|
||||
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();
|
||||
|
||||
if (isCameraOpened()) {
|
||||
camera.stopPreview();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplaySurface(Object displaySurface) {
|
||||
recordMethod();
|
||||
|
||||
try {
|
||||
trySetDisplaySurface(displaySurface);
|
||||
} catch (IOException e) {
|
||||
throwOnFailSetDisplaySurface(displaySurface, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplayOrientation(int degrees) {
|
||||
recordMethod();
|
||||
|
||||
if (!isCameraOpened()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Camera.CameraInfo info = getCameraInfo(cameraId);
|
||||
|
||||
imageRotation = computeImageOrientation(degrees, info);
|
||||
|
||||
camera.setDisplayOrientation(
|
||||
computeDisplayOrientation(degrees, info)
|
||||
);
|
||||
previewStream.setFrameOrientation(imageRotation);
|
||||
}
|
||||
|
||||
private int computeDisplayOrientation(int screenRotationDegrees,
|
||||
Camera.CameraInfo info) {
|
||||
return OrientationUtils.computeDisplayOrientation(
|
||||
screenRotationDegrees,
|
||||
info.orientation,
|
||||
info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT
|
||||
);
|
||||
}
|
||||
|
||||
private int computeImageOrientation(int screenRotationDegrees,
|
||||
Camera.CameraInfo info) {
|
||||
return OrientationUtils.computeImageOrientation(
|
||||
screenRotationDegrees,
|
||||
info.orientation,
|
||||
info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateParameters(Parameters parameters) {
|
||||
recordMethod();
|
||||
|
||||
parametersOperator().updateParameters(parameters);
|
||||
|
||||
cachedZoomParameters = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parameters getCurrentParameters() {
|
||||
Camera.Parameters platformParameters = camera.getParameters();
|
||||
return parametersConverter.fromPlatformParameters(
|
||||
new CameraParametersDecorator(platformParameters)
|
||||
);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private SwitchOnFailureParametersOperator parametersOperator() {
|
||||
ParametersOperator unsafeParametersOperator = new UnsafeParametersOperator(
|
||||
camera,
|
||||
parametersConverter
|
||||
);
|
||||
|
||||
ParametersOperator fallbackOperator = new SplitParametersOperator(
|
||||
new SupressExceptionsParametersOperator(
|
||||
unsafeParametersOperator,
|
||||
logger
|
||||
)
|
||||
);
|
||||
|
||||
return new SwitchOnFailureParametersOperator(
|
||||
unsafeParametersOperator,
|
||||
fallbackOperator
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Capabilities getCapabilities() {
|
||||
if (cachedCapabilities != null) {
|
||||
return cachedCapabilities;
|
||||
}
|
||||
|
||||
recordMethod();
|
||||
|
||||
Capabilities capabilities = capabilitiesFactory.fromParameters(
|
||||
new CameraParametersDecorator(camera.getParameters())
|
||||
);
|
||||
|
||||
cachedCapabilities = capabilities;
|
||||
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
private void trySetDisplaySurface(Object displaySurface) throws IOException {
|
||||
if (displaySurface instanceof TextureView) {
|
||||
camera.setPreviewTexture(((TextureView) displaySurface).getSurfaceTexture());
|
||||
} else if (displaySurface instanceof SurfaceView) {
|
||||
camera.setPreviewDisplay(((SurfaceView) displaySurface).getHolder());
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported display surface: " + displaySurface);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Photo takePicture() {
|
||||
recordMethod();
|
||||
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final AtomicReference<Photo> photoReference = new AtomicReference<>();
|
||||
|
||||
camera.takePicture(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new Camera.PictureCallback() {
|
||||
@Override
|
||||
public void onPictureTaken(byte[] data, Camera camera) {
|
||||
photoReference.set(
|
||||
new Photo(data, imageRotation)
|
||||
);
|
||||
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
try {
|
||||
latch.await();
|
||||
} catch (InterruptedException e) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
return photoReference.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreviewStream getPreviewStream() {
|
||||
recordMethod();
|
||||
|
||||
return isPreviewStreamInitialized()
|
||||
? previewStream
|
||||
: PreviewStream.NULL;
|
||||
}
|
||||
|
||||
private boolean isPreviewStreamInitialized() {
|
||||
return previewStream != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RendererParameters getRendererParameters() {
|
||||
recordMethod();
|
||||
|
||||
RendererParameters rendererParameters = new RendererParameters(
|
||||
previewSize(),
|
||||
imageRotation
|
||||
);
|
||||
|
||||
logRendererParameters(rendererParameters);
|
||||
|
||||
return rendererParameters;
|
||||
}
|
||||
|
||||
private void logRendererParameters(RendererParameters rendererParameters) {
|
||||
logger.log("Renderer parameters are: " + rendererParameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FocusResult autoFocus() {
|
||||
recordMethod();
|
||||
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
try {
|
||||
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
|
||||
}
|
||||
|
||||
return FocusResult.successNoMeasurement();
|
||||
}
|
||||
|
||||
private void logFailedAutoFocus(Exception e) {
|
||||
logger.log("Failed to perform autofocus using device " + cameraId + " e: " + e.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void measureExposure() {
|
||||
// Do nothing. Not supported by Camera1.
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LensPosition> getAvailableLensPositions() {
|
||||
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();
|
||||
|
||||
return new Size(
|
||||
previewSize.width,
|
||||
previewSize.height
|
||||
);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Camera.CameraInfo getCameraInfo(int id) {
|
||||
Camera.CameraInfo info = new Camera.CameraInfo();
|
||||
Camera.getCameraInfo(id, info);
|
||||
return info;
|
||||
}
|
||||
|
||||
private boolean isCameraOpened() {
|
||||
return camera != null;
|
||||
}
|
||||
|
||||
private void recordMethod() {
|
||||
lastStacktrace = new Exception();
|
||||
|
||||
logger.log(
|
||||
lastStacktrace.getStackTrace()[1].getMethodName()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,228 +0,0 @@
|
||||
package io.fotoapparat.hardware.v1;
|
||||
|
||||
import android.hardware.Camera;
|
||||
import android.hardware.Camera.Size;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Decorator for {@link Camera.Parameters} with methods
|
||||
* for getting and settings additional camera parameters.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class CameraParametersDecorator {
|
||||
|
||||
/* Raw camera params keys */
|
||||
private static final String[] RAW_ISO_SUPPORTED_VALUES_KEYS = {
|
||||
"iso-values", "iso-mode-values", "iso-speed-values", "nv-picture-iso-values"
|
||||
};
|
||||
private static final String[] RAW_ISO_CURRENT_VALUE_KEYS = {
|
||||
"iso", "iso-speed", "nv-picture-iso"
|
||||
};
|
||||
|
||||
private Camera.Parameters cameraParameters;
|
||||
|
||||
public CameraParametersDecorator(Camera.Parameters cameraParameters) {
|
||||
this.cameraParameters = cameraParameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link Camera.Parameters} view of this decorator.
|
||||
*/
|
||||
public Camera.Parameters asCameraParameters() {
|
||||
return cameraParameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Camera.Parameters#isZoomSupported()
|
||||
*/
|
||||
public boolean isZoomSupported() {
|
||||
return cameraParameters.isZoomSupported();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Camera.Parameters#getSupportedPreviewSizes()
|
||||
*/
|
||||
public List<Size> getSupportedPreviewSizes() {
|
||||
return cameraParameters.getSupportedPreviewSizes();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Camera.Parameters#getSupportedPictureSizes()
|
||||
*/
|
||||
public List<Size> getSupportedPictureSizes() {
|
||||
return cameraParameters.getSupportedPictureSizes();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Camera.Parameters#getSupportedFlashModes()
|
||||
*/
|
||||
public List<String> getSupportedFlashModes() {
|
||||
return cameraParameters.getSupportedFlashModes();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Camera.Parameters#getSupportedFocusModes()
|
||||
*/
|
||||
public List<String> getSupportedFocusModes() {
|
||||
return cameraParameters.getSupportedFocusModes();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Camera.Parameters#getSupportedPreviewFpsRange()
|
||||
*/
|
||||
public List<int[]> getSupportedPreviewFpsRange() {
|
||||
return cameraParameters.getSupportedPreviewFpsRange();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Camera.Parameters#getFocusMode()
|
||||
*/
|
||||
public String getFocusMode() {
|
||||
return cameraParameters.getFocusMode();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Camera.Parameters#setFocusMode(String)
|
||||
*/
|
||||
public void setFocusMode(String focusMode) {
|
||||
cameraParameters.setFocusMode(focusMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Camera.Parameters#getFlashMode()
|
||||
*/
|
||||
public String getFlashMode() {
|
||||
return cameraParameters.getFlashMode();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Camera.Parameters#setFlashMode(String)
|
||||
*/
|
||||
public void setFlashMode(String flash) {
|
||||
cameraParameters.setFlashMode(flash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Camera.Parameters#getPictureSize()
|
||||
*/
|
||||
public Camera.Size getPictureSize() {
|
||||
return cameraParameters.getPictureSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Camera.Parameters#getPreviewSize()
|
||||
*/
|
||||
public Camera.Size getPreviewSize() {
|
||||
return cameraParameters.getPreviewSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Camera.Parameters#setPreviewSize(int, int)
|
||||
*/
|
||||
public void setPreviewSize(int width, int height) {
|
||||
cameraParameters.setPreviewSize(width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Camera.Parameters#setPictureSize(int, int)
|
||||
*/
|
||||
public void setPictureSize(int width, int height) {
|
||||
cameraParameters.setPictureSize(width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Camera.Parameters#setPreviewFpsRange(int, int)
|
||||
*/
|
||||
public void setPreviewFpsRange(int min, int max) {
|
||||
cameraParameters.setPreviewFpsRange(min, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns set of ISO values, that camera supports.
|
||||
*
|
||||
* @return the set of supported ISO values.
|
||||
*/
|
||||
@NonNull
|
||||
public Set<Integer> getSensorSensitivityValues() {
|
||||
String[] rawValues = extractRawCameraValues(RAW_ISO_SUPPORTED_VALUES_KEYS);
|
||||
return convertParamsToInts(rawValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets ISO value for camera.
|
||||
*/
|
||||
public void setSensorSensitivityValue(int isoValue) {
|
||||
String isoKey = findExistingKey(RAW_ISO_CURRENT_VALUE_KEYS);
|
||||
if (isoKey != null) {
|
||||
cameraParameters.set(isoKey, isoValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Camera.Parameters#getJpegQuality()
|
||||
*/
|
||||
public int getJpegQuality(){
|
||||
return cameraParameters.getJpegQuality();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Camera.Parameters#setJpegQuality(int)
|
||||
*/
|
||||
public void setJpegQuality(int quality){
|
||||
cameraParameters.setJpegQuality(quality);
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
private String findExistingKey(@NonNull String[] keys) {
|
||||
for (String key : keys) {
|
||||
if (cameraParameters.get(key) != null) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String[] extractRawCameraValues(@NonNull String[] keys) {
|
||||
for (String key : keys) {
|
||||
String[] rawValues = extractStringValuesFromParams(key);
|
||||
if (rawValues != null) {
|
||||
return rawValues;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String[] extractStringValuesFromParams(@NonNull String key) {
|
||||
String rawValues = cameraParameters.get(key);
|
||||
if (rawValues != null) {
|
||||
return rawValues.split(",");
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Set<Integer> convertParamsToInts(@Nullable String[] values) {
|
||||
Set<Integer> integerValues = new HashSet<>();
|
||||
if (values != null) {
|
||||
for (String value : values) {
|
||||
try {
|
||||
integerValues.add(Integer.valueOf(value.trim()));
|
||||
} catch (NumberFormatException e) {
|
||||
// Found not number option. Skip it.
|
||||
}
|
||||
}
|
||||
}
|
||||
return integerValues;
|
||||
}
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
package io.fotoapparat.hardware.v1;
|
||||
|
||||
import android.hardware.Camera;
|
||||
|
||||
import io.fotoapparat.hardware.v1.capabilities.FlashCapability;
|
||||
import io.fotoapparat.hardware.v1.capabilities.FocusCapability;
|
||||
import io.fotoapparat.parameter.Flash;
|
||||
import io.fotoapparat.parameter.FocusMode;
|
||||
import io.fotoapparat.parameter.Parameters;
|
||||
import io.fotoapparat.parameter.Size;
|
||||
import io.fotoapparat.parameter.range.Range;
|
||||
|
||||
/**
|
||||
* Converts {@link Parameters} to {@link CameraParametersDecorator}.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class ParametersConverter {
|
||||
|
||||
/**
|
||||
* Converts {@link Parameters} to {@link CameraParametersDecorator}.
|
||||
*
|
||||
* @param parameters parameters which should be converted.
|
||||
* @param output output value. It is required because of C-style API in Camera v1.
|
||||
* @return same object which was passed as {@code output}, but filled with new parameters.
|
||||
*/
|
||||
public CameraParametersDecorator toPlatformParameters(Parameters parameters,
|
||||
CameraParametersDecorator output) {
|
||||
for (Parameters.Type storedType : parameters.storedTypes()) {
|
||||
applyParameter(
|
||||
storedType,
|
||||
parameters,
|
||||
output
|
||||
);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts {@link Camera.Parameters} to {@link Parameters}.
|
||||
*
|
||||
* @param platformParameters parameters which should be converted.
|
||||
* @return Converted parameters object
|
||||
*/
|
||||
public Parameters fromPlatformParameters(CameraParametersDecorator platformParameters) {
|
||||
Parameters parameters = new Parameters();
|
||||
|
||||
FocusMode focusMode = FocusCapability.toFocusMode(platformParameters.getFocusMode());
|
||||
parameters.putValue(Parameters.Type.FOCUS_MODE, focusMode);
|
||||
|
||||
Flash flash = FlashCapability.toFlash(platformParameters.getFlashMode());
|
||||
parameters.putValue(Parameters.Type.FLASH, flash);
|
||||
|
||||
Camera.Size platformSize = platformParameters.getPictureSize();
|
||||
Size pictureSize = new Size(platformSize.width, platformSize.height);
|
||||
parameters.putValue(Parameters.Type.PICTURE_SIZE, pictureSize);
|
||||
|
||||
Camera.Size platformPreviewSize = platformParameters.getPreviewSize();
|
||||
Size previewSize = new Size(platformPreviewSize.width, platformPreviewSize.height);
|
||||
parameters.putValue(Parameters.Type.PREVIEW_SIZE, previewSize);
|
||||
|
||||
Integer jpegQuality = platformParameters.getJpegQuality();
|
||||
parameters.putValue(Parameters.Type.JPEG_QUALITY, jpegQuality);
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
private void applyParameter(Parameters.Type type,
|
||||
Parameters input,
|
||||
CameraParametersDecorator output) {
|
||||
switch (type) {
|
||||
case FOCUS_MODE:
|
||||
applyFocusMode(
|
||||
(FocusMode) input.getValue(type),
|
||||
output
|
||||
);
|
||||
break;
|
||||
case FLASH:
|
||||
applyFlash(
|
||||
(Flash) input.getValue(type),
|
||||
output
|
||||
);
|
||||
break;
|
||||
case PICTURE_SIZE:
|
||||
applyPictureSize(
|
||||
(Size) input.getValue(type),
|
||||
output
|
||||
);
|
||||
break;
|
||||
case PREVIEW_SIZE:
|
||||
applyPreviewSize(
|
||||
(Size) input.getValue(type),
|
||||
output
|
||||
);
|
||||
break;
|
||||
case PREVIEW_FPS_RANGE:
|
||||
applyPreviewFpsRange(
|
||||
getRange(type, input),
|
||||
output
|
||||
);
|
||||
break;
|
||||
case SENSOR_SENSITIVITY:
|
||||
applySensorSensitivity(
|
||||
(Integer) input.getValue(type),
|
||||
output
|
||||
);
|
||||
break;
|
||||
case JPEG_QUALITY:
|
||||
applyJpegQuality(
|
||||
(Integer) input.getValue(type),
|
||||
output);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Range<Integer> getRange(Parameters.Type type, Parameters input) {
|
||||
return (Range<Integer>) input.getValue(type);
|
||||
}
|
||||
|
||||
private void applyPreviewSize(Size size,
|
||||
CameraParametersDecorator output) {
|
||||
output.setPreviewSize(size.width, size.height);
|
||||
}
|
||||
|
||||
private void applyPictureSize(Size size,
|
||||
CameraParametersDecorator output) {
|
||||
output.setPictureSize(size.width, size.height);
|
||||
}
|
||||
|
||||
private void applyFlash(Flash flash,
|
||||
CameraParametersDecorator output) {
|
||||
output.setFlashMode(
|
||||
FlashCapability.toCode(flash)
|
||||
);
|
||||
}
|
||||
|
||||
private void applyFocusMode(FocusMode focusMode,
|
||||
CameraParametersDecorator output) {
|
||||
output.setFocusMode(
|
||||
FocusCapability.toCode(focusMode)
|
||||
);
|
||||
}
|
||||
|
||||
private void applyPreviewFpsRange(Range<Integer> fpsRange,
|
||||
CameraParametersDecorator output) {
|
||||
output.setPreviewFpsRange(
|
||||
fpsRange.lowest(), fpsRange.highest()
|
||||
);
|
||||
}
|
||||
|
||||
private void applySensorSensitivity(Integer value,
|
||||
CameraParametersDecorator output) {
|
||||
output.setSensorSensitivityValue(value);
|
||||
}
|
||||
|
||||
private void applyJpegQuality(Integer value,
|
||||
CameraParametersDecorator output){
|
||||
output.setJpegQuality(value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
package io.fotoapparat.hardware.v1;
|
||||
|
||||
import android.graphics.ImageFormat;
|
||||
import android.hardware.Camera;
|
||||
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;
|
||||
import io.fotoapparat.preview.FrameProcessor;
|
||||
import io.fotoapparat.preview.PreviewStream;
|
||||
|
||||
/**
|
||||
* {@link PreviewStream} of Camera v1.
|
||||
*/
|
||||
@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<>();
|
||||
|
||||
private Size previewSize = null;
|
||||
private int frameOrientation = 0;
|
||||
|
||||
public PreviewStream1(Camera camera) {
|
||||
this.camera = camera;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param frameOrientation CW rotation of frames in degrees.
|
||||
*/
|
||||
public void setFrameOrientation(int frameOrientation) {
|
||||
this.frameOrientation = frameOrientation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addFrameToBuffer() {
|
||||
camera.addCallbackBuffer(
|
||||
allocateBuffer(camera.getParameters())
|
||||
);
|
||||
}
|
||||
|
||||
private byte[] allocateBuffer(Camera.Parameters parameters) {
|
||||
ensureNv21Format(parameters);
|
||||
|
||||
Camera.Size previewSize = parameters.getPreviewSize();
|
||||
this.previewSize = new Size(
|
||||
previewSize.width,
|
||||
previewSize.height
|
||||
);
|
||||
|
||||
return new byte[bytesPerFrame(previewSize)];
|
||||
}
|
||||
|
||||
private int bytesPerFrame(Camera.Size previewSize) {
|
||||
return (previewSize.width * previewSize.height * ImageFormat.getBitsPerPixel(ImageFormat.NV21)) / 8;
|
||||
}
|
||||
|
||||
private void ensureNv21Format(Camera.Parameters parameters) {
|
||||
if (parameters.getPreviewFormat() != ImageFormat.NV21) {
|
||||
throw new UnsupportedOperationException("Only NV21 preview format is supported");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addProcessor(@NonNull FrameProcessor processor) {
|
||||
synchronized (frameProcessors) {
|
||||
frameProcessors.add(processor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeProcessor(@NonNull FrameProcessor processor) {
|
||||
synchronized (frameProcessors) {
|
||||
frameProcessors.remove(processor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
addFrameToBuffer();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void dispatchFrame(byte[] image) {
|
||||
ensurePreviewSizeAvailable();
|
||||
|
||||
final Frame frame = new Frame(previewSize, image, frameOrientation);
|
||||
|
||||
for (final FrameProcessor frameProcessor : frameProcessors) {
|
||||
frameProcessor.processFrame(frame);
|
||||
}
|
||||
|
||||
returnFrameToBuffer(frame);
|
||||
}
|
||||
|
||||
private void ensurePreviewSizeAvailable() {
|
||||
if (previewSize == null) {
|
||||
throw new IllegalStateException("previewSize is null. Frame was not added?");
|
||||
}
|
||||
}
|
||||
|
||||
private void returnFrameToBuffer(Frame frame) {
|
||||
camera.addCallbackBuffer(
|
||||
frame.image
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
-123
@@ -1,123 +0,0 @@
|
||||
package io.fotoapparat.hardware.v1.capabilities;
|
||||
|
||||
import android.hardware.Camera;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import io.fotoapparat.hardware.Capabilities;
|
||||
import io.fotoapparat.hardware.v1.Camera1;
|
||||
import io.fotoapparat.hardware.v1.CameraParametersDecorator;
|
||||
import io.fotoapparat.parameter.Flash;
|
||||
import io.fotoapparat.parameter.FocusMode;
|
||||
import io.fotoapparat.parameter.Size;
|
||||
import io.fotoapparat.parameter.range.Range;
|
||||
import io.fotoapparat.parameter.range.Ranges;
|
||||
|
||||
/**
|
||||
* {@link Capabilities} of {@link Camera1}.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class CapabilitiesFactory {
|
||||
|
||||
/**
|
||||
* @return {@link Capabilities} from given camera parameters.
|
||||
*/
|
||||
public Capabilities fromParameters(CameraParametersDecorator parametersProvider) {
|
||||
return new Capabilities(
|
||||
extractPictureSizes(parametersProvider),
|
||||
extractPreviewSizes(parametersProvider),
|
||||
extractFocusModes(parametersProvider),
|
||||
extractFlashModes(parametersProvider),
|
||||
extractPreviewFpsRanges(parametersProvider),
|
||||
extractSensorSensitivityRange(parametersProvider),
|
||||
parametersProvider.isZoomSupported()
|
||||
);
|
||||
}
|
||||
|
||||
private Set<Size> extractPreviewSizes(CameraParametersDecorator parametersProvider) {
|
||||
return mapSizes(
|
||||
parametersProvider.getSupportedPreviewSizes()
|
||||
);
|
||||
}
|
||||
|
||||
private Set<Size> extractPictureSizes(CameraParametersDecorator parametersProvider) {
|
||||
return mapSizes(
|
||||
parametersProvider.getSupportedPictureSizes()
|
||||
);
|
||||
}
|
||||
|
||||
private Set<Size> mapSizes(Collection<Camera.Size> sizes) {
|
||||
HashSet<Size> result = new HashSet<>();
|
||||
|
||||
for (Camera.Size size : sizes) {
|
||||
result.add(new Size(
|
||||
size.width,
|
||||
size.height
|
||||
));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Set<Flash> extractFlashModes(CameraParametersDecorator parameters) {
|
||||
HashSet<Flash> result = new HashSet<>();
|
||||
|
||||
for (String flashMode : supportedFlashModes(parameters)) {
|
||||
result.add(
|
||||
FlashCapability.toFlash(flashMode)
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<String> supportedFlashModes(CameraParametersDecorator parametersProvider) {
|
||||
List<String> supportedFlashModes = parametersProvider.getSupportedFlashModes();
|
||||
return supportedFlashModes != null
|
||||
? supportedFlashModes
|
||||
: Collections.singletonList(Camera.Parameters.FLASH_MODE_OFF);
|
||||
}
|
||||
|
||||
private Set<FocusMode> extractFocusModes(CameraParametersDecorator parametersProvider) {
|
||||
HashSet<FocusMode> result = new HashSet<>();
|
||||
|
||||
for (String focusMode : parametersProvider.getSupportedFocusModes()) {
|
||||
result.add(
|
||||
FocusCapability.toFocusMode(focusMode)
|
||||
);
|
||||
}
|
||||
|
||||
result.add(FocusMode.FIXED);
|
||||
return result;
|
||||
}
|
||||
|
||||
private Set<Range<Integer>> extractPreviewFpsRanges(CameraParametersDecorator parametersProvider) {
|
||||
List<int[]> fpsRanges = parametersProvider.getSupportedPreviewFpsRange();
|
||||
|
||||
if (fpsRanges == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
Set<Range<Integer>> wrappedFpsRanges = new HashSet<>(fpsRanges.size());
|
||||
for (int[] range : fpsRanges) {
|
||||
wrappedFpsRanges.add(Ranges.continuousRange(
|
||||
range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
|
||||
range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]
|
||||
));
|
||||
}
|
||||
return wrappedFpsRanges;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Range<Integer> extractSensorSensitivityRange(CameraParametersDecorator parametersProvider) {
|
||||
final Set<Integer> isoValuesSet = parametersProvider.getSensorSensitivityValues();
|
||||
return Ranges.discreteRange(isoValuesSet);
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user