Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9454f3e4d1 | |||
| 7094f9850a | |||
| 7039cf2059 | |||
| 09a551635c | |||
| 72c7c81a42 | |||
| a7287e363c | |||
| 6156f4c897 | |||
| 29be38b88b | |||
| 69d99e1aea | |||
| af9e8158b8 | |||
| 4494d76bb1 | |||
| f1830853c3 | |||
| 75181ecb92 | |||
| 697f996d17 | |||
| 1783058ade | |||
| f78c324f23 | |||
| b1f6f491cd | |||
| 46fe442a4e | |||
| 27c9a7a910 | |||
| f05fc12039 | |||
| b37c3df8c9 | |||
| 353e8a326b | |||
| 9021507995 | |||
| 4a23a70565 | |||
| 9104a316ee | |||
| 33ce7fa483 | |||
| 45368b3691 | |||
| 65023340e5 | |||
| ede7e4cbbe | |||
| 3bbec9b6d8 | |||
| ec6cdf3d66 | |||
| cb4c8d1ee1 | |||
| 245d74fb46 | |||
| b67ee40be2 | |||
| 66c7489708 | |||
| 96fc39bd84 | |||
| c82b628b44 | |||
| 7ee03f1a8c | |||
| 816d4da6aa | |||
| 4fc20316d0 | |||
| 3b17c030f5 | |||
| 0c17e2bf29 | |||
| 8189847088 | |||
| 36670caf02 |
@@ -6,4 +6,6 @@
|
||||
build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.project
|
||||
.settings
|
||||
|
||||
|
||||
+13
-7
@@ -3,15 +3,21 @@ jdk: oraclejdk8
|
||||
|
||||
android:
|
||||
components:
|
||||
- tools
|
||||
- platform-tools
|
||||
- tools
|
||||
- build-tools-27.0.3
|
||||
- android-27
|
||||
- extra-android-m2repository
|
||||
- tools
|
||||
- platform-tools
|
||||
- tools
|
||||
- build-tools-28.0.3
|
||||
- android-28
|
||||
- extra-android-m2repository
|
||||
|
||||
before_install:
|
||||
- yes | sdkmanager "platforms;android-27"
|
||||
- yes | sdkmanager "platforms;android-28"
|
||||
|
||||
script:
|
||||
- ./gradlew build test
|
||||
|
||||
deploy:
|
||||
provider: script
|
||||
script: ./gradlew bintrayUpload
|
||||
on:
|
||||
tags: true
|
||||
|
||||
@@ -2,19 +2,18 @@
|
||||
|
||||

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

