Devops: KTLint to lint Kotlin code (#6)
* Adds KTLint as a GitHub action * Adds KTLint to the gradle project for IDE integration * Adds .editorconfig to configure KTLint (android/)
This commit is contained in:
parent
2e60110070
commit
03b9246afe
11
.github/workflows/validate-android.yml
vendored
11
.github/workflows/validate-android.yml
vendored
@ -7,10 +7,12 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- '.github/workflows/validate-android.yml'
|
- '.github/workflows/validate-android.yml'
|
||||||
- 'android/**'
|
- 'android/**'
|
||||||
|
- '.editorconfig'
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- '.github/workflows/validate-android.yml'
|
- '.github/workflows/validate-android.yml'
|
||||||
- 'android/**'
|
- 'android/**'
|
||||||
|
- '.editorconfig'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint:
|
lint:
|
||||||
@ -32,3 +34,12 @@ jobs:
|
|||||||
- uses: yutailang0119/action-android-lint@v1.0.2
|
- uses: yutailang0119/action-android-lint@v1.0.2
|
||||||
with:
|
with:
|
||||||
xml_path: android/build/reports/lint-results.xml
|
xml_path: android/build/reports/lint-results.xml
|
||||||
|
ktlint:
|
||||||
|
name: Kotlin Lint
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Run KTLint
|
||||||
|
uses: mrousavy/action-ktlint@v1.6
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.github_token }}
|
||||||
|
5
android/.editorconfig
Normal file
5
android/.editorconfig
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[*.{kt,kts}]
|
||||||
|
indent_size=2
|
||||||
|
insert_final_newline=true
|
||||||
|
max_line_length=off
|
||||||
|
disabled_rules=no-wildcard-imports
|
16
android/README.md
Normal file
16
android/README.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# android
|
||||||
|
|
||||||
|
This folder contains the Android-platform-specific code for react-native-vision-camera.
|
||||||
|
|
||||||
|
## Prerequesites
|
||||||
|
|
||||||
|
1. Install ktlint
|
||||||
|
```sh
|
||||||
|
brew install ktlint
|
||||||
|
```
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
It is recommended that you work on the code using the Example project (`example/android/`), since that always includes the React Native header files, plus you can easily test changes that way.
|
||||||
|
|
||||||
|
You can however still edit the library project here by opening this folder with Android Studio.
|
@ -15,12 +15,15 @@ buildscript {
|
|||||||
// noinspection DifferentKotlinGradleVersion
|
// noinspection DifferentKotlinGradleVersion
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
|
||||||
|
// ktlint
|
||||||
|
classpath "org.jlleitschuh.gradle:ktlint-gradle:10.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'com.android.library'
|
apply plugin: 'com.android.library'
|
||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
apply plugin: 'kotlin-android-extensions'
|
apply plugin: 'kotlin-android-extensions'
|
||||||
|
apply plugin: 'org.jlleitschuh.gradle.ktlint'
|
||||||
|
|
||||||
def getExtOrDefault(name) {
|
def getExtOrDefault(name) {
|
||||||
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['VisionCamera_' + name]
|
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['VisionCamera_' + name]
|
||||||
|
@ -2,8 +2,8 @@ package com.mrousavy.camera
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import androidx.camera.core.VideoCapture
|
import androidx.camera.core.VideoCapture
|
||||||
import com.mrousavy.camera.utils.makeErrorMap
|
|
||||||
import com.facebook.react.bridge.*
|
import com.facebook.react.bridge.*
|
||||||
|
import com.mrousavy.camera.utils.makeErrorMap
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@ -26,7 +26,9 @@ suspend fun CameraView.startRecording(options: ReadableMap, onRecordCallback: Ca
|
|||||||
}
|
}
|
||||||
val videoFileOptions = VideoCapture.OutputFileOptions.Builder(videoFile)
|
val videoFileOptions = VideoCapture.OutputFileOptions.Builder(videoFile)
|
||||||
|
|
||||||
videoCapture!!.startRecording(videoFileOptions.build(), recordVideoExecutor, object : VideoCapture.OnVideoSavedCallback {
|
videoCapture!!.startRecording(
|
||||||
|
videoFileOptions.build(), recordVideoExecutor,
|
||||||
|
object : VideoCapture.OnVideoSavedCallback {
|
||||||
override fun onVideoSaved(outputFileResults: VideoCapture.OutputFileResults) {
|
override fun onVideoSaved(outputFileResults: VideoCapture.OutputFileResults) {
|
||||||
val map = Arguments.createMap()
|
val map = Arguments.createMap()
|
||||||
map.putString("path", videoFile.absolutePath)
|
map.putString("path", videoFile.absolutePath)
|
||||||
@ -52,12 +54,12 @@ suspend fun CameraView.startRecording(options: ReadableMap, onRecordCallback: Ca
|
|||||||
// reset the torch mode
|
// reset the torch mode
|
||||||
camera!!.cameraControl.enableTorch(torch == "on")
|
camera!!.cameraControl.enableTorch(torch == "on")
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return TemporaryFile(videoFile.absolutePath)
|
return TemporaryFile(videoFile.absolutePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
fun CameraView.stopRecording() {
|
fun CameraView.stopRecording() {
|
||||||
if (videoCapture == null) {
|
if (videoCapture == null) {
|
||||||
|
@ -7,10 +7,10 @@ import androidx.camera.camera2.interop.Camera2CameraInfo
|
|||||||
import androidx.camera.core.ImageCapture
|
import androidx.camera.core.ImageCapture
|
||||||
import androidx.camera.core.ImageProxy
|
import androidx.camera.core.ImageProxy
|
||||||
import androidx.exifinterface.media.ExifInterface
|
import androidx.exifinterface.media.ExifInterface
|
||||||
import com.mrousavy.camera.utils.*
|
|
||||||
import com.facebook.react.bridge.Arguments
|
import com.facebook.react.bridge.Arguments
|
||||||
import com.facebook.react.bridge.ReadableMap
|
import com.facebook.react.bridge.ReadableMap
|
||||||
import com.facebook.react.bridge.WritableMap
|
import com.facebook.react.bridge.WritableMap
|
||||||
|
import com.mrousavy.camera.utils.*
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@ -70,7 +70,8 @@ suspend fun CameraView.takePhoto(options: ReadableMap): WritableMap = coroutineS
|
|||||||
async(Dispatchers.IO) {
|
async(Dispatchers.IO) {
|
||||||
Log.d(CameraView.REACT_CLASS, "Creating temp file...")
|
Log.d(CameraView.REACT_CLASS, "Creating temp file...")
|
||||||
File.createTempFile("mrousavy", ".jpg", context.cacheDir).apply { deleteOnExit() }
|
File.createTempFile("mrousavy", ".jpg", context.cacheDir).apply { deleteOnExit() }
|
||||||
})
|
}
|
||||||
|
)
|
||||||
val photo = results.first { it is ImageProxy } as ImageProxy
|
val photo = results.first { it is ImageProxy } as ImageProxy
|
||||||
val file = results.first { it is File } as File
|
val file = results.first { it is File } as File
|
||||||
|
|
||||||
|
@ -2,10 +2,10 @@ package com.mrousavy.camera
|
|||||||
|
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import androidx.exifinterface.media.ExifInterface
|
import androidx.exifinterface.media.ExifInterface
|
||||||
import com.mrousavy.camera.utils.buildMetadataMap
|
|
||||||
import com.facebook.react.bridge.Arguments
|
import com.facebook.react.bridge.Arguments
|
||||||
import com.facebook.react.bridge.ReadableMap
|
import com.facebook.react.bridge.ReadableMap
|
||||||
import com.facebook.react.bridge.WritableMap
|
import com.facebook.react.bridge.WritableMap
|
||||||
|
import com.mrousavy.camera.utils.buildMetadataMap
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
@ -20,9 +20,9 @@ import androidx.camera.extensions.NightPreviewExtender
|
|||||||
import androidx.camera.view.PreviewView
|
import androidx.camera.view.PreviewView
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.lifecycle.*
|
import androidx.lifecycle.*
|
||||||
import com.mrousavy.camera.utils.*
|
|
||||||
import com.facebook.react.bridge.*
|
import com.facebook.react.bridge.*
|
||||||
import com.facebook.react.uimanager.events.RCTEventEmitter
|
import com.facebook.react.uimanager.events.RCTEventEmitter
|
||||||
|
import com.mrousavy.camera.utils.*
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import java.lang.IllegalArgumentException
|
import java.lang.IllegalArgumentException
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
@ -342,7 +342,7 @@ class CameraView(context: Context) : FrameLayout(context), LifecycleOwner {
|
|||||||
|
|
||||||
Log.d(REACT_CLASS, "Session configured! Camera: ${camera!!}")
|
Log.d(REACT_CLASS, "Session configured! Camera: ${camera!!}")
|
||||||
invokeOnInitialized()
|
invokeOnInitialized()
|
||||||
} catch(exc: Throwable) {
|
} catch (exc: Throwable) {
|
||||||
throw when (exc) {
|
throw when (exc) {
|
||||||
is CameraError -> exc
|
is CameraError -> exc
|
||||||
is IllegalArgumentException -> InvalidCameraDeviceError(exc)
|
is IllegalArgumentException -> InvalidCameraDeviceError(exc)
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
package com.mrousavy.camera
|
package com.mrousavy.camera
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.facebook.react.bridge.ReactContext
|
|
||||||
import com.facebook.react.bridge.ReadableArray
|
import com.facebook.react.bridge.ReadableArray
|
||||||
import com.facebook.react.bridge.ReadableMap
|
import com.facebook.react.bridge.ReadableMap
|
||||||
import com.facebook.react.common.MapBuilder
|
import com.facebook.react.common.MapBuilder
|
||||||
import com.facebook.react.uimanager.SimpleViewManager
|
import com.facebook.react.uimanager.SimpleViewManager
|
||||||
import com.facebook.react.uimanager.ThemedReactContext
|
import com.facebook.react.uimanager.ThemedReactContext
|
||||||
import com.facebook.react.uimanager.annotations.ReactProp
|
import com.facebook.react.uimanager.annotations.ReactProp
|
||||||
import java.lang.ref.WeakReference
|
|
||||||
|
|
||||||
class CameraViewManager : SimpleViewManager<CameraView>() {
|
class CameraViewManager : SimpleViewManager<CameraView>() {
|
||||||
private fun addChangedPropToTransaction(view: CameraView, changedProp: String) {
|
private fun addChangedPropToTransaction(view: CameraView, changedProp: String) {
|
||||||
|
@ -14,11 +14,11 @@ import androidx.camera.core.ImageCapture
|
|||||||
import androidx.camera.extensions.HdrImageCaptureExtender
|
import androidx.camera.extensions.HdrImageCaptureExtender
|
||||||
import androidx.camera.extensions.NightImageCaptureExtender
|
import androidx.camera.extensions.NightImageCaptureExtender
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.mrousavy.camera.parsers.*
|
|
||||||
import com.mrousavy.camera.utils.*
|
|
||||||
import com.facebook.react.bridge.*
|
import com.facebook.react.bridge.*
|
||||||
import com.facebook.react.modules.core.PermissionAwareActivity
|
import com.facebook.react.modules.core.PermissionAwareActivity
|
||||||
import com.facebook.react.modules.core.PermissionListener
|
import com.facebook.react.modules.core.PermissionListener
|
||||||
|
import com.mrousavy.camera.parsers.*
|
||||||
|
import com.mrousavy.camera.utils.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -29,7 +29,7 @@ class CameraViewModule(reactContext: ReactApplicationContext) : ReactContextBase
|
|||||||
var RequestCode = 10
|
var RequestCode = 10
|
||||||
|
|
||||||
fun parsePermissionStatus(status: Int): String {
|
fun parsePermissionStatus(status: Int): String {
|
||||||
return when(status) {
|
return when (status) {
|
||||||
PackageManager.PERMISSION_DENIED -> "denied"
|
PackageManager.PERMISSION_DENIED -> "denied"
|
||||||
PackageManager.PERMISSION_GRANTED -> "authorized"
|
PackageManager.PERMISSION_GRANTED -> "authorized"
|
||||||
else -> "not-determined"
|
else -> "not-determined"
|
||||||
@ -127,8 +127,11 @@ class CameraViewModule(reactContext: ReactApplicationContext) : ReactContextBase
|
|||||||
|
|
||||||
// Filters out cameras that are LEGACY hardware level. Those don't support Preview + Photo Capture + Video Capture at the same time.
|
// Filters out cameras that are LEGACY hardware level. Those don't support Preview + Photo Capture + Video Capture at the same time.
|
||||||
if (hardwareLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
|
if (hardwareLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
|
||||||
Log.i(REACT_CLASS, "Skipping Camera #${id} because it does not meet the minimum requirements for react-native-vision-camera. " +
|
Log.i(
|
||||||
"See the tables at https://developer.android.com/reference/android/hardware/camera2/CameraDevice#regular-capture for more information.")
|
REACT_CLASS,
|
||||||
|
"Skipping Camera #$id because it does not meet the minimum requirements for react-native-vision-camera. " +
|
||||||
|
"See the tables at https://developer.android.com/reference/android/hardware/camera2/CameraDevice#regular-capture for more information."
|
||||||
|
)
|
||||||
return@loop
|
return@loop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,37 +25,42 @@ abstract class CameraError(
|
|||||||
* A throwable that caused this error.
|
* A throwable that caused this error.
|
||||||
*/
|
*/
|
||||||
cause: Throwable? = null
|
cause: Throwable? = null
|
||||||
): Throwable("[$domain/$id] $message", cause)
|
) : Throwable("[$domain/$id] $message", cause)
|
||||||
|
|
||||||
val CameraError.code: String
|
val CameraError.code: String
|
||||||
get() = "$domain/$id"
|
get() = "$domain/$id"
|
||||||
|
|
||||||
|
class MicrophonePermissionError : CameraError("permission", "microphone-permission-denied", "The Microphone permission was denied!")
|
||||||
|
class CameraPermissionError : CameraError("permission", "camera-permission-denied", "The Camera permission was denied!")
|
||||||
|
|
||||||
class MicrophonePermissionError: CameraError("permission", "microphone-permission-denied", "The Microphone permission was denied!")
|
class InvalidTypeScriptUnionError(unionName: String, unionValue: String) : CameraError("parameter", "invalid-parameter", "The given value for $unionName could not be parsed! (Received: $unionValue)")
|
||||||
class CameraPermissionError: CameraError("permission", "camera-permission-denied", "The Camera permission was denied!")
|
class UnsupportedOSError(unionName: String, unionValue: String, supportedOnOS: String) : CameraError("parameter", "unsupported-os", "The given value \"$unionValue\" could not be used for $unionName, as it is only available on Android $supportedOnOS and above!")
|
||||||
|
|
||||||
class InvalidTypeScriptUnionError(unionName: String, unionValue: String): CameraError("parameter", "invalid-parameter", "The given value for $unionName could not be parsed! (Received: $unionValue)")
|
class NoCameraDeviceError : CameraError("device", "no-device", "No device was set! Use `getAvailableCameraDevices()` to select a suitable Camera device.")
|
||||||
class UnsupportedOSError(unionName: String, unionValue: String, supportedOnOS: String): CameraError("parameter", "unsupported-os", "The given value \"$unionValue\" could not be used for $unionName, as it is only available on Android $supportedOnOS and above!")
|
class InvalidCameraDeviceError(cause: Throwable) : CameraError("device", "invalid-device", "The given Camera device could not be found for use-case binding!", cause)
|
||||||
|
|
||||||
class NoCameraDeviceError: CameraError("device", "no-device", "No device was set! Use `getAvailableCameraDevices()` to select a suitable Camera device.")
|
class FpsNotContainedInFormatError(fps: Int) : CameraError("format", "invalid-fps", "The given FPS were not valid for the currently selected format. Make sure you select a format which `frameRateRanges` includes $fps FPS!")
|
||||||
class InvalidCameraDeviceError(cause: Throwable): CameraError("device", "invalid-device", "The given Camera device could not be found for use-case binding!", cause)
|
class HdrNotContainedInFormatError() : CameraError(
|
||||||
|
"format", "invalid-hdr",
|
||||||
|
"The currently selected format does not support HDR capture! " +
|
||||||
|
"Make sure you select a format which `frameRateRanges` includes `supportsPhotoHDR`!"
|
||||||
|
)
|
||||||
|
class LowLightBoostNotContainedInFormatError() : CameraError(
|
||||||
|
"format", "invalid-low-light-boost",
|
||||||
|
"The currently selected format does not support low-light boost (night mode)! " +
|
||||||
|
"Make sure you select a format which includes `supportsLowLightBoost`."
|
||||||
|
)
|
||||||
|
|
||||||
class FpsNotContainedInFormatError(fps: Int): CameraError("format", "invalid-fps", "The given FPS were not valid for the currently selected format. Make sure you select a format which `frameRateRanges` includes $fps FPS!")
|
class CameraNotReadyError : CameraError("session", "camera-not-ready", "The Camera is not ready yet! Wait for the onInitialized() callback!")
|
||||||
class HdrNotContainedInFormatError(): CameraError("format", "invalid-hdr", "The currently selected format does not support HDR capture! " +
|
|
||||||
"Make sure you select a format which `frameRateRanges` includes `supportsPhotoHDR`!")
|
|
||||||
class LowLightBoostNotContainedInFormatError(): CameraError("format", "invalid-low-light-boost", "The currently selected format does not support low-light boost (night mode)! " +
|
|
||||||
"Make sure you select a format which includes `supportsLowLightBoost`.")
|
|
||||||
|
|
||||||
class CameraNotReadyError: CameraError("session", "camera-not-ready", "The Camera is not ready yet! Wait for the onInitialized() callback!")
|
class InvalidFormatError(format: Int) : CameraError("capture", "invalid-photo-format", "The Photo has an invalid format! Expected ${ImageFormat.YUV_420_888}, actual: $format")
|
||||||
|
class VideoEncoderError(message: String, cause: Throwable? = null) : CameraError("capture", "encoder-error", message, cause)
|
||||||
|
class VideoMuxerError(message: String, cause: Throwable? = null) : CameraError("capture", "muxer-error", message, cause)
|
||||||
|
class RecordingInProgressError(message: String, cause: Throwable? = null) : CameraError("capture", "recording-in-progress", message, cause)
|
||||||
|
class FileIOError(message: String, cause: Throwable? = null) : CameraError("capture", "file-io-error", message, cause)
|
||||||
|
class InvalidCameraError(message: String, cause: Throwable? = null) : CameraError("capture", "not-bound-error", message, cause)
|
||||||
|
|
||||||
class InvalidFormatError(format: Int): CameraError("capture", "invalid-photo-format", "The Photo has an invalid format! Expected ${ImageFormat.YUV_420_888}, actual: $format")
|
class CameraManagerUnavailableError : CameraError("system", "no-camera-manager", "The Camera manager instance was unavailable for the current Application!")
|
||||||
class VideoEncoderError(message: String, cause: Throwable? = null): CameraError("capture", "encoder-error", message, cause)
|
class ViewNotFoundError(viewId: Int) : CameraError("system", "view-not-found", "The given view (ID $viewId) was not found in the view manager.")
|
||||||
class VideoMuxerError(message: String, cause: Throwable? = null): CameraError("capture", "muxer-error", message, cause)
|
|
||||||
class RecordingInProgressError(message: String, cause: Throwable? = null): CameraError("capture", "recording-in-progress", message, cause)
|
|
||||||
class FileIOError(message: String, cause: Throwable? = null): CameraError("capture", "file-io-error", message, cause)
|
|
||||||
class InvalidCameraError(message: String, cause: Throwable? = null): CameraError("capture", "not-bound-error", message, cause)
|
|
||||||
|
|
||||||
class CameraManagerUnavailableError: CameraError("system", "no-camera-manager", "The Camera manager instance was unavailable for the current Application!")
|
class UnknownCameraError(cause: Throwable) : CameraError("unknown", "unknown", cause.message ?: "An unknown camera error occured.", cause)
|
||||||
class ViewNotFoundError(viewId: Int): CameraError("system", "view-not-found", "The given view (ID $viewId) was not found in the view manager.")
|
|
||||||
|
|
||||||
class UnknownCameraError(cause: Throwable): CameraError("unknown", "unknown", cause.message ?: "An unknown camera error occured.", cause)
|
|
||||||
|
@ -2,14 +2,12 @@ package com.mrousavy.camera.utils
|
|||||||
|
|
||||||
import android.hardware.camera2.CameraCharacteristics
|
import android.hardware.camera2.CameraCharacteristics
|
||||||
import android.util.Size
|
import android.util.Size
|
||||||
import com.mrousavy.camera.parsers.bigger
|
|
||||||
import com.mrousavy.camera.parsers.parseLensFacing
|
|
||||||
import com.facebook.react.bridge.Arguments
|
import com.facebook.react.bridge.Arguments
|
||||||
import com.facebook.react.bridge.ReadableArray
|
import com.facebook.react.bridge.ReadableArray
|
||||||
|
import com.mrousavy.camera.parsers.bigger
|
||||||
import kotlin.math.PI
|
import kotlin.math.PI
|
||||||
import kotlin.math.atan
|
import kotlin.math.atan
|
||||||
|
|
||||||
|
|
||||||
// 35mm is 135 film format, a standard in which focal lengths are usually measured
|
// 35mm is 135 film format, a standard in which focal lengths are usually measured
|
||||||
val Size35mm = Size(36, 24)
|
val Size35mm = Size(36, 24)
|
||||||
|
|
||||||
@ -38,7 +36,7 @@ fun CameraCharacteristics.getDeviceTypes(): ReadableArray {
|
|||||||
val deviceTypes = Arguments.createArray()
|
val deviceTypes = Arguments.createArray()
|
||||||
|
|
||||||
val containsTelephoto = focalLengths.any { l -> (l * cropFactor) > 35 } // TODO: Telephoto lenses are > 85mm, but we don't have anything between that range..
|
val containsTelephoto = focalLengths.any { l -> (l * cropFactor) > 35 } // TODO: Telephoto lenses are > 85mm, but we don't have anything between that range..
|
||||||
//val containsNormalLens = focalLengths.any { l -> (l * cropFactor) > 35 && (l * cropFactor) <= 55 }
|
// val containsNormalLens = focalLengths.any { l -> (l * cropFactor) > 35 && (l * cropFactor) <= 55 }
|
||||||
val containsWideAngle = focalLengths.any { l -> (l * cropFactor) >= 24 && (l * cropFactor) <= 35 }
|
val containsWideAngle = focalLengths.any { l -> (l * cropFactor) >= 24 && (l * cropFactor) <= 35 }
|
||||||
val containsUltraWideAngle = focalLengths.any { l -> (l * cropFactor) < 24 }
|
val containsUltraWideAngle = focalLengths.any { l -> (l * cropFactor) < 24 }
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@ import androidx.camera.camera2.interop.Camera2CameraInfo
|
|||||||
import androidx.camera.core.CameraSelector
|
import androidx.camera.core.CameraSelector
|
||||||
import java.lang.IllegalArgumentException
|
import java.lang.IllegalArgumentException
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new [CameraSelector] which selects the camera with the given [cameraId]
|
* Create a new [CameraSelector] which selects the camera with the given [cameraId]
|
||||||
*/
|
*/
|
||||||
|
@ -24,7 +24,7 @@ class DeviceFormat(map: ReadableMap) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun rangeFactory(minFrameRate: Any?, maxFrameRate: Any?): Range<Int> {
|
fun rangeFactory(minFrameRate: Any?, maxFrameRate: Any?): Range<Int> {
|
||||||
return when(minFrameRate) {
|
return when (minFrameRate) {
|
||||||
is Int -> Range(minFrameRate, maxFrameRate as Int)
|
is Int -> Range(minFrameRate, maxFrameRate as Int)
|
||||||
is Double -> Range(minFrameRate.toInt(), (maxFrameRate as Double).toInt())
|
is Double -> Range(minFrameRate.toInt(), (maxFrameRate as Double).toInt())
|
||||||
else -> throw IllegalArgumentException()
|
else -> throw IllegalArgumentException()
|
||||||
|
@ -4,7 +4,6 @@ import androidx.exifinterface.media.ExifInterface
|
|||||||
import com.facebook.react.bridge.Arguments
|
import com.facebook.react.bridge.Arguments
|
||||||
import com.facebook.react.bridge.WritableMap
|
import com.facebook.react.bridge.WritableMap
|
||||||
|
|
||||||
|
|
||||||
fun ExifInterface.buildMetadataMap(): WritableMap {
|
fun ExifInterface.buildMetadataMap(): WritableMap {
|
||||||
val metadataMap = Arguments.createMap()
|
val metadataMap = Arguments.createMap()
|
||||||
metadataMap.putInt("Orientation", this.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL))
|
metadataMap.putInt("Orientation", this.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL))
|
||||||
|
@ -4,14 +4,14 @@ import androidx.camera.core.ImageCapture
|
|||||||
import androidx.camera.core.ImageCaptureException
|
import androidx.camera.core.ImageCaptureException
|
||||||
import androidx.camera.core.ImageProxy
|
import androidx.camera.core.ImageProxy
|
||||||
import java.util.concurrent.Executor
|
import java.util.concurrent.Executor
|
||||||
import java.util.concurrent.Executors
|
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlin.coroutines.resumeWithException
|
import kotlin.coroutines.resumeWithException
|
||||||
import kotlin.coroutines.suspendCoroutine
|
import kotlin.coroutines.suspendCoroutine
|
||||||
|
|
||||||
|
|
||||||
suspend inline fun ImageCapture.takePicture(options: ImageCapture.OutputFileOptions, executor: Executor) = suspendCoroutine<ImageCapture.OutputFileResults> { cont ->
|
suspend inline fun ImageCapture.takePicture(options: ImageCapture.OutputFileOptions, executor: Executor) = suspendCoroutine<ImageCapture.OutputFileResults> { cont ->
|
||||||
this.takePicture(options, executor, object: ImageCapture.OnImageSavedCallback {
|
this.takePicture(
|
||||||
|
options, executor,
|
||||||
|
object : ImageCapture.OnImageSavedCallback {
|
||||||
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
|
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
|
||||||
cont.resume(outputFileResults)
|
cont.resume(outputFileResults)
|
||||||
}
|
}
|
||||||
@ -19,11 +19,14 @@ suspend inline fun ImageCapture.takePicture(options: ImageCapture.OutputFileOpti
|
|||||||
override fun onError(exception: ImageCaptureException) {
|
override fun onError(exception: ImageCaptureException) {
|
||||||
cont.resumeWithException(exception)
|
cont.resumeWithException(exception)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend inline fun ImageCapture.takePicture(executor: Executor) = suspendCoroutine<ImageProxy> { cont ->
|
suspend inline fun ImageCapture.takePicture(executor: Executor) = suspendCoroutine<ImageProxy> { cont ->
|
||||||
this.takePicture(executor, object: ImageCapture.OnImageCapturedCallback() {
|
this.takePicture(
|
||||||
|
executor,
|
||||||
|
object : ImageCapture.OnImageCapturedCallback() {
|
||||||
override fun onCaptureSuccess(image: ImageProxy) {
|
override fun onCaptureSuccess(image: ImageProxy) {
|
||||||
super.onCaptureSuccess(image)
|
super.onCaptureSuccess(image)
|
||||||
cont.resume(image)
|
cont.resume(image)
|
||||||
@ -33,5 +36,6 @@ suspend inline fun ImageCapture.takePicture(executor: Executor) = suspendCorouti
|
|||||||
super.onError(exception)
|
super.onError(exception)
|
||||||
cont.resumeWithException(exception)
|
cont.resumeWithException(exception)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,16 @@
|
|||||||
package com.mrousavy.camera.utils
|
package com.mrousavy.camera.utils
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.graphics.BitmapFactory
|
|
||||||
import android.graphics.ImageFormat
|
import android.graphics.ImageFormat
|
||||||
import androidx.camera.core.ImageProxy
|
import androidx.camera.core.ImageProxy
|
||||||
import com.mrousavy.camera.InvalidFormatError
|
import com.mrousavy.camera.InvalidFormatError
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.util.stream.Stream.concat
|
|
||||||
|
|
||||||
// TODO: Fix this flip() function (this outputs a black image)
|
// TODO: Fix this flip() function (this outputs a black image)
|
||||||
fun flip(imageBytes: ByteArray, imageWidth: Int): ByteArray {
|
fun flip(imageBytes: ByteArray, imageWidth: Int): ByteArray {
|
||||||
//separate out the sub arrays
|
// separate out the sub arrays
|
||||||
var holder = ByteArray(imageBytes.size)
|
var holder = ByteArray(imageBytes.size)
|
||||||
var subArray = ByteArray(imageWidth)
|
var subArray = ByteArray(imageWidth)
|
||||||
var subCount = 0
|
var subCount = 0
|
||||||
@ -35,7 +33,6 @@ fun flip(imageBytes: ByteArray, imageWidth: Int): ByteArray {
|
|||||||
return holder + subArray
|
return holder + subArray
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressLint("UnsafeExperimentalUsageError")
|
@SuppressLint("UnsafeExperimentalUsageError")
|
||||||
fun ImageProxy.save(file: File, flipHorizontally: Boolean) {
|
fun ImageProxy.save(file: File, flipHorizontally: Boolean) {
|
||||||
when (format) {
|
when (format) {
|
||||||
|
@ -9,7 +9,10 @@ import kotlin.coroutines.suspendCoroutine
|
|||||||
suspend fun getCameraProvider(context: Context) = suspendCoroutine<ProcessCameraProvider> { cont ->
|
suspend fun getCameraProvider(context: Context) = suspendCoroutine<ProcessCameraProvider> { cont ->
|
||||||
val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
|
val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
|
||||||
|
|
||||||
cameraProviderFuture.addListener({
|
cameraProviderFuture.addListener(
|
||||||
|
{
|
||||||
cont.resume(cameraProviderFuture.get())
|
cont.resume(cameraProviderFuture.get())
|
||||||
}, ContextCompat.getMainExecutor(context))
|
},
|
||||||
|
ContextCompat.getMainExecutor(context)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package com.mrousavy.camera.utils
|
|||||||
import android.util.Size
|
import android.util.Size
|
||||||
import android.view.Surface
|
import android.view.Surface
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rotate by a given Surface Rotation
|
* Rotate by a given Surface Rotation
|
||||||
*/
|
*/
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package com.mrousavy.camera.utils
|
package com.mrousavy.camera.utils
|
||||||
|
|
||||||
|
import com.facebook.react.bridge.Promise
|
||||||
import com.mrousavy.camera.CameraError
|
import com.mrousavy.camera.CameraError
|
||||||
import com.mrousavy.camera.UnknownCameraError
|
import com.mrousavy.camera.UnknownCameraError
|
||||||
import com.facebook.react.bridge.Promise
|
|
||||||
|
|
||||||
inline fun withPromise(promise: Promise, closure: () -> Any?) {
|
inline fun withPromise(promise: Promise, closure: () -> Any?) {
|
||||||
try {
|
try {
|
||||||
|
Loading…
Reference in New Issue
Block a user