|
||||
|
||||
Camera API in Android is hard. Having 2 different API for new and old Camera does not make things any easier. But fret not, that is your lucky day! After several years of working with Camera, we came up with Fotoapparat.
|
||||
|
||||
What it provides:
|
||||
|
||||
- Camera API which does not allow you to shoot yourself in the foot.
|
||||
- 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.
|
||||
|
||||
- Last, but not least, non 0% test coverage.
|
||||
|
||||
Taking picture becomes as simple as:
|
||||
|
||||
@@ -23,9 +22,9 @@ val fotoapparat = Fotoapparat(
|
||||
context = this,
|
||||
view = cameraView
|
||||
)
|
||||
|
||||
|
||||
fotoapparat.start()
|
||||
|
||||
|
||||
fotoapparat
|
||||
.takePicture()
|
||||
.saveToFile(someFile)
|
||||
@@ -48,7 +47,7 @@ Add `CameraView` to your layout
|
||||
|
||||
Configure `Fotoapparat` instance.
|
||||
|
||||
```kotlin
|
||||
```kotlin
|
||||
Fotoapparat(
|
||||
context = this,
|
||||
view = cameraView, // view which will draw the camera preview
|
||||
@@ -61,12 +60,12 @@ Fotoapparat(
|
||||
),
|
||||
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).
|
||||
|
||||
|
||||
### Step Three
|
||||
|
||||
Call `start()` and `stop()`. No rocket science here.
|
||||
@@ -76,7 +75,7 @@ override fun onStart() {
|
||||
super.onStart()
|
||||
fotoapparat.start()
|
||||
}
|
||||
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
fotoapparat.stop()
|
||||
@@ -89,30 +88,30 @@ Finally, we are ready to take a picture. You have various options.
|
||||
|
||||
```kotlin
|
||||
val photoResult = fotoapparat.takePicture()
|
||||
|
||||
|
||||
// Asynchronously saves photo to file
|
||||
photoResult.saveToFile(someFile)
|
||||
|
||||
|
||||
// Asynchronously converts photo to bitmap and returns the result on the main thread
|
||||
photoResult
|
||||
.toBitmap()
|
||||
.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 the main thread though.
|
||||
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()
|
||||
.toSingle()
|
||||
.subscribe { bitmapPhoto ->
|
||||
|
||||
.subscribe { bitmapPhoto ->
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
@@ -125,23 +124,23 @@ fotoapparat.updateConfiguration(
|
||||
UpdateConfiguration(
|
||||
flashMode = if (isChecked) torch() else off()
|
||||
// ...
|
||||
// all the parameters available in CameraConfiguration
|
||||
// all the parameters available in CameraConfiguration
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
Or alternatively, you may provide updates on an existing full configuration.
|
||||
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
|
||||
// all the parameters available in CameraConfiguration
|
||||
)
|
||||
)
|
||||
```
|
||||
@@ -162,40 +161,21 @@ fotoapparat.switchTo(
|
||||
Add dependency to your `build.gradle`
|
||||
|
||||
```groovy
|
||||
repositories {
|
||||
maven { url 'https://jitpack.io' }
|
||||
}
|
||||
|
||||
implementation 'io.fotoapparat.fotoapparat:library:2.3.0'
|
||||
implementation 'io.fotoapparat:fotoapparat:2.7.0'
|
||||
```
|
||||
|
||||
Camera permission will be automatically added to your `AndroidManifest.xml`. Do not forget to request this permission on Marshmallow and higher.
|
||||
|
||||
#### Snapshot versions
|
||||
|
||||
If you like to test and try the latest added features & bugfixes you can use the `-SNAPSHOT` version.
|
||||
Gradle will download & cache the latest snapshot version, so be sure to delete the cache if you want to try a newer `snapshot` version than the downloaded/cached one.
|
||||
|
||||
```groovy
|
||||
implementation 'io.fotoapparat:fotoapparat:library:-SNAPSHOT'
|
||||
```
|
||||
|
||||
## Face detection
|
||||
|
||||
Optionally, you can check out our other library which adds face detection capabilities - [FaceDetector](https://github.com/Fotoapparat/FaceDetector).
|
||||
|
||||
## Videoapparat
|
||||
|
||||
For video recording possibility, check out our premium library - [Videoapparat](https://github.com/Fotoapparat/Videoapparat)
|
||||
|
||||
|
||||
## Credits
|
||||
|
||||
We want to say thanks to [Mark Murphy](https://github.com/commonsguy) for the awesome job he did with [CWAC-Camera](https://github.com/commonsguy/cwac-camera). We were using his library for a couple of years and now we feel that Fotoapparat is a next step in the right direction.
|
||||
|
||||
We also want to say many thanks to [Leander Lenzing](http://leanderlenzing.com/) for the amazing icon. Don't forget to follow his work in [dribbble](https://dribbble.com/leanderlenzing).
|
||||
|
||||
|
||||
## License
|
||||
|
||||
```
|
||||
|
||||
+19
-16
@@ -1,30 +1,34 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
subprojects {
|
||||
ext {
|
||||
artifactVersion = '2.7.0'
|
||||
}
|
||||
}
|
||||
|
||||
buildscript {
|
||||
ext {
|
||||
versions = [
|
||||
gradle : '4.4.1',
|
||||
kotlin : '1.2.50',
|
||||
kotlin : '1.3.50',
|
||||
code : 1,
|
||||
name : '1.0.0',
|
||||
sdk : [
|
||||
minimum: 14,
|
||||
target : 27
|
||||
target : 28
|
||||
],
|
||||
android: [
|
||||
buildTools: '27.0.3',
|
||||
support : '27.0.2'
|
||||
buildTools: '28.0.3',
|
||||
appcompat : '1.1.0',
|
||||
annotation : '1.1.0',
|
||||
exifinterface : '1.0.0'
|
||||
],
|
||||
rx : [
|
||||
rxJava1: '1.2.9',
|
||||
rxJava2: '2.0.8'
|
||||
rxJava1: '1.3.8',
|
||||
rxJava2: '2.2.12'
|
||||
],
|
||||
test : [
|
||||
junit : '4.12',
|
||||
mockito: '2.13.0'
|
||||
],
|
||||
util : [
|
||||
commons: '2.5'
|
||||
mockito: '2.23.0'
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -33,9 +37,11 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.1.3'
|
||||
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
|
||||
classpath 'com.android.tools.build:gradle:3.5.1'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
|
||||
|
||||
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
|
||||
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +56,3 @@ task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
|
||||
task wrapper(type: Wrapper) {
|
||||
gradleVersion = versions.gradle
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
apply plugin: 'com.jfrog.bintray'
|
||||
apply plugin: 'com.github.dcendents.android-maven'
|
||||
|
||||
ext {
|
||||
bintrayOrganisation = 'fotoapparat'
|
||||
bintrayRepo = 'fotoapparat'
|
||||
|
||||
publishedGroupId = 'io.fotoapparat'
|
||||
|
||||
siteUrl = 'https://github.com/RedApparat/Fotoapparat'
|
||||
gitUrl = 'https://github.com/RedApparat/Fotoapparat.git'
|
||||
|
||||
developerId = 'fotoapparat'
|
||||
developerName = 'Fotoapparat'
|
||||
developerEmail = 'dmitry.zaicew@gmail.com'
|
||||
|
||||
licenseName = 'The Apache Software License, Version 2.0'
|
||||
licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
|
||||
allLicenses = ["Apache-2.0"]
|
||||
}
|
||||
|
||||
group = publishedGroupId
|
||||
version = libraryVersion
|
||||
|
||||
install {
|
||||
repositories.mavenInstaller {
|
||||
pom.project {
|
||||
packaging 'aar'
|
||||
groupId publishedGroupId
|
||||
artifactId artifact
|
||||
|
||||
name libraryName
|
||||
description libraryDescription
|
||||
url siteUrl
|
||||
|
||||
licenses {
|
||||
license {
|
||||
name licenseName
|
||||
url licenseUrl
|
||||
}
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id developerId
|
||||
name developerName
|
||||
email developerEmail
|
||||
}
|
||||
}
|
||||
scm {
|
||||
connection gitUrl
|
||||
developerConnection gitUrl
|
||||
url siteUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task sourcesJar(type: Jar) {
|
||||
classifier = 'sources'
|
||||
from android.sourceSets.main.java.srcDirs
|
||||
}
|
||||
|
||||
task javadoc(type: Javadoc) {
|
||||
source = android.sourceSets.main.java.srcDirs
|
||||
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
|
||||
failOnError = false
|
||||
}
|
||||
|
||||
task javadocJar(type: Jar, dependsOn: javadoc) {
|
||||
classifier = 'javadoc'
|
||||
from javadoc.destinationDir
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives javadocJar
|
||||
archives sourcesJar
|
||||
}
|
||||
|
||||
bintray {
|
||||
user = project.properties["bintray.user"] ?: System.getenv('BINTRAY_USER')
|
||||
key = project.properties["bintray.apikey"] ?: System.getenv('BINTRAY_API_KEY')
|
||||
|
||||
configurations = ['archives']
|
||||
pkg {
|
||||
userOrg = bintrayOrganisation
|
||||
repo = bintrayRepo
|
||||
name = bintrayName
|
||||
desc = libraryDescription
|
||||
websiteUrl = siteUrl
|
||||
vcsUrl = gitUrl
|
||||
licenses = allLicenses
|
||||
dryRun = false
|
||||
publish = true
|
||||
override = false
|
||||
publicDownloadNumbers = true
|
||||
version {
|
||||
desc = libraryDescription
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,17 +11,31 @@ android {
|
||||
defaultConfig {
|
||||
minSdkVersion versions.sdk.minimum
|
||||
targetSdkVersion versions.sdk.target
|
||||
}
|
||||
|
||||
archivesBaseName = 'adapter-rxjava'
|
||||
archivesBaseName = 'adapter-rxjava'
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly project(':fotoapparat')
|
||||
compileOnly "io.reactivex:rxjava:${versions.rx.rxJava1}"
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:${versions.kotlin}"
|
||||
|
||||
testImplementation "io.reactivex:rxjava:${versions.rx.rxJava1}"
|
||||
testImplementation "junit:junit:${versions.test.junit}"
|
||||
}
|
||||
}
|
||||
|
||||
ext {
|
||||
bintrayName = 'adapter-rxjava'
|
||||
|
||||
libraryName = 'Fotoapparat Adapters - RxJava'
|
||||
artifact = 'adapter-rxjava'
|
||||
libraryVersion = artifactVersion
|
||||
|
||||
libraryDescription = 'RxJava1 adapter for Fotoapparat.'
|
||||
}
|
||||
|
||||
apply from: '../../deploy.gradle'
|
||||
|
||||
@@ -11,17 +11,31 @@ android {
|
||||
defaultConfig {
|
||||
minSdkVersion versions.sdk.minimum
|
||||
targetSdkVersion versions.sdk.target
|
||||
}
|
||||
|
||||
archivesBaseName = 'adapter-rxjava2'
|
||||
archivesBaseName = 'adapter-rxjava2'
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly project(':fotoapparat')
|
||||
compileOnly "io.reactivex.rxjava2:rxjava:${versions.rx.rxJava2}"
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:${versions.kotlin}"
|
||||
|
||||
testImplementation "io.reactivex.rxjava2:rxjava:${versions.rx.rxJava2}"
|
||||
testImplementation "junit:junit:${versions.test.junit}"
|
||||
}
|
||||
}
|
||||
|
||||
ext {
|
||||
bintrayName = 'adapter-rxjava2'
|
||||
|
||||
libraryName = 'Fotoapparat Adapters - RxJava2'
|
||||
artifact = 'adapter-rxjava2'
|
||||
libraryVersion = artifactVersion
|
||||
|
||||
libraryDescription = 'RxJava2 adapter for Fotoapparat.'
|
||||
}
|
||||
|
||||
apply from: '../../deploy.gradle'
|
||||
|
||||
+21
-10
@@ -4,8 +4,6 @@ apply plugin: 'kotlin-android'
|
||||
|
||||
group = 'io.fotoapparat'
|
||||
|
||||
kotlin.experimental.coroutines 'enable'
|
||||
|
||||
android {
|
||||
buildToolsVersion versions.android.buildTools
|
||||
compileSdkVersion versions.sdk.target
|
||||
@@ -13,8 +11,6 @@ android {
|
||||
defaultConfig {
|
||||
minSdkVersion versions.sdk.minimum
|
||||
targetSdkVersion versions.sdk.target
|
||||
|
||||
archivesBaseName = 'library'
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
@@ -26,15 +22,30 @@ android {
|
||||
testOptions {
|
||||
unitTests.returnDefaultValues = true
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
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'
|
||||
|
||||
implementation "androidx.annotation:annotation:${versions.android.annotation}"
|
||||
implementation "androidx.exifinterface:exifinterface:${versions.android.exifinterface}"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:${versions.kotlin}"
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0'
|
||||
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}"
|
||||
}
|
||||
}
|
||||
|
||||
ext {
|
||||
bintrayName = 'fotoapparat'
|
||||
|
||||
libraryName = 'Fotoapparat'
|
||||
artifact = 'fotoapparat'
|
||||
libraryVersion = artifactVersion
|
||||
|
||||
libraryDescription = 'Camera library for Android with a more user-friendly API.'
|
||||
}
|
||||
|
||||
apply from: '../deploy.gradle'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.fotoapparat
|
||||
|
||||
import android.content.Context
|
||||
import android.support.annotation.FloatRange
|
||||
import androidx.annotation.FloatRange
|
||||
import io.fotoapparat.concurrent.CameraExecutor
|
||||
import io.fotoapparat.concurrent.CameraExecutor.Operation
|
||||
import io.fotoapparat.configuration.CameraConfiguration
|
||||
@@ -63,10 +63,12 @@ class Fotoapparat
|
||||
executor = executor
|
||||
)
|
||||
|
||||
private val orientationSensor = OrientationSensor(
|
||||
context = context,
|
||||
device = device
|
||||
)
|
||||
private val orientationSensor by lazy {
|
||||
OrientationSensor(
|
||||
context = context,
|
||||
device = device
|
||||
)
|
||||
}
|
||||
|
||||
init {
|
||||
logger.recordMethod()
|
||||
@@ -220,6 +222,7 @@ class Fotoapparat
|
||||
|
||||
executor.execute(Operation(cancellable = true) {
|
||||
device.switchCamera(
|
||||
orientationSensor = orientationSensor,
|
||||
newLensPositionSelector = lensPosition,
|
||||
newConfiguration = cameraConfiguration,
|
||||
mainThreadErrorCallback = mainThreadErrorCallback
|
||||
|
||||
@@ -7,6 +7,7 @@ import io.fotoapparat.error.CameraErrorListener
|
||||
import io.fotoapparat.log.Logger
|
||||
import io.fotoapparat.log.none
|
||||
import io.fotoapparat.parameter.ScaleType
|
||||
import io.fotoapparat.preview.Frame
|
||||
import io.fotoapparat.selector.*
|
||||
import io.fotoapparat.util.FrameProcessor
|
||||
import io.fotoapparat.view.CameraRenderer
|
||||
@@ -131,9 +132,9 @@ class FotoapparatBuilder internal constructor(private var context: Context) {
|
||||
* @param frameProcessor receives preview frames for processing.
|
||||
* @see FrameProcessorJava
|
||||
*/
|
||||
fun frameProcessor(frameProcessor: FrameProcessorJava): FotoapparatBuilder = apply {
|
||||
fun frameProcessor(frameProcessor: FrameProcessorJava?): FotoapparatBuilder = apply {
|
||||
configuration = configuration.copy(
|
||||
frameProcessor = { frameProcessor.process(it) }
|
||||
frameProcessor = frameProcessor?.let { it::process }
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,16 +8,22 @@ sealed class LensPosition : Characteristic {
|
||||
/**
|
||||
* The back camera.
|
||||
*/
|
||||
object Back : LensPosition()
|
||||
object Back : LensPosition() {
|
||||
override fun toString(): String = "LensPosition.Back"
|
||||
}
|
||||
|
||||
/**
|
||||
* The front camera.
|
||||
*/
|
||||
object Front : LensPosition()
|
||||
object Front : LensPosition() {
|
||||
override fun toString(): String = "LensPosition.Front"
|
||||
}
|
||||
|
||||
/**
|
||||
* An external camera.
|
||||
*/
|
||||
object External : LensPosition()
|
||||
object External : LensPosition() {
|
||||
override fun toString(): String = "LensPosition.External"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package io.fotoapparat.concurrent
|
||||
|
||||
import android.os.Looper
|
||||
|
||||
/**
|
||||
* Throws [IllegalThreadStateException] if called from main thread.
|
||||
*/
|
||||
fun ensureBackgroundThread() {
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
throw IllegalThreadStateException("Operation should not run from main thread.")
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import io.fotoapparat.selector.*
|
||||
import io.fotoapparat.util.FrameProcessor
|
||||
import io.fotoapparat.preview.FrameProcessor as FrameProcessorJava
|
||||
|
||||
|
||||
private const val DEFAULT_JPEG_QUALITY = 90
|
||||
private const val DEFAULT_EXPOSURE_COMPENSATION = 0
|
||||
|
||||
@@ -21,7 +20,7 @@ data class CameraConfiguration(
|
||||
),
|
||||
override val jpegQuality: QualitySelector = manualJpegQuality(DEFAULT_JPEG_QUALITY),
|
||||
override val exposureCompensation: ExposureSelector = manualExposure(DEFAULT_EXPOSURE_COMPENSATION),
|
||||
override val frameProcessor: FrameProcessor = {},
|
||||
override val frameProcessor: FrameProcessor? = null,
|
||||
override val previewFpsRange: FpsRangeSelector = highestFps(),
|
||||
override val antiBandingMode: AntiBandingModeSelector = firstAvailable(
|
||||
auto(),
|
||||
@@ -95,9 +94,9 @@ data class CameraConfiguration(
|
||||
)
|
||||
}
|
||||
|
||||
fun frameProcessor(frameProcessor: FrameProcessorJava): Builder = apply {
|
||||
fun frameProcessor(frameProcessor: FrameProcessorJava?): Builder = apply {
|
||||
cameraConfiguration = cameraConfiguration.copy(
|
||||
frameProcessor = frameProcessor::process
|
||||
frameProcessor = frameProcessor?.let { it::process }
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ data class UpdateConfiguration(
|
||||
)
|
||||
}
|
||||
|
||||
fun frameProcessor(frameProcessor: FrameProcessor): Builder = apply {
|
||||
fun frameProcessor(frameProcessor: FrameProcessor?): Builder = apply {
|
||||
configuration = configuration.copy(
|
||||
frameProcessor = frameProcessor
|
||||
)
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package io.fotoapparat.coroutines
|
||||
|
||||
import kotlinx.coroutines.experimental.CompletableDeferred
|
||||
import kotlinx.coroutines.experimental.Deferred
|
||||
import kotlinx.coroutines.experimental.channels.BroadcastChannel
|
||||
import kotlinx.coroutines.experimental.channels.ConflatedBroadcastChannel
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.channels.BroadcastChannel
|
||||
import kotlinx.coroutines.channels.ConflatedBroadcastChannel
|
||||
|
||||
/**
|
||||
* A [ConflatedBroadcastChannel] which exposes a [getValue] which will [await] for at least one value.
|
||||
*/
|
||||
@ExperimentalCoroutinesApi
|
||||
internal class AwaitBroadcastChannel<T>(
|
||||
private val channel: ConflatedBroadcastChannel<T> = ConflatedBroadcastChannel(),
|
||||
private val deferred: CompletableDeferred<Boolean> = CompletableDeferred()
|
||||
@@ -30,4 +30,14 @@ internal class AwaitBroadcastChannel<T>(
|
||||
deferred.complete(true)
|
||||
channel.send(element)
|
||||
}
|
||||
}
|
||||
|
||||
override fun cancel(cause: CancellationException?) {
|
||||
channel.cancel(cause)
|
||||
deferred.cancel(cause)
|
||||
}
|
||||
|
||||
override fun cancel(cause: Throwable?): Boolean {
|
||||
deferred.cancel(cause?.message ?:"", cause)
|
||||
return channel.close(cause)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.os.Looper
|
||||
import io.fotoapparat.exception.camera.CameraException
|
||||
import io.fotoapparat.hardware.executeMainThread
|
||||
|
||||
|
||||
typealias CameraErrorCallback = (CameraException) -> Unit
|
||||
|
||||
/**
|
||||
@@ -16,4 +15,4 @@ fun CameraErrorCallback.onMainThread(): CameraErrorCallback = { cameraException
|
||||
} else {
|
||||
executeMainThread { this(cameraException) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.fotoapparat.exif
|
||||
|
||||
import android.media.ExifInterface
|
||||
import androidx.exifinterface.media.ExifInterface
|
||||
import io.fotoapparat.exception.FileSaveException
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
@@ -4,8 +4,8 @@ package io.fotoapparat.hardware
|
||||
|
||||
import android.hardware.Camera
|
||||
import android.media.MediaRecorder
|
||||
import android.support.annotation.FloatRange
|
||||
import android.view.Surface
|
||||
import androidx.annotation.FloatRange
|
||||
import io.fotoapparat.capability.Capabilities
|
||||
import io.fotoapparat.capability.provide.getCapabilities
|
||||
import io.fotoapparat.characteristic.Characteristics
|
||||
@@ -15,7 +15,6 @@ import io.fotoapparat.exception.camera.CameraException
|
||||
import io.fotoapparat.hardware.metering.FocalRequest
|
||||
import io.fotoapparat.hardware.metering.convert.toFocusAreas
|
||||
import io.fotoapparat.hardware.orientation.*
|
||||
import io.fotoapparat.hardware.orientation.Orientation.Vertical.Portrait
|
||||
import io.fotoapparat.log.Logger
|
||||
import io.fotoapparat.parameter.FocusMode
|
||||
import io.fotoapparat.parameter.Resolution
|
||||
@@ -28,13 +27,12 @@ import io.fotoapparat.result.Photo
|
||||
import io.fotoapparat.util.FrameProcessor
|
||||
import io.fotoapparat.util.lineSeparator
|
||||
import io.fotoapparat.view.Preview
|
||||
import kotlinx.coroutines.experimental.CompletableDeferred
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
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
|
||||
|
||||
/**
|
||||
@@ -52,9 +50,9 @@ internal open class CameraDevice(
|
||||
private lateinit var camera: Camera
|
||||
|
||||
private var cachedCameraParameters: Camera.Parameters? = null
|
||||
private var displayOrientation: Orientation = Portrait
|
||||
private var imageOrientation: Orientation = Portrait
|
||||
private var previewOrientation: Orientation = Portrait
|
||||
private lateinit var displayOrientation: Orientation
|
||||
private lateinit var imageOrientation: Orientation
|
||||
private lateinit var previewOrientation: Orientation
|
||||
|
||||
/**
|
||||
* Opens a connection to a camera.
|
||||
@@ -207,9 +205,16 @@ internal open class CameraDevice(
|
||||
cameraIsMirrored = characteristics.isMirrored
|
||||
)
|
||||
|
||||
logger.log("Image orientation is: $imageOrientation. " + lineSeparator +
|
||||
"Display orientation is: $displayOrientation. " + lineSeparator +
|
||||
"Preview orientation is: $previewOrientation."
|
||||
logger.log("Orientations: $lineSeparator" +
|
||||
"Screen orientation (preview) is: ${orientationState.screenOrientation}. " + lineSeparator +
|
||||
"Camera sensor orientation is always at: ${characteristics.cameraOrientation}. " + lineSeparator +
|
||||
"Camera is " + if (characteristics.isMirrored) "mirrored." else "not mirrored."
|
||||
)
|
||||
|
||||
logger.log("Orientation adjustments: $lineSeparator" +
|
||||
"Image orientation will be adjusted by: ${imageOrientation.degrees} degrees. " + lineSeparator +
|
||||
"Display orientation will be adjusted by: ${displayOrientation.degrees} degrees. " + lineSeparator +
|
||||
"Preview orientation will be adjusted by: ${previewOrientation.degrees} degrees."
|
||||
)
|
||||
|
||||
previewStream.frameOrientation = previewOrientation
|
||||
@@ -289,7 +294,6 @@ internal open class CameraDevice(
|
||||
return previewResolution
|
||||
}
|
||||
|
||||
|
||||
private fun setZoomSafely(@FloatRange(from = 0.0, to = 1.0) level: Float) {
|
||||
try {
|
||||
setZoomUnsafe(level)
|
||||
@@ -359,16 +363,13 @@ internal open class CameraDevice(
|
||||
|
||||
private fun Camera.clearFocusingAreas() {
|
||||
parameters = parameters.apply {
|
||||
with(capabilities) {
|
||||
meteringAreas = null
|
||||
focusAreas = null
|
||||
}
|
||||
meteringAreas = null
|
||||
focusAreas = null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private const val AUTOFOCUS_TIMEOUT_SECONDS = 3L
|
||||
|
||||
private fun Camera.takePhoto(imageRotation: Int): Photo {
|
||||
|
||||
@@ -18,8 +18,7 @@ import io.fotoapparat.selector.LensPositionSelector
|
||||
import io.fotoapparat.util.FrameProcessor
|
||||
import io.fotoapparat.view.CameraRenderer
|
||||
import io.fotoapparat.view.FocalPointSelector
|
||||
import kotlinx.coroutines.experimental.CompletableDeferred
|
||||
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
|
||||
/**
|
||||
* Phone.
|
||||
@@ -150,12 +149,13 @@ internal open class Device(
|
||||
/**
|
||||
* @return The frame processor.
|
||||
*/
|
||||
open fun getFrameProcessor(): FrameProcessor = savedConfiguration.frameProcessor
|
||||
open fun getFrameProcessor(): FrameProcessor? = savedConfiguration.frameProcessor
|
||||
|
||||
/**
|
||||
* @return The desired from the user camera lens position.
|
||||
*/
|
||||
open fun getLensPositionSelector(): LensPositionSelector = lensPositionSelector
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,7 +167,8 @@ internal fun updateConfiguration(
|
||||
) = CameraConfiguration(
|
||||
flashMode = newConfiguration.flashMode ?: savedConfiguration.flashMode,
|
||||
focusMode = newConfiguration.focusMode ?: savedConfiguration.focusMode,
|
||||
exposureCompensation = newConfiguration.exposureCompensation ?: savedConfiguration.exposureCompensation,
|
||||
exposureCompensation = newConfiguration.exposureCompensation
|
||||
?: savedConfiguration.exposureCompensation,
|
||||
frameProcessor = newConfiguration.frameProcessor ?: savedConfiguration.frameProcessor,
|
||||
previewFpsRange = newConfiguration.previewFpsRange ?: savedConfiguration.previewFpsRange,
|
||||
sensorSensitivity = newConfiguration.sensorSensitivity
|
||||
@@ -190,4 +191,4 @@ internal fun selectCamera(
|
||||
val desiredPosition = lensPositionSelector(lensPositions)
|
||||
|
||||
return availableCameras.find { it.characteristics.lensPosition == desiredPosition }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.os.Handler
|
||||
import android.os.Looper
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
|
||||
private val loggingExecutor = Executors.newSingleThreadExecutor()
|
||||
private val mainThreadHandler = Handler(Looper.getMainLooper())
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package io.fotoapparat.hardware.metering
|
||||
|
||||
|
||||
/**
|
||||
* A point in arbitrary scale.
|
||||
*/
|
||||
|
||||
@@ -18,12 +18,16 @@ sealed class Orientation(
|
||||
/**
|
||||
* A vertical, normal orientation.
|
||||
*/
|
||||
object Portrait : Vertical(0)
|
||||
object Portrait : Vertical(0) {
|
||||
override fun toString(): String = "Orientation.Vertical.Portrait"
|
||||
}
|
||||
|
||||
/**
|
||||
* A reversed (flipped phone) orientation.
|
||||
*/
|
||||
object ReversePortrait : Vertical(180)
|
||||
object ReversePortrait : Vertical(180) {
|
||||
override fun toString(): String = "Orientation.Vertical.ReversePortrait"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,12 +39,16 @@ sealed class Orientation(
|
||||
/**
|
||||
* A 90 degrees clockwise from "normal", orientation.
|
||||
*/
|
||||
object Landscape : Horizontal(90)
|
||||
object Landscape : Horizontal(90) {
|
||||
override fun toString(): String = "Orientation.Horizontal.Landscape"
|
||||
}
|
||||
|
||||
/**
|
||||
* A 90 degrees counter-clockwise from "normal", orientation.
|
||||
*/
|
||||
object ReverseLandscape : Horizontal(270)
|
||||
object ReverseLandscape : Horizontal(270) {
|
||||
override fun toString(): String = "Orientation.Horizontal.ReverseLandscape"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+16
-12
@@ -4,7 +4,6 @@ import android.content.Context
|
||||
import io.fotoapparat.hardware.Device
|
||||
import io.fotoapparat.hardware.orientation.Orientation.Vertical.Portrait
|
||||
|
||||
|
||||
/**
|
||||
* Monitors orientation of the device.
|
||||
*/
|
||||
@@ -13,23 +12,28 @@ internal open class OrientationSensor(
|
||||
private val device: Device
|
||||
) {
|
||||
|
||||
private lateinit var listener: (OrientationState) -> Unit
|
||||
private val onOrientationChanged: (DeviceRotationDegrees) -> Unit = { deviceRotation ->
|
||||
deviceRotation.toClosestRightAngle()
|
||||
.toOrientation()
|
||||
.takeIf { it != lastKnownDeviceOrientation }
|
||||
?.let {
|
||||
val state = OrientationState(
|
||||
deviceOrientation = it,
|
||||
screenOrientation = device.getScreenOrientation()
|
||||
.let { deviceOrientation ->
|
||||
val screenOrientation = device.getScreenOrientation()
|
||||
|
||||
val newState = OrientationState(
|
||||
deviceOrientation = deviceOrientation,
|
||||
screenOrientation = screenOrientation
|
||||
)
|
||||
|
||||
lastKnownDeviceOrientation = state.deviceOrientation
|
||||
listener(state)
|
||||
if (newState != lastKnownOrientationState) {
|
||||
lastKnownOrientationState = newState
|
||||
listener(newState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var listener: (OrientationState) -> Unit
|
||||
private var lastKnownDeviceOrientation: Orientation = Portrait
|
||||
open var lastKnownOrientationState: OrientationState = OrientationState(
|
||||
deviceOrientation = Portrait,
|
||||
screenOrientation = device.getScreenOrientation()
|
||||
)
|
||||
|
||||
constructor(
|
||||
context: Context,
|
||||
@@ -58,4 +62,4 @@ internal open class OrientationSensor(
|
||||
rotationListener.disable()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ 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.
|
||||
*/
|
||||
@@ -19,4 +18,4 @@ internal open class RotationListener(
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package io.fotoapparat.log
|
||||
|
||||
|
||||
/**
|
||||
* Logger which delegates messages to multiple other loggers.
|
||||
*/
|
||||
@@ -10,4 +9,4 @@ internal class CompositeLogger(private val loggers: List<Logger>) : Logger {
|
||||
loggers.forEach { it.log(message) }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,21 +8,29 @@ sealed class AntiBandingMode : Parameter {
|
||||
/**
|
||||
* Auto adjust. This should be the default.
|
||||
*/
|
||||
object Auto : AntiBandingMode()
|
||||
object Auto : AntiBandingMode() {
|
||||
override fun toString(): String = "AntiBandingMode.Auto"
|
||||
}
|
||||
|
||||
/**
|
||||
* Anti Banding is set to 50Hz light frequency.
|
||||
*/
|
||||
object HZ50 : AntiBandingMode()
|
||||
object HZ50 : AntiBandingMode() {
|
||||
override fun toString(): String = "AntiBandingMode.HZ50"
|
||||
}
|
||||
|
||||
/**
|
||||
* Anti Banding is set to 60Hz light frequency.
|
||||
*/
|
||||
object HZ60 : AntiBandingMode()
|
||||
object HZ60 : AntiBandingMode() {
|
||||
override fun toString(): String = "AntiBandingMode.HZ60"
|
||||
}
|
||||
|
||||
/**
|
||||
* Anti Banding is not supported or ignored.
|
||||
*/
|
||||
object None : AntiBandingMode()
|
||||
object None : AntiBandingMode() {
|
||||
override fun toString(): String = "AntiBandingMode.None"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,27 +8,37 @@ sealed class Flash : Parameter {
|
||||
/**
|
||||
* Camera flash will not fire.
|
||||
*/
|
||||
object Off : Flash()
|
||||
object Off : Flash() {
|
||||
override fun toString(): String = "Flash.Off"
|
||||
}
|
||||
|
||||
/**
|
||||
* Camera flash will always fire regardless light conditions.
|
||||
*/
|
||||
object On : Flash()
|
||||
object On : Flash() {
|
||||
override fun toString(): String = "Flash.On"
|
||||
}
|
||||
|
||||
/**
|
||||
* Camera flash will fire only in low light conditions.
|
||||
*/
|
||||
object Auto : Flash()
|
||||
object Auto : Flash() {
|
||||
override fun toString(): String = "Flash.Auto"
|
||||
}
|
||||
|
||||
/**
|
||||
* If deemed necessary by the camera device, a red eye reduction flash will fire during the
|
||||
* precapture sequence in low light conditions.
|
||||
*/
|
||||
object AutoRedEye : Flash()
|
||||
object AutoRedEye : Flash() {
|
||||
override fun toString(): String = "Flash.AutoRedEye"
|
||||
}
|
||||
|
||||
/**
|
||||
* Transition flash to continuously on.
|
||||
*/
|
||||
object Torch : Flash()
|
||||
object Torch : Flash() {
|
||||
override fun toString(): String = "Flash.Torch"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,40 +8,54 @@ sealed class FocusMode : Parameter {
|
||||
/**
|
||||
* Focus is not adjustable. It is always used by devices which do not support auto-focus.
|
||||
*/
|
||||
object Fixed : FocusMode()
|
||||
object Fixed : FocusMode() {
|
||||
override fun toString(): String = "FocusMode.Fixed"
|
||||
}
|
||||
|
||||
/**
|
||||
* Camera is focused at infinity.
|
||||
*/
|
||||
object Infinity : FocusMode()
|
||||
object Infinity : FocusMode() {
|
||||
override fun toString(): String = "FocusMode.Infinity"
|
||||
}
|
||||
|
||||
/**
|
||||
* Macro focus mode.
|
||||
*/
|
||||
object Macro : FocusMode()
|
||||
object Macro : FocusMode() {
|
||||
override fun toString(): String = "FocusMode.Macro"
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto focus. Camera is trying to focus automatically when manually requested.
|
||||
*/
|
||||
object Auto : FocusMode()
|
||||
object Auto : FocusMode() {
|
||||
override fun toString(): String = "FocusMode.Auto"
|
||||
}
|
||||
|
||||
/**
|
||||
* Camera is constantly trying to stay in focus.
|
||||
*
|
||||
* The speed of focus change is more aggressive than [ContinuousFocusVideo].
|
||||
*/
|
||||
object ContinuousFocusPicture : FocusMode()
|
||||
object ContinuousFocusPicture : FocusMode() {
|
||||
override fun toString(): String = "FocusMode.ContinuousFocusPicture"
|
||||
}
|
||||
|
||||
/**
|
||||
* Camera is constantly trying to stay in focus.
|
||||
*
|
||||
* The speed of focus change is smoother than [ContinuousFocusPicture].
|
||||
*/
|
||||
object ContinuousFocusVideo : FocusMode()
|
||||
object ContinuousFocusVideo : FocusMode() {
|
||||
override fun toString(): String = "FocusMode.ContinuousFocusVideo"
|
||||
}
|
||||
|
||||
/**
|
||||
* The camera device will produce images with an extended depth of field.
|
||||
*/
|
||||
object Edof : FocusMode()
|
||||
object Edof : FocusMode() {
|
||||
override fun toString(): String = "FocusMode.Edof"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,7 +26,5 @@ data class FpsRange(
|
||||
/**
|
||||
* `true` if the current range is fixed (min == max).
|
||||
*/
|
||||
val isFixed by lazy {
|
||||
max == min
|
||||
}
|
||||
val isFixed get() = max == min
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.fotoapparat.parameter
|
||||
|
||||
import android.support.annotation.IntRange
|
||||
import androidx.annotation.IntRange
|
||||
|
||||
/**
|
||||
* Resolution. Units in pixels.
|
||||
@@ -13,22 +13,21 @@ data class Resolution(
|
||||
/**
|
||||
* The total area this [Resolution] is covering.
|
||||
*/
|
||||
val area: Int by lazy { width * height }
|
||||
val area: Int get() = width * height
|
||||
|
||||
/**
|
||||
* The aspect ratio for this size. [Float.NaN] if invalid dimensions.
|
||||
*/
|
||||
val aspectRatio: Float by lazy {
|
||||
when {
|
||||
val aspectRatio: Float
|
||||
get() = when {
|
||||
width == 0 -> Float.NaN
|
||||
height == 0 -> Float.NaN
|
||||
else -> width.toFloat() / height
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return new instance of [Resolution] with width and height being swapped.
|
||||
*/
|
||||
fun flipDimensions(): Resolution = Resolution(height, width)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,13 @@ enum class ScaleType {
|
||||
* The preview will be scaled so as its one dimensions will be equal and the other one equal or
|
||||
* smaller than the corresponding dimension of the view
|
||||
*/
|
||||
CenterInside
|
||||
CenterInside,
|
||||
|
||||
|
||||
/**
|
||||
* The preview will be scaled so as its one dimensions will be equal and the other one equal or
|
||||
* larger than the corresponding dimension of the view with focus on the top part
|
||||
*/
|
||||
TopCrop,
|
||||
|
||||
}
|
||||
@@ -60,7 +60,7 @@ internal class SupportedParameters(
|
||||
* and [io.fotoapparat.parameter.Zoom.VariableZoom] with max zoom level otherwise.
|
||||
*/
|
||||
val supportedZoom by lazy {
|
||||
if (cameraParameters.isZoomSupported) Zoom.VariableZoom(cameraParameters.maxZoom) else Zoom.FixedZoom
|
||||
if (cameraParameters.isZoomSupported) Zoom.VariableZoom(cameraParameters.maxZoom, cameraParameters.zoomRatios) else Zoom.FixedZoom
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,11 +8,18 @@ sealed class Zoom {
|
||||
/**
|
||||
* The camera can only support one, fixed zoom level.
|
||||
*/
|
||||
object FixedZoom : Zoom()
|
||||
object FixedZoom : Zoom() {
|
||||
override fun toString(): String = "Zoom.FixedZoom"
|
||||
}
|
||||
|
||||
/**
|
||||
* The camera can only support a variable zoom level between (and including) 0 and [maxZoom] values.
|
||||
* [zoomRatios] is a list of all zoom values. Ratios are in 1/100 increments. For example, zoom of
|
||||
* 2.7x is returned as 270. The number of elements is [maxZoom] + 1. List is sorted from small to
|
||||
* large. First element is always 100. The last element is the zoom ratio of the maximum zoom value.
|
||||
*/
|
||||
data class VariableZoom(val maxZoom: Int) : Zoom()
|
||||
data class VariableZoom(val maxZoom: Int, val zoomRatios: List<Int>) : Zoom() {
|
||||
override fun toString(): String = "Zoom.VariableZoom(maxZoom=$maxZoom, zoomRatios=$zoomRatios)"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import io.fotoapparat.parameter.*
|
||||
import io.fotoapparat.util.lineSeparator
|
||||
import io.fotoapparat.util.wrap
|
||||
|
||||
|
||||
/**
|
||||
* Parameters of [CameraDevice].
|
||||
*/
|
||||
@@ -32,4 +31,4 @@ data class CameraParameters(
|
||||
"pictureResolution:" + pictureResolution.wrap() +
|
||||
"previewResolution:" + previewResolution.wrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-1
@@ -10,7 +10,6 @@ import io.fotoapparat.parameter.Resolution
|
||||
import io.fotoapparat.parameter.camera.CameraParameters
|
||||
import io.fotoapparat.selector.*
|
||||
|
||||
|
||||
/**
|
||||
* @return [CameraParameters] which will be used by [CameraDevice].
|
||||
*/
|
||||
|
||||
@@ -8,11 +8,15 @@ sealed class FocusResult {
|
||||
/**
|
||||
* Camera is unable to focus for some reason.
|
||||
*/
|
||||
object UnableToFocus : FocusResult()
|
||||
object UnableToFocus : FocusResult() {
|
||||
override fun toString(): String = "FocusResult.UnableToFocus"
|
||||
}
|
||||
|
||||
/**
|
||||
* Camera is focused successfully.
|
||||
*/
|
||||
object Focused : FocusResult()
|
||||
object Focused : FocusResult() {
|
||||
override fun toString(): String = "FocusResult.Focused"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.fotoapparat.result
|
||||
|
||||
import io.fotoapparat.capability.Capabilities
|
||||
import io.fotoapparat.concurrent.ensureBackgroundThread
|
||||
import io.fotoapparat.exception.UnableToDecodeBitmapException
|
||||
import io.fotoapparat.hardware.executeMainThread
|
||||
import io.fotoapparat.hardware.pendingResultExecutor
|
||||
@@ -8,7 +9,6 @@ import io.fotoapparat.log.Logger
|
||||
import io.fotoapparat.parameter.camera.CameraParameters
|
||||
import java.util.concurrent.*
|
||||
|
||||
|
||||
/**
|
||||
* Result which might not be readily available at the given moment but will be available in the
|
||||
* future.
|
||||
@@ -20,7 +20,10 @@ internal constructor(
|
||||
private val executor: Executor
|
||||
) {
|
||||
private val resultUnsafe: T
|
||||
get() = future.get()
|
||||
get() {
|
||||
ensureBackgroundThread()
|
||||
return future.get()
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms result from one type to another.
|
||||
@@ -69,16 +72,24 @@ internal constructor(
|
||||
fun whenAvailable(callback: (T?) -> Unit) {
|
||||
executor.execute {
|
||||
try {
|
||||
resultUnsafe.notifyCallbackOnMainThread(callback)
|
||||
val result = resultUnsafe
|
||||
notifyOnMainThread {
|
||||
callback(result)
|
||||
}
|
||||
} catch (e: UnableToDecodeBitmapException) {
|
||||
logger.log("Couldn't decode bitmap from byte array")
|
||||
notifyOnMainThread {
|
||||
callback(null)
|
||||
}
|
||||
} catch (e: InterruptedException) {
|
||||
logger.log("Couldn't deliver pending result: Camera stopped before delivering result.")
|
||||
} catch (e: CancellationException) {
|
||||
logger.log("Couldn't deliver pending result: Camera operation was cancelled.")
|
||||
} catch (e: ExecutionException) {
|
||||
logger.log("Couldn't deliver pending result: Operation failed internally.")
|
||||
callback(null)
|
||||
notifyOnMainThread {
|
||||
callback(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,9 +117,9 @@ internal constructor(
|
||||
|
||||
}
|
||||
|
||||
private fun <T> T.notifyCallbackOnMainThread(callback: (T) -> Unit) {
|
||||
private fun notifyOnMainThread(function: () -> Unit) {
|
||||
executeMainThread {
|
||||
callback(this)
|
||||
function()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.fotoapparat.result
|
||||
|
||||
import android.graphics.BitmapFactory
|
||||
import java.util.*
|
||||
import java.util.Arrays
|
||||
|
||||
/**
|
||||
* Taken photo.
|
||||
@@ -27,22 +27,20 @@ data class Photo(
|
||||
/**
|
||||
* The height of the photo.
|
||||
*/
|
||||
val height by lazy { decodedBounds.height }
|
||||
val height: Int
|
||||
get() = decodedBounds.outHeight
|
||||
|
||||
/**
|
||||
* The width of the photo.
|
||||
*/
|
||||
val width by lazy { decodedBounds.width }
|
||||
val width: Int
|
||||
get() = decodedBounds.outWidth
|
||||
|
||||
private val decodedBounds by lazy {
|
||||
BitmapFactory.decodeByteArray(
|
||||
encodedImage,
|
||||
0,
|
||||
encodedImage.size,
|
||||
BitmapFactory.Options().apply {
|
||||
inJustDecodeBounds = true
|
||||
}
|
||||
)
|
||||
BitmapFactory.Options().apply {
|
||||
inJustDecodeBounds = true
|
||||
BitmapFactory.decodeByteArray(encodedImage, 0, encodedImage.size, this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
@@ -63,14 +61,14 @@ data class Photo(
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String =
|
||||
"Photo(encodedImage=ByteArray(${encodedImage.size}) rotationDegrees=$rotationDegrees)"
|
||||
|
||||
companion object {
|
||||
|
||||
private val EMPTY by lazy { Photo(encodedImage = ByteArray(0), rotationDegrees = 0) }
|
||||
|
||||
/**
|
||||
* @return empty [Photo].
|
||||
*/
|
||||
internal fun empty(): Photo = EMPTY
|
||||
internal fun empty(): Photo = Photo(encodedImage = ByteArray(0), rotationDegrees = 0)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+18
-3
@@ -23,7 +23,8 @@ internal class BitmapPhotoTransformer(
|
||||
desiredResolution = desiredResolution
|
||||
)
|
||||
|
||||
val decodedBitmap = input.decodeBitmap(scaleFactor) ?: throw UnableToDecodeBitmapException()
|
||||
val decodedBitmap = input.decodeBitmap(scaleFactor, originalResolution, desiredResolution)
|
||||
?: throw UnableToDecodeBitmapException()
|
||||
|
||||
val bitmap = if (decodedBitmap.width == desiredResolution.width && decodedBitmap.height == desiredResolution.height) {
|
||||
decodedBitmap
|
||||
@@ -44,14 +45,28 @@ internal class BitmapPhotoTransformer(
|
||||
|
||||
}
|
||||
|
||||
private fun Photo.decodeBitmap(scaleFactor: Float): Bitmap? {
|
||||
private fun Photo.decodeBitmap(
|
||||
scaleFactor: Float,
|
||||
originalResolution: Resolution,
|
||||
desiredResolution: Resolution
|
||||
): Bitmap? {
|
||||
val options = BitmapFactory.Options()
|
||||
options.inSampleSize = scaleFactor.toInt()
|
||||
options.inScaled = true
|
||||
|
||||
if (desiredResolution.width > desiredResolution.height) {
|
||||
options.inDensity = originalResolution.width
|
||||
options.inTargetDensity = desiredResolution.width * options.inSampleSize
|
||||
} else {
|
||||
options.inDensity = originalResolution.height
|
||||
options.inTargetDensity = desiredResolution.height * options.inSampleSize
|
||||
}
|
||||
|
||||
return BitmapFactory.decodeByteArray(
|
||||
encodedImage,
|
||||
0,
|
||||
encodedImage.size
|
||||
encodedImage.size,
|
||||
options
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+1
-2
@@ -2,7 +2,6 @@ package io.fotoapparat.result.transformer
|
||||
|
||||
import io.fotoapparat.parameter.Resolution
|
||||
|
||||
|
||||
typealias ResolutionTransformer = (Resolution) -> Resolution
|
||||
|
||||
/**
|
||||
@@ -19,4 +18,4 @@ fun scaled(scaleFactor: Float): ResolutionTransformer = { input ->
|
||||
width = (input.width * scaleFactor).toInt(),
|
||||
height = (input.height * scaleFactor).toInt()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,7 @@ import io.fotoapparat.concurrent.CameraExecutor.Operation
|
||||
import io.fotoapparat.error.CameraErrorCallback
|
||||
import io.fotoapparat.exception.camera.CameraException
|
||||
import io.fotoapparat.hardware.Device
|
||||
import io.fotoapparat.hardware.orientation.Orientation
|
||||
import io.fotoapparat.hardware.orientation.OrientationSensor
|
||||
import io.fotoapparat.hardware.orientation.OrientationState
|
||||
import io.fotoapparat.routine.focus.focusOnPoint
|
||||
import io.fotoapparat.routine.orientation.startOrientationMonitoring
|
||||
import java.io.IOException
|
||||
@@ -23,7 +21,9 @@ internal fun Device.bootStart(
|
||||
}
|
||||
|
||||
try {
|
||||
start()
|
||||
start(
|
||||
orientationSensor = orientationSensor
|
||||
)
|
||||
startOrientationMonitoring(
|
||||
orientationSensor = orientationSensor
|
||||
)
|
||||
@@ -35,7 +35,7 @@ internal fun Device.bootStart(
|
||||
/**
|
||||
* Starts the camera.
|
||||
*/
|
||||
internal fun Device.start() {
|
||||
internal fun Device.start(orientationSensor: OrientationSensor) {
|
||||
selectCamera()
|
||||
|
||||
val cameraDevice = getSelectedCamera().apply {
|
||||
@@ -44,12 +44,7 @@ internal fun Device.start() {
|
||||
updateCameraConfiguration(
|
||||
cameraDevice = this
|
||||
)
|
||||
setDisplayOrientation(
|
||||
orientationState = OrientationState(
|
||||
deviceOrientation = Orientation.Vertical.Portrait,
|
||||
screenOrientation = getScreenOrientation()
|
||||
)
|
||||
)
|
||||
setDisplayOrientation(orientationSensor.lastKnownOrientationState)
|
||||
}
|
||||
|
||||
val previewResolution = cameraDevice.getPreviewResolution()
|
||||
|
||||
@@ -5,7 +5,6 @@ import io.fotoapparat.hardware.Device
|
||||
import io.fotoapparat.hardware.orientation.OrientationSensor
|
||||
import io.fotoapparat.routine.orientation.stopMonitoring
|
||||
|
||||
|
||||
/**
|
||||
* Stops the camera completely.
|
||||
*/
|
||||
@@ -29,4 +28,4 @@ internal fun Device.stop(cameraDevice: CameraDevice) {
|
||||
cameraDevice.close()
|
||||
|
||||
clearSelectedCamera()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import io.fotoapparat.error.CameraErrorCallback
|
||||
import io.fotoapparat.exception.camera.CameraException
|
||||
import io.fotoapparat.hardware.CameraDevice
|
||||
import io.fotoapparat.hardware.Device
|
||||
import io.fotoapparat.hardware.orientation.OrientationSensor
|
||||
import io.fotoapparat.selector.LensPositionSelector
|
||||
|
||||
/**
|
||||
@@ -16,7 +17,8 @@ import io.fotoapparat.selector.LensPositionSelector
|
||||
internal fun Device.switchCamera(
|
||||
newLensPositionSelector: LensPositionSelector,
|
||||
newConfiguration: CameraConfiguration,
|
||||
mainThreadErrorCallback: CameraErrorCallback
|
||||
mainThreadErrorCallback: CameraErrorCallback,
|
||||
orientationSensor: OrientationSensor
|
||||
) {
|
||||
val oldCameraDevice = try {
|
||||
getSelectedCamera()
|
||||
@@ -33,6 +35,7 @@ internal fun Device.switchCamera(
|
||||
|
||||
restartPreview(
|
||||
oldCameraDevice,
|
||||
orientationSensor,
|
||||
mainThreadErrorCallback
|
||||
)
|
||||
}
|
||||
@@ -43,12 +46,13 @@ internal fun Device.switchCamera(
|
||||
*/
|
||||
internal fun Device.restartPreview(
|
||||
oldCameraDevice: CameraDevice,
|
||||
orientationSensor: OrientationSensor,
|
||||
mainThreadErrorCallback: CameraErrorCallback
|
||||
) {
|
||||
stop(oldCameraDevice)
|
||||
|
||||
try {
|
||||
start()
|
||||
start(orientationSensor)
|
||||
} catch (e: CameraException) {
|
||||
mainThreadErrorCallback(e)
|
||||
}
|
||||
|
||||
+2
-3
@@ -3,8 +3,7 @@ package io.fotoapparat.routine.camera
|
||||
import io.fotoapparat.configuration.Configuration
|
||||
import io.fotoapparat.hardware.CameraDevice
|
||||
import io.fotoapparat.hardware.Device
|
||||
import kotlinx.coroutines.experimental.runBlocking
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
/**
|
||||
* Updates [Device] configuration.
|
||||
@@ -33,4 +32,4 @@ internal fun Device.updateCameraConfiguration(
|
||||
cameraDevice.updateFrameProcessor(
|
||||
frameProcessor = frameProcessor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ package io.fotoapparat.routine.capability
|
||||
|
||||
import io.fotoapparat.capability.Capabilities
|
||||
import io.fotoapparat.hardware.Device
|
||||
import kotlinx.coroutines.experimental.runBlocking
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
/**
|
||||
* Returns the camera [Capabilities].
|
||||
|
||||
@@ -3,7 +3,7 @@ package io.fotoapparat.routine.focus
|
||||
import io.fotoapparat.hardware.Device
|
||||
import io.fotoapparat.hardware.metering.FocalRequest
|
||||
import io.fotoapparat.result.FocusResult
|
||||
import kotlinx.coroutines.experimental.runBlocking
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
/**
|
||||
* Focuses the camera on a particular point.
|
||||
@@ -14,4 +14,4 @@ internal fun Device.focusOnPoint(focalRequest: FocalRequest): FocusResult = runB
|
||||
setFocalPoint(focalRequest)
|
||||
autoFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package io.fotoapparat.routine.focus
|
||||
|
||||
import io.fotoapparat.hardware.Device
|
||||
import io.fotoapparat.result.FocusResult
|
||||
import kotlinx.coroutines.experimental.runBlocking
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
/**
|
||||
* Focuses the camera.
|
||||
@@ -11,4 +11,4 @@ internal fun Device.focus(): FocusResult = runBlocking {
|
||||
val cameraDevice = awaitSelectedCamera()
|
||||
|
||||
cameraDevice.autoFocus()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package io.fotoapparat.routine.parameter
|
||||
|
||||
import io.fotoapparat.hardware.Device
|
||||
import io.fotoapparat.parameter.camera.CameraParameters
|
||||
import kotlinx.coroutines.experimental.runBlocking
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
/**
|
||||
* Returns the current [CameraParameters].
|
||||
@@ -11,4 +11,4 @@ internal fun Device.getCurrentParameters(): CameraParameters = runBlocking {
|
||||
val cameraDevice = awaitSelectedCamera()
|
||||
|
||||
cameraDevice.getParameters()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import io.fotoapparat.exception.camera.CameraException
|
||||
import io.fotoapparat.hardware.CameraDevice
|
||||
import io.fotoapparat.hardware.Device
|
||||
import io.fotoapparat.result.Photo
|
||||
import kotlinx.coroutines.experimental.runBlocking
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
/**
|
||||
* Takes a photo.
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package io.fotoapparat.routine.zoom
|
||||
|
||||
import android.support.annotation.FloatRange
|
||||
import androidx.annotation.FloatRange
|
||||
import io.fotoapparat.exception.LevelOutOfRangeException
|
||||
import io.fotoapparat.hardware.Device
|
||||
import io.fotoapparat.parameter.Zoom
|
||||
import kotlinx.coroutines.experimental.runBlocking
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
/**
|
||||
* Updates zoom level of the camera. If zoom is not supported - does nothing.
|
||||
@@ -27,4 +26,4 @@ private fun Float.ensureInBounds() {
|
||||
if (this !in 0f..1f) {
|
||||
throw LevelOutOfRangeException(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package io.fotoapparat.selector
|
||||
|
||||
import io.fotoapparat.parameter.AntiBandingMode
|
||||
|
||||
|
||||
typealias AntiBandingModeSelector = Iterable<AntiBandingMode>.() -> AntiBandingMode?
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.fotoapparat.selector
|
||||
|
||||
import android.support.annotation.FloatRange
|
||||
import androidx.annotation.FloatRange
|
||||
|
||||
/**
|
||||
* @param selector Input selector
|
||||
|
||||
@@ -2,7 +2,6 @@ package io.fotoapparat.selector
|
||||
|
||||
import io.fotoapparat.parameter.FocusMode
|
||||
|
||||
|
||||
typealias FocusModeSelector = Iterable<FocusMode>.() -> FocusMode?
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,6 @@ package io.fotoapparat.selector
|
||||
|
||||
import io.fotoapparat.characteristic.LensPosition
|
||||
|
||||
|
||||
typealias LensPositionSelector = Iterable<LensPosition>.() -> LensPosition?
|
||||
|
||||
/**
|
||||
@@ -21,4 +20,4 @@ fun back(): LensPositionSelector = single(LensPosition.Back)
|
||||
* @return Selector function which provides the external camera if it is available.
|
||||
* Otherwise provides `null`.
|
||||
*/
|
||||
fun external(): LensPositionSelector = single(LensPosition.External)
|
||||
fun external(): LensPositionSelector = single(LensPosition.External)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package io.fotoapparat.selector
|
||||
|
||||
|
||||
/**
|
||||
* @return Selector function which always returns `null`.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package io.fotoapparat.selector
|
||||
|
||||
|
||||
typealias SensorSensitivitySelector = Iterable<Int>.() -> Int?
|
||||
|
||||
/**
|
||||
@@ -18,4 +17,4 @@ fun highestSensorSensitivity(): SensorSensitivitySelector = highest()
|
||||
/**
|
||||
* @return Selector function which selects lowest ISO value.
|
||||
*/
|
||||
fun lowestSensorSensitivity(): SensorSensitivitySelector = lowest()
|
||||
fun lowestSensorSensitivity(): SensorSensitivitySelector = lowest()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package io.fotoapparat.util
|
||||
|
||||
|
||||
/**
|
||||
* System line separator.
|
||||
*/
|
||||
|
||||
@@ -2,4 +2,15 @@ package io.fotoapparat.util
|
||||
|
||||
import io.fotoapparat.preview.Frame
|
||||
|
||||
typealias FrameProcessor = (Frame) -> Unit
|
||||
/**
|
||||
* Performs processing on preview frames.
|
||||
*
|
||||
* This function is called from worker thread (aka non-UI thread). After
|
||||
* the function completes the frame is returned back to the pool where it is reused
|
||||
* afterwards. Thus, implementations should take special care to not do any operations on
|
||||
* frame after method completes.
|
||||
*
|
||||
* @param frame frame of the preview. Do not cache it as it will eventually be reused by the
|
||||
* camera.
|
||||
*/
|
||||
typealias FrameProcessor = (frame: Frame) -> Unit
|
||||
|
||||
@@ -12,7 +12,6 @@ import io.fotoapparat.parameter.Resolution
|
||||
import io.fotoapparat.parameter.ScaleType
|
||||
import java.util.concurrent.CountDownLatch
|
||||
|
||||
|
||||
/**
|
||||
* Uses [android.view.TextureView] as an output for camera.
|
||||
*/
|
||||
@@ -83,6 +82,7 @@ private fun ViewGroup.layoutTextureView(
|
||||
) = when (scaleType) {
|
||||
ScaleType.CenterInside -> previewResolution?.centerInside(this)
|
||||
ScaleType.CenterCrop -> previewResolution?.centerCrop(this)
|
||||
ScaleType.TopCrop -> previewResolution?.topCrop(this)
|
||||
else -> null
|
||||
}
|
||||
|
||||
@@ -130,6 +130,28 @@ private fun Resolution.centerCrop(view: ViewGroup) {
|
||||
view.layoutChildrenAt(rect)
|
||||
}
|
||||
|
||||
private fun Resolution.topCrop(view: ViewGroup) {
|
||||
val scale = Math.max(
|
||||
view.measuredWidth / width.toFloat(),
|
||||
view.measuredHeight / height.toFloat()
|
||||
)
|
||||
|
||||
val width = (width * scale).toInt()
|
||||
val height = (height * scale).toInt()
|
||||
|
||||
val extraX = Math.max(0, width - view.measuredWidth)
|
||||
|
||||
val rect = Rect(
|
||||
-extraX / 2,
|
||||
0,
|
||||
width - extraX / 2,
|
||||
height
|
||||
)
|
||||
|
||||
view.layoutChildrenAt(rect)
|
||||
}
|
||||
|
||||
|
||||
private fun ViewGroup.layoutChildrenAt(rect: Rect) {
|
||||
(0 until childCount).forEach {
|
||||
getChildAt(it).layout(
|
||||
|
||||
@@ -3,15 +3,14 @@ package io.fotoapparat.view
|
||||
import android.animation.AnimatorInflater.loadAnimator
|
||||
import android.animation.AnimatorSet
|
||||
import android.content.Context
|
||||
import android.support.annotation.AnimatorRes
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import androidx.annotation.AnimatorRes
|
||||
import io.fotoapparat.R
|
||||
|
||||
|
||||
/**
|
||||
* A circle which gives feedback to the user about a click.
|
||||
*/
|
||||
@@ -81,4 +80,4 @@ internal class FeedbackCircleView
|
||||
private fun newAnimator(@AnimatorRes id: Int, target: View) = loadAnimator(context, id).apply {
|
||||
setTarget(target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ package io.fotoapparat.view
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.GestureDetector
|
||||
import android.view.MotionEvent
|
||||
import android.view.ScaleGestureDetector
|
||||
import android.widget.FrameLayout
|
||||
import io.fotoapparat.hardware.metering.FocalRequest
|
||||
import io.fotoapparat.hardware.metering.PointF
|
||||
@@ -14,7 +16,7 @@ import io.fotoapparat.parameter.Resolution
|
||||
*
|
||||
* If the camera doesn't support focus metering on specific area this will only display a visual feedback.
|
||||
*/
|
||||
class FocusView
|
||||
open class FocusView
|
||||
@JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
@@ -24,6 +26,15 @@ class FocusView
|
||||
private val visualFeedbackCircle = FeedbackCircleView(context, attrs, defStyleAttr)
|
||||
private var focusMeteringListener: ((FocalRequest) -> Unit)? = null
|
||||
|
||||
var scaleListener: ((Float) -> Unit)? = null
|
||||
var ptrListener: ((Int) -> Unit)? = null
|
||||
|
||||
private var mPtrCount: Int = 0
|
||||
set(value) {
|
||||
field = value
|
||||
ptrListener?.invoke(value)
|
||||
}
|
||||
|
||||
init {
|
||||
clipToPadding = false
|
||||
clipChildren = false
|
||||
@@ -36,27 +47,50 @@ class FocusView
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
||||
if (event.action == MotionEvent.ACTION_DOWN) {
|
||||
focusMeteringListener?.let {
|
||||
it(FocalRequest(
|
||||
point = PointF(
|
||||
x = event.x,
|
||||
y = event.y
|
||||
),
|
||||
previewResolution = Resolution(
|
||||
width = width,
|
||||
height = height
|
||||
)
|
||||
))
|
||||
visualFeedbackCircle.showAt(
|
||||
x = event.x - visualFeedbackCircle.width / 2,
|
||||
y = event.y - visualFeedbackCircle.height / 2
|
||||
)
|
||||
performClick()
|
||||
return true
|
||||
}
|
||||
tapDetector.onTouchEvent(event)
|
||||
scaleDetector.onTouchEvent(event)
|
||||
|
||||
when (event.action and MotionEvent.ACTION_MASK) {
|
||||
MotionEvent.ACTION_POINTER_DOWN -> mPtrCount++
|
||||
MotionEvent.ACTION_POINTER_UP -> mPtrCount--
|
||||
MotionEvent.ACTION_DOWN -> mPtrCount++
|
||||
MotionEvent.ACTION_UP -> mPtrCount--
|
||||
}
|
||||
return super.onTouchEvent(event)
|
||||
return true
|
||||
}
|
||||
|
||||
private val gestureDetectorListener = object : GestureDetector.SimpleOnGestureListener() {
|
||||
override fun onSingleTapUp(e: MotionEvent): Boolean {
|
||||
return focusMeteringListener
|
||||
?.let {
|
||||
it(FocalRequest(
|
||||
point = PointF(
|
||||
x = e.x,
|
||||
y = e.y),
|
||||
previewResolution = Resolution(
|
||||
width = width,
|
||||
height = height)))
|
||||
visualFeedbackCircle.showAt(
|
||||
x = e.x - visualFeedbackCircle.width / 2,
|
||||
y = e.y - visualFeedbackCircle.height / 2)
|
||||
performClick()
|
||||
|
||||
true
|
||||
}
|
||||
?: super.onSingleTapUp(e)
|
||||
}
|
||||
}
|
||||
|
||||
private val tapDetector = GestureDetector(context, gestureDetectorListener)
|
||||
|
||||
private val scaleGestureDetector = object : ScaleGestureDetector.SimpleOnScaleGestureListener() {
|
||||
override fun onScale(detector: ScaleGestureDetector): Boolean {
|
||||
return scaleListener
|
||||
?.let {
|
||||
it(detector.scaleFactor)
|
||||
true
|
||||
}?: super.onScale(detector)
|
||||
}
|
||||
}
|
||||
private val scaleDetector = ScaleGestureDetector(context, scaleGestureDetector)
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<resources>
|
||||
<string name="app_name">Fotoapparat</string>
|
||||
</resources>
|
||||
@@ -237,12 +237,12 @@ class FotoapparatBuilderTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `frameProcessor empty by default`() {
|
||||
fun `frameProcessor null by default`() {
|
||||
// When
|
||||
val builder = builderWithMandatoryArguments()
|
||||
|
||||
// Then
|
||||
assertNotNull(builder.configuration.frameProcessor)
|
||||
assertNull(builder.configuration.frameProcessor)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+3
-1
@@ -2,6 +2,7 @@ package io.fotoapparat.hardware.orientation
|
||||
|
||||
import io.fotoapparat.hardware.Device
|
||||
import io.fotoapparat.hardware.orientation.Orientation.Horizontal.Landscape
|
||||
import io.fotoapparat.hardware.orientation.Orientation.Vertical.Portrait
|
||||
import io.fotoapparat.hardware.orientation.Orientation.Vertical.ReversePortrait
|
||||
import io.fotoapparat.test.willReturn
|
||||
import org.junit.Before
|
||||
@@ -25,6 +26,7 @@ internal class OrientationSensorTest {
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
device.getScreenOrientation() willReturn Portrait
|
||||
testee = OrientationSensor(
|
||||
rotationListener,
|
||||
device
|
||||
@@ -36,7 +38,7 @@ internal class OrientationSensorTest {
|
||||
// Given
|
||||
|
||||
// When
|
||||
testee.start({})
|
||||
testee.start {}
|
||||
testee.stop()
|
||||
|
||||
// Then
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package io.fotoapparat.log
|
||||
|
||||
import org.apache.commons.io.FileUtils.readFileToString
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
import java.nio.charset.Charset
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
internal class FileLoggerTest {
|
||||
@@ -29,7 +27,7 @@ internal class FileLoggerTest {
|
||||
// Then
|
||||
assertEquals(
|
||||
"message\nmessage\n",
|
||||
readFileToString(file, Charset.defaultCharset())
|
||||
file.readText()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+1
-2
@@ -11,7 +11,6 @@ import io.fotoapparat.selector.single
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
|
||||
internal class CameraParametersProviderTest {
|
||||
|
||||
val resolution = Resolution(10, 10)
|
||||
@@ -385,4 +384,4 @@ internal class CameraParametersProviderTest {
|
||||
// Then
|
||||
// throw exception
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,7 @@ import android.graphics.SurfaceTexture
|
||||
import io.fotoapparat.exception.camera.CameraException
|
||||
import io.fotoapparat.hardware.CameraDevice
|
||||
import io.fotoapparat.hardware.Device
|
||||
import io.fotoapparat.hardware.orientation.Orientation.Horizontal.Landscape
|
||||
import io.fotoapparat.hardware.orientation.Orientation.Vertical.Portrait
|
||||
import io.fotoapparat.hardware.orientation.OrientationSensor
|
||||
import io.fotoapparat.hardware.orientation.OrientationState
|
||||
import io.fotoapparat.log.Logger
|
||||
import io.fotoapparat.parameter.ScaleType
|
||||
import io.fotoapparat.test.testResolution
|
||||
@@ -27,7 +24,6 @@ import java.util.concurrent.atomic.AtomicBoolean
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
|
||||
@RunWith(MockitoJUnitRunner::class)
|
||||
internal class StartRoutineTest {
|
||||
|
||||
@@ -56,7 +52,6 @@ internal class StartRoutineTest {
|
||||
// Given
|
||||
device.apply {
|
||||
getSelectedCamera() willReturn cameraDevice
|
||||
getScreenOrientation() willReturn Landscape
|
||||
cameraRenderer willReturn cameraViewRenderer
|
||||
scaleType willReturn ScaleType.CenterCrop
|
||||
}
|
||||
@@ -64,7 +59,7 @@ internal class StartRoutineTest {
|
||||
cameraViewRenderer.getPreview() willReturn preview
|
||||
|
||||
// When
|
||||
device.start()
|
||||
device.start(orientationSensor)
|
||||
|
||||
// Then
|
||||
val inOrder = inOrder(
|
||||
@@ -77,10 +72,7 @@ internal class StartRoutineTest {
|
||||
verify(device).selectCamera()
|
||||
verify(cameraDevice).open()
|
||||
verify(device).updateCameraConfiguration(cameraDevice)
|
||||
verify(cameraDevice).setDisplayOrientation(OrientationState(
|
||||
Portrait,
|
||||
Landscape
|
||||
))
|
||||
verify(cameraDevice).setDisplayOrientation(orientationSensor.lastKnownOrientationState)
|
||||
verify(cameraViewRenderer).setScaleType(ScaleType.CenterCrop)
|
||||
verify(cameraViewRenderer).setPreviewResolution(testResolution)
|
||||
verify(cameraDevice).setDisplaySurface(preview)
|
||||
@@ -93,7 +85,6 @@ internal class StartRoutineTest {
|
||||
// Given
|
||||
device.apply {
|
||||
getSelectedCamera() willReturn cameraDevice
|
||||
getScreenOrientation() willReturn Landscape
|
||||
cameraRenderer willReturn cameraViewRenderer
|
||||
scaleType willReturn ScaleType.CenterCrop
|
||||
logger willReturn mockLogger
|
||||
@@ -103,7 +94,7 @@ internal class StartRoutineTest {
|
||||
cameraViewRenderer.getPreview() willReturn preview
|
||||
|
||||
// When
|
||||
device.start()
|
||||
device.start(orientationSensor)
|
||||
|
||||
// Then
|
||||
verify(cameraDevice, never()).startPreview()
|
||||
@@ -116,8 +107,8 @@ internal class StartRoutineTest {
|
||||
|
||||
// When
|
||||
device.bootStart(
|
||||
orientationSensor,
|
||||
{}
|
||||
orientationSensor = orientationSensor,
|
||||
mainThreadErrorCallback = {}
|
||||
)
|
||||
|
||||
// Then
|
||||
@@ -132,8 +123,8 @@ internal class StartRoutineTest {
|
||||
|
||||
// When
|
||||
device.bootStart(
|
||||
orientationSensor,
|
||||
{ hasErrors.set(true) }
|
||||
orientationSensor = orientationSensor,
|
||||
mainThreadErrorCallback = { hasErrors.set(true) }
|
||||
)
|
||||
|
||||
// Then
|
||||
@@ -148,7 +139,6 @@ internal class StartRoutineTest {
|
||||
device.apply {
|
||||
hasSelectedCamera() willReturn false
|
||||
getSelectedCamera() willReturn cameraDevice
|
||||
getScreenOrientation() willReturn Landscape
|
||||
cameraRenderer willReturn cameraViewRenderer
|
||||
scaleType willReturn ScaleType.CenterCrop
|
||||
}
|
||||
@@ -157,14 +147,14 @@ internal class StartRoutineTest {
|
||||
|
||||
// When
|
||||
device.bootStart(
|
||||
orientationSensor,
|
||||
{ hasErrors = true }
|
||||
orientationSensor = orientationSensor,
|
||||
mainThreadErrorCallback = { hasErrors = true }
|
||||
)
|
||||
|
||||
// Then
|
||||
assertFalse(hasErrors)
|
||||
|
||||
verify(device).start()
|
||||
verify(device).start(orientationSensor)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+16
-15
@@ -4,11 +4,10 @@ import io.fotoapparat.characteristic.LensPosition
|
||||
import io.fotoapparat.error.CameraErrorCallback
|
||||
import io.fotoapparat.hardware.CameraDevice
|
||||
import io.fotoapparat.hardware.Device
|
||||
import io.fotoapparat.hardware.orientation.Orientation.Horizontal.Landscape
|
||||
import io.fotoapparat.hardware.orientation.OrientationSensor
|
||||
import io.fotoapparat.test.testConfiguration
|
||||
import io.fotoapparat.test.willReturn
|
||||
import io.fotoapparat.view.CameraRenderer
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.BDDMockito.given
|
||||
@@ -27,14 +26,11 @@ internal class SwitchCameraRoutineTest {
|
||||
lateinit var device: Device
|
||||
@Mock
|
||||
lateinit var cameraRenderer: CameraRenderer
|
||||
@Mock
|
||||
lateinit var orientationSensor: OrientationSensor
|
||||
|
||||
private val mainThreadErrorCallback: CameraErrorCallback = {}
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
device.getScreenOrientation() willReturn Landscape
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Switch camera, not started`() {
|
||||
// Given
|
||||
@@ -48,13 +44,14 @@ internal class SwitchCameraRoutineTest {
|
||||
device.switchCamera(
|
||||
newLensPositionSelector = lensPositionSelector,
|
||||
newConfiguration = testConfiguration,
|
||||
mainThreadErrorCallback = mainThreadErrorCallback
|
||||
mainThreadErrorCallback = mainThreadErrorCallback,
|
||||
orientationSensor = orientationSensor
|
||||
)
|
||||
|
||||
// Then
|
||||
verify(device).updateLensPositionSelector(lensPositionSelector)
|
||||
verify(device).updateConfiguration(testConfiguration)
|
||||
verify(device, never()).restartPreview(oldCameraDevice, mainThreadErrorCallback)
|
||||
verify(device, never()).restartPreview(oldCameraDevice, orientationSensor, mainThreadErrorCallback)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -69,7 +66,8 @@ internal class SwitchCameraRoutineTest {
|
||||
device.switchCamera(
|
||||
newLensPositionSelector = lensPositionSelector,
|
||||
newConfiguration = testConfiguration,
|
||||
mainThreadErrorCallback = mainThreadErrorCallback
|
||||
mainThreadErrorCallback = mainThreadErrorCallback,
|
||||
orientationSensor = orientationSensor
|
||||
)
|
||||
|
||||
// Then
|
||||
@@ -80,7 +78,7 @@ internal class SwitchCameraRoutineTest {
|
||||
inOrder.apply {
|
||||
verify(device, never()).updateLensPositionSelector(lensPositionSelector)
|
||||
verify(device, never()).updateConfiguration(testConfiguration)
|
||||
verify(device, never()).restartPreview(oldCameraDevice, mainThreadErrorCallback)
|
||||
verify(device, never()).restartPreview(oldCameraDevice, orientationSensor, mainThreadErrorCallback)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +94,8 @@ internal class SwitchCameraRoutineTest {
|
||||
device.switchCamera(
|
||||
newLensPositionSelector = lensPositionSelector,
|
||||
newConfiguration = testConfiguration,
|
||||
mainThreadErrorCallback = mainThreadErrorCallback
|
||||
mainThreadErrorCallback = mainThreadErrorCallback,
|
||||
orientationSensor = orientationSensor
|
||||
)
|
||||
|
||||
// Then
|
||||
@@ -107,7 +106,7 @@ internal class SwitchCameraRoutineTest {
|
||||
inOrder.apply {
|
||||
verify(device).updateLensPositionSelector(lensPositionSelector)
|
||||
verify(device).updateConfiguration(testConfiguration)
|
||||
verify(device).restartPreview(oldCameraDevice, mainThreadErrorCallback)
|
||||
verify(device).restartPreview(oldCameraDevice, orientationSensor, mainThreadErrorCallback)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,6 +119,7 @@ internal class SwitchCameraRoutineTest {
|
||||
// When
|
||||
device.restartPreview(
|
||||
oldCameraDevice = oldCameraDevice,
|
||||
orientationSensor = orientationSensor,
|
||||
mainThreadErrorCallback = mainThreadErrorCallback
|
||||
)
|
||||
|
||||
@@ -128,7 +128,7 @@ internal class SwitchCameraRoutineTest {
|
||||
|
||||
inOrder.apply {
|
||||
verify(device).stop(oldCameraDevice)
|
||||
verify(device).start()
|
||||
verify(device).start(orientationSensor)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,6 +141,7 @@ internal class SwitchCameraRoutineTest {
|
||||
// When
|
||||
device.restartPreview(
|
||||
oldCameraDevice = oldCameraDevice,
|
||||
orientationSensor = orientationSensor,
|
||||
mainThreadErrorCallback = mainThreadErrorCallback
|
||||
)
|
||||
|
||||
@@ -148,7 +149,7 @@ internal class SwitchCameraRoutineTest {
|
||||
val inOrder = inOrder(device)
|
||||
inOrder.apply {
|
||||
verify(device).stop(oldCameraDevice)
|
||||
verify(device).start()
|
||||
verify(device).start(orientationSensor)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ import io.fotoapparat.hardware.Device
|
||||
import io.fotoapparat.test.testCameraParameters
|
||||
import io.fotoapparat.test.testFrameProcessor
|
||||
import io.fotoapparat.test.willReturn
|
||||
import kotlinx.coroutines.experimental.runBlocking
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
@@ -59,4 +59,4 @@ internal class UpdateCameraConfigurationRoutineTest {
|
||||
inOrder.verify(cameraDevice).updateParameters(testCameraParameters)
|
||||
inOrder.verify(cameraDevice).updateFrameProcessor(testFrameProcessor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@ import io.fotoapparat.hardware.CameraDevice
|
||||
import io.fotoapparat.hardware.Device
|
||||
import io.fotoapparat.test.testCapabilities
|
||||
import io.fotoapparat.test.willReturn
|
||||
import kotlinx.coroutines.experimental.runBlocking
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
@@ -35,4 +35,4 @@ internal class GetCapabilitiesRoutineTest {
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import io.fotoapparat.hardware.metering.PointF
|
||||
import io.fotoapparat.parameter.Resolution
|
||||
import io.fotoapparat.result.FocusResult
|
||||
import io.fotoapparat.test.willReturn
|
||||
import kotlinx.coroutines.experimental.runBlocking
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
@@ -70,4 +70,4 @@ internal class FocusRoutineTest {
|
||||
actual = focusResult
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@ import io.fotoapparat.hardware.CameraDevice
|
||||
import io.fotoapparat.hardware.Device
|
||||
import io.fotoapparat.test.testCameraParameters
|
||||
import io.fotoapparat.test.willReturn
|
||||
import kotlinx.coroutines.experimental.runBlocking
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
@@ -34,4 +34,4 @@ internal class GetParametersRoutineTest {
|
||||
actual = currentParameters
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import io.fotoapparat.hardware.Device
|
||||
import io.fotoapparat.result.Photo
|
||||
import io.fotoapparat.test.willReturn
|
||||
import io.fotoapparat.test.willThrow
|
||||
import kotlinx.coroutines.experimental.runBlocking
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
@@ -68,4 +68,4 @@ internal class TakePhotoRoutineTest {
|
||||
actual = result
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import io.fotoapparat.hardware.Device
|
||||
import io.fotoapparat.parameter.Zoom
|
||||
import io.fotoapparat.test.testCapabilities
|
||||
import io.fotoapparat.test.willReturn
|
||||
import kotlinx.coroutines.experimental.runBlocking
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentMatchers.anyFloat
|
||||
@@ -46,7 +46,7 @@ internal class UpdateZoomLevelRoutineTest {
|
||||
// Given
|
||||
device.awaitSelectedCamera() willReturn cameraDevice
|
||||
cameraDevice.getCapabilities() willReturn testCapabilities.copy(
|
||||
zoom = Zoom.VariableZoom(3)
|
||||
zoom = Zoom.VariableZoom(3, listOf(100, 150, 200, 250))
|
||||
)
|
||||
|
||||
// When
|
||||
@@ -71,4 +71,4 @@ internal class UpdateZoomLevelRoutineTest {
|
||||
verify(cameraDevice, never()).setZoom(anyFloat())
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ internal val testConfiguration = CameraConfiguration(
|
||||
* Test object for [Capabilities].
|
||||
*/
|
||||
val testCapabilities = Capabilities(
|
||||
zoom = Zoom.VariableZoom(3),
|
||||
zoom = Zoom.VariableZoom(3, listOf(100, 150, 200, 250)),
|
||||
flashModes = setOf(Flash.AutoRedEye),
|
||||
focusModes = setOf(FocusMode.Fixed),
|
||||
canSmoothZoom = false,
|
||||
|
||||
@@ -2,3 +2,5 @@
|
||||
org.gradle.daemon=true
|
||||
org.gradle.parallel=true
|
||||
org.gradle.jvmargs=-Xmx1536m
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+1
-2
@@ -1,6 +1,5 @@
|
||||
#Mon Dec 04 00:49:03 CET 2017
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
@@ -6,42 +6,6 @@
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
esac
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
@@ -60,6 +24,46 @@ cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
@@ -85,7 +89,7 @@ location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
@@ -150,11 +154,19 @@ if $cygwin ; then
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
||||
Vendored
+4
-10
@@ -8,14 +8,14 @@
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
@@ -46,10 +46,9 @@ echo location of your Java installation.
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windowz variants
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
@@ -60,11 +59,6 @@ set _SKIP=2
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
+1
-2
@@ -25,6 +25,5 @@ android {
|
||||
dependencies {
|
||||
implementation project(':fotoapparat')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:${versions.kotlin}"
|
||||
|
||||
implementation "com.android.support:appcompat-v7:${versions.android.support}"
|
||||
implementation "androidx.appcompat:appcompat:${versions.android.appcompat}"
|
||||
}
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="io.fotoapparat.sample"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="io.fotoapparat.sample">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
android:theme="@style/AppTheme"
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
<!--Set android:name to ".ActivityJava" for java example-->
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:screenOrientation="fullSensor">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
@@ -1,27 +1,30 @@
|
||||
package io.fotoapparat.sample;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.SwitchCompat;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Locale;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.SwitchCompat;
|
||||
import io.fotoapparat.Fotoapparat;
|
||||
import io.fotoapparat.capability.Capabilities;
|
||||
import io.fotoapparat.configuration.CameraConfiguration;
|
||||
import io.fotoapparat.configuration.UpdateConfiguration;
|
||||
import io.fotoapparat.error.CameraErrorListener;
|
||||
import io.fotoapparat.exception.camera.CameraException;
|
||||
import io.fotoapparat.parameter.ScaleType;
|
||||
import io.fotoapparat.parameter.Zoom;
|
||||
import io.fotoapparat.preview.Frame;
|
||||
import io.fotoapparat.preview.FrameProcessor;
|
||||
import io.fotoapparat.result.BitmapPhoto;
|
||||
@@ -29,6 +32,8 @@ import io.fotoapparat.result.PhotoResult;
|
||||
import io.fotoapparat.result.WhenDoneListener;
|
||||
import io.fotoapparat.view.CameraView;
|
||||
import io.fotoapparat.view.FocusView;
|
||||
import kotlin.Unit;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
|
||||
import static io.fotoapparat.log.LoggersKt.fileLogger;
|
||||
import static io.fotoapparat.log.LoggersKt.logcat;
|
||||
@@ -57,9 +62,13 @@ public class ActivityJava extends AppCompatActivity {
|
||||
private boolean hasCameraPermission;
|
||||
private CameraView cameraView;
|
||||
private FocusView focusView;
|
||||
private TextView zoomLvl;
|
||||
private ImageView switchCamera;
|
||||
private View capture;
|
||||
|
||||
private Fotoapparat fotoapparat;
|
||||
private Zoom.VariableZoom cameraZoom;
|
||||
private float curZoom = 0f;
|
||||
|
||||
boolean activeCameraBack = true;
|
||||
|
||||
@@ -92,6 +101,8 @@ public class ActivityJava extends AppCompatActivity {
|
||||
cameraView = findViewById(R.id.cameraView);
|
||||
focusView = findViewById(R.id.focusView);
|
||||
capture = findViewById(R.id.capture);
|
||||
zoomLvl = findViewById(R.id.zoomLvl);
|
||||
switchCamera = findViewById(R.id.switchCamera);
|
||||
hasCameraPermission = permissionsDelegate.hasCameraPermission();
|
||||
|
||||
if (hasCameraPermission) {
|
||||
@@ -105,7 +116,6 @@ public class ActivityJava extends AppCompatActivity {
|
||||
takePictureOnClick();
|
||||
switchCameraOnClick();
|
||||
toggleTorchOnSwitch();
|
||||
zoomSeekBar();
|
||||
}
|
||||
|
||||
private Fotoapparat createFotoapparat() {
|
||||
@@ -129,15 +139,66 @@ public class ActivityJava extends AppCompatActivity {
|
||||
.build();
|
||||
}
|
||||
|
||||
private void zoomSeekBar() {
|
||||
SeekBar seekBar = findViewById(R.id.zoomSeekBar);
|
||||
|
||||
seekBar.setOnSeekBarChangeListener(new OnProgressChanged() {
|
||||
private void adjustViewsVisibility() {
|
||||
fotoapparat.getCapabilities().whenAvailable(new Function1<Capabilities, Unit>() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
fotoapparat.setZoom(progress / (float) seekBar.getMax());
|
||||
public Unit invoke(Capabilities capabilities) {
|
||||
Zoom zoom = capabilities != null ? capabilities.getZoom() : null;
|
||||
if(zoom instanceof Zoom.VariableZoom){
|
||||
cameraZoom = (Zoom.VariableZoom) zoom;
|
||||
focusView.setScaleListener(new Function1<Float, Unit>() {
|
||||
@Override
|
||||
public Unit invoke(Float aFloat) {
|
||||
scaleZoom(aFloat);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
focusView.setPtrListener(new Function1<Integer, Unit>() {
|
||||
@Override
|
||||
public Unit invoke(Integer integer) {
|
||||
pointerChanged(integer);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
zoomLvl.setVisibility(View.GONE);
|
||||
focusView.setScaleListener(null);
|
||||
focusView.setPtrListener(null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
if (fotoapparat.isAvailable(front())){
|
||||
switchCamera.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
switchCamera.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void scaleZoom(float scaleFactor){
|
||||
float plusZoom = 0;
|
||||
if (scaleFactor < 1) plusZoom = -1 * (1 - scaleFactor);
|
||||
else plusZoom = scaleFactor - 1;
|
||||
|
||||
float newZoom = curZoom + plusZoom;
|
||||
if (newZoom < 0 || newZoom > 1) return;
|
||||
|
||||
curZoom = newZoom;
|
||||
fotoapparat.setZoom(curZoom);
|
||||
|
||||
int progress = Math.round (cameraZoom.getMaxZoom() * curZoom);
|
||||
int value = cameraZoom.getZoomRatios().get(progress);
|
||||
|
||||
float roundedValue = (float)(Math.round(((float)value) / 10f)) / 10f;
|
||||
|
||||
zoomLvl.setVisibility(View.VISIBLE);
|
||||
zoomLvl.setText(String.format(Locale.getDefault(), "%.1f×", roundedValue));
|
||||
}
|
||||
|
||||
private void pointerChanged(int fingerCount){
|
||||
if(fingerCount == 0) {
|
||||
zoomLvl.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void switchCameraOnClick() {
|
||||
@@ -180,6 +241,7 @@ public class ActivityJava extends AppCompatActivity {
|
||||
activeCameraBack ? back() : front(),
|
||||
cameraConfiguration
|
||||
);
|
||||
adjustViewsVisibility();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -223,6 +285,7 @@ public class ActivityJava extends AppCompatActivity {
|
||||
super.onStart();
|
||||
if (hasCameraPermission) {
|
||||
fotoapparat.start();
|
||||
adjustViewsVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,6 +305,7 @@ public class ActivityJava extends AppCompatActivity {
|
||||
if (permissionsDelegate.resultGranted(requestCode, permissions, grantResults)) {
|
||||
hasCameraPermission = true;
|
||||
fotoapparat.start();
|
||||
adjustViewsVisibility();
|
||||
cameraView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
package io.fotoapparat.sample
|
||||
|
||||
import android.os.Bundle
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.CompoundButton
|
||||
import android.widget.ImageView
|
||||
import android.widget.SeekBar
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import io.fotoapparat.Fotoapparat
|
||||
import io.fotoapparat.configuration.CameraConfiguration
|
||||
import io.fotoapparat.configuration.UpdateConfiguration
|
||||
@@ -17,7 +16,7 @@ import io.fotoapparat.result.transformer.scaled
|
||||
import io.fotoapparat.selector.*
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
import java.io.File
|
||||
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
@@ -27,7 +26,9 @@ class MainActivity : AppCompatActivity() {
|
||||
private var activeCamera: Camera = Camera.Back
|
||||
|
||||
private lateinit var fotoapparat: Fotoapparat
|
||||
private lateinit var cameraZoom: Zoom.VariableZoom
|
||||
|
||||
private var curZoom: Float = 0f
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -52,15 +53,10 @@ class MainActivity : AppCompatActivity() {
|
||||
)
|
||||
|
||||
capture onClick takePicture()
|
||||
zoomSeekBar onProgressChanged updateZoom()
|
||||
switchCamera onClick changeCamera()
|
||||
torchSwitch onCheckedChanged toggleFlash()
|
||||
}
|
||||
|
||||
private fun updateZoom(): (SeekBar, Int) -> Unit = { seekBar: SeekBar, progress: Int ->
|
||||
fotoapparat.setZoom(progress / seekBar.max.toFloat())
|
||||
}
|
||||
|
||||
private fun takePicture(): () -> Unit = {
|
||||
val photoResult = fotoapparat
|
||||
.autoFocus()
|
||||
@@ -101,7 +97,6 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
adjustViewsVisibility()
|
||||
|
||||
zoomSeekBar.progress = 0
|
||||
torchSwitch.isChecked = false
|
||||
|
||||
Log.i(LOGGING_TAG, "New camera position: ${if (activeCamera is Camera.Back) "back" else "front"}")
|
||||
@@ -154,7 +149,18 @@ class MainActivity : AppCompatActivity() {
|
||||
.whenAvailable { capabilities ->
|
||||
capabilities
|
||||
?.let {
|
||||
zoomSeekBar.visibility = if (it.zoom is Zoom.VariableZoom) View.VISIBLE else View.GONE
|
||||
(it.zoom as? Zoom.VariableZoom)
|
||||
?.let {
|
||||
cameraZoom = it
|
||||
focusView.scaleListener = this::scaleZoom
|
||||
focusView.ptrListener = this::pointerChanged
|
||||
}
|
||||
?: run {
|
||||
zoomLvl?.visibility = View.GONE
|
||||
focusView.scaleListener = null
|
||||
focusView.ptrListener = null
|
||||
}
|
||||
|
||||
torchSwitch.visibility = if (it.flashModes.contains(Flash.Torch)) View.VISIBLE else View.GONE
|
||||
}
|
||||
?: Log.e(LOGGING_TAG, "Couldn't obtain capabilities.")
|
||||
@@ -163,6 +169,28 @@ class MainActivity : AppCompatActivity() {
|
||||
switchCamera.visibility = if (fotoapparat.isAvailable(front())) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
//When zooming slowly, the values are approximately 0.9 ~ 1.1
|
||||
private fun scaleZoom(scaleFactor: Float) {
|
||||
//convert to -0.1 ~ 0.1
|
||||
val plusZoom = if (scaleFactor < 1) -1 * (1 - scaleFactor) else scaleFactor - 1
|
||||
val newZoom = curZoom + plusZoom
|
||||
if (newZoom < 0 || newZoom > 1) return
|
||||
|
||||
curZoom = newZoom
|
||||
fotoapparat.setZoom(curZoom)
|
||||
val progress = (cameraZoom.maxZoom * curZoom).roundToInt()
|
||||
val value = cameraZoom.zoomRatios[progress]
|
||||
val roundedValue = ((value.toFloat()) / 10).roundToInt().toFloat() / 10
|
||||
|
||||
zoomLvl.visibility = View.VISIBLE
|
||||
zoomLvl.text = String.format("%.1f×", roundedValue)
|
||||
}
|
||||
|
||||
private fun pointerChanged(fingerCount: Int){
|
||||
if(fingerCount == 0) {
|
||||
zoomLvl?.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val LOGGING_TAG = "Fotoapparat Example"
|
||||
|
||||
@@ -3,10 +3,11 @@ package io.fotoapparat.sample;
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
class PermissionsDelegate {
|
||||
|
||||
private static final int REQUEST_CODE = 10;
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package io.fotoapparat.sample
|
||||
|
||||
import android.support.v7.widget.SwitchCompat
|
||||
import android.view.View
|
||||
import android.widget.CompoundButton
|
||||
import android.widget.SeekBar
|
||||
|
||||
import androidx.appcompat.widget.SwitchCompat
|
||||
|
||||
internal infix fun SwitchCompat.onCheckedChanged(function: (CompoundButton, Boolean) -> Unit) {
|
||||
setOnCheckedChangeListener(function)
|
||||
@@ -14,10 +13,10 @@ internal infix fun View.onClick(function: () -> Unit) {
|
||||
setOnClickListener { function() }
|
||||
}
|
||||
|
||||
internal infix fun SeekBar.onProgressChanged(updateZoom: (SeekBar, Int) -> Unit) {
|
||||
internal infix fun SeekBar.onProgressChanged(zoomUpdated: () -> Unit) {
|
||||
setOnSeekBarChangeListener(object : OnProgressChanged() {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
updateZoom(seekBar, progress)
|
||||
zoomUpdated()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.v7.widget.FitWindowsFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.appcompat.widget.FitWindowsFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@@ -35,7 +35,6 @@
|
||||
tools:ignore="HardcodedText"
|
||||
tools:visibility="visible" />
|
||||
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@@ -46,7 +45,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top">
|
||||
|
||||
<android.support.v7.widget.SwitchCompat
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/torchSwitch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -54,13 +53,6 @@
|
||||
android:padding="20dp"
|
||||
tools:ignore="RtlHardcoded" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/zoomSeekBar"
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:max="30" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/switchCamera"
|
||||
android:layout_width="wrap_content"
|
||||
@@ -72,6 +64,15 @@
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/zoomLvl"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:textColor="#FFF"
|
||||
android:textSize="20sp"
|
||||
tools:text="2.4" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/result"
|
||||
android:layout_width="150dp"
|
||||
@@ -89,4 +90,4 @@
|
||||
tools:ignore="ContentDescription" />
|
||||
</FrameLayout>
|
||||
|
||||
</android.support.v7.widget.FitWindowsFrameLayout>
|
||||
</androidx.appcompat.widget.FitWindowsFrameLayout>
|
||||
|
||||
Reference in New Issue
Block a user