diff --git a/package/android/src/main/java/com/mrousavy/camera/core/CameraSession.kt b/package/android/src/main/java/com/mrousavy/camera/core/CameraSession.kt index 12f4b778ab..41d21e6627 100644 --- a/package/android/src/main/java/com/mrousavy/camera/core/CameraSession.kt +++ b/package/android/src/main/java/com/mrousavy/camera/core/CameraSession.kt @@ -232,7 +232,7 @@ class CameraSession(private val context: Context, private val cameraManager: Cam /** * Set up the `CaptureSession` with all outputs (preview, photo, video, codeScanner) and their HDR/Format settings. */ - private fun configureOutputs(configuration: CameraConfiguration) { + private suspend fun configureOutputs(configuration: CameraConfiguration) { val cameraId = configuration.cameraId ?: throw NoCameraDeviceError() // Destroy previous outputs @@ -313,7 +313,7 @@ class CameraSession(private val context: Context, private val cameraManager: Cam ) outputs.add(output) // Size is usually landscape, so we flip it here - previewView?.size = Size(size.height, size.width) + previewView?.setSurfaceSize(size.width, size.height) } // CodeScanner Output diff --git a/package/android/src/main/java/com/mrousavy/camera/core/PreviewView.kt b/package/android/src/main/java/com/mrousavy/camera/core/PreviewView.kt index cfda57a79d..8af1e5860a 100644 --- a/package/android/src/main/java/com/mrousavy/camera/core/PreviewView.kt +++ b/package/android/src/main/java/com/mrousavy/camera/core/PreviewView.kt @@ -10,25 +10,21 @@ import android.view.SurfaceView import android.widget.FrameLayout import com.facebook.react.bridge.UiThreadUtil import com.mrousavy.camera.extensions.getMaximumPreviewSize +import com.mrousavy.camera.extensions.resize import com.mrousavy.camera.types.ResizeMode import kotlin.math.roundToInt +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext @SuppressLint("ViewConstructor") class PreviewView(context: Context, callback: SurfaceHolder.Callback) : SurfaceView(context) { var size: Size = getMaximumPreviewSize() - set(value) { - field = value - UiThreadUtil.runOnUiThread { - Log.i(TAG, "Setting PreviewView Surface Size to $width x $height...") - holder.setFixedSize(value.height, value.width) - requestLayout() - invalidate() - } - } + private set var resizeMode: ResizeMode = ResizeMode.COVER set(value) { field = value UiThreadUtil.runOnUiThread { + Log.i(TAG, "Setting PreviewView ResizeMode to $value...") requestLayout() invalidate() } @@ -41,11 +37,23 @@ class PreviewView(context: Context, callback: SurfaceHolder.Callback) : SurfaceV FrameLayout.LayoutParams.MATCH_PARENT, Gravity.CENTER ) + holder.setKeepScreenOn(true) holder.addCallback(callback) } + suspend fun setSurfaceSize(width: Int, height: Int) { + withContext(Dispatchers.Main) { + size = Size(width, height) + Log.i(TAG, "Setting PreviewView Surface Size to $size...") + requestLayout() + invalidate() + holder.resize(width, height) + } + } + private fun getSize(contentSize: Size, containerSize: Size, resizeMode: ResizeMode): Size { - val contentAspectRatio = contentSize.width.toDouble() / contentSize.height + // TODO: Take sensor orientation into account here + val contentAspectRatio = contentSize.height.toDouble() / contentSize.width val containerAspectRatio = containerSize.width.toDouble() / containerSize.height val widthOverHeight = when (resizeMode) { diff --git a/package/android/src/main/java/com/mrousavy/camera/extensions/CameraCharacteristics+getPreviewSize.kt b/package/android/src/main/java/com/mrousavy/camera/extensions/CameraCharacteristics+getPreviewSize.kt index e758c454c1..ecd525cf98 100644 --- a/package/android/src/main/java/com/mrousavy/camera/extensions/CameraCharacteristics+getPreviewSize.kt +++ b/package/android/src/main/java/com/mrousavy/camera/extensions/CameraCharacteristics+getPreviewSize.kt @@ -9,7 +9,7 @@ fun getMaximumPreviewSize(): Size { // See https://developer.android.com/reference/android/hardware/camera2/params/StreamConfigurationMap // According to the Android Developer documentation, PREVIEW streams can have a resolution // of up to the phone's display's resolution, with a maximum of 1920x1080. - val display1080p = Size(1080, 1920) + val display1080p = Size(1920, 1080) val displaySize = Size( Resources.getSystem().displayMetrics.widthPixels, Resources.getSystem().displayMetrics.heightPixels diff --git a/package/android/src/main/java/com/mrousavy/camera/extensions/SurfaceHolder+resize.kt b/package/android/src/main/java/com/mrousavy/camera/extensions/SurfaceHolder+resize.kt new file mode 100644 index 0000000000..35f00b7dc1 --- /dev/null +++ b/package/android/src/main/java/com/mrousavy/camera/extensions/SurfaceHolder+resize.kt @@ -0,0 +1,32 @@ +package com.mrousavy.camera.extensions + +import android.view.SurfaceHolder +import androidx.annotation.UiThread +import kotlin.coroutines.resume +import kotlinx.coroutines.suspendCancellableCoroutine + +@UiThread +suspend fun SurfaceHolder.resize(width: Int, height: Int) { + return suspendCancellableCoroutine { continuation -> + val currentSize = this.surfaceFrame + if (currentSize.width() == width && currentSize.height() == height) { + // Already in target size + continuation.resume(Unit) + return@suspendCancellableCoroutine + } + + val callback = object : SurfaceHolder.Callback { + override fun surfaceCreated(holder: SurfaceHolder) = Unit + override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) { + holder.removeCallback(this) + continuation.resume(Unit) + } + override fun surfaceDestroyed(holder: SurfaceHolder) { + holder.removeCallback(this) + continuation.cancel(Error("Tried to resize SurfaceView, but Surface has been destroyed!")) + } + } + this.addCallback(callback) + this.setFixedSize(width, height) + } +} diff --git a/package/example/src/CameraPage.tsx b/package/example/src/CameraPage.tsx index bef7fcffba..8b5683f0d6 100644 --- a/package/example/src/CameraPage.tsx +++ b/package/example/src/CameraPage.tsx @@ -99,7 +99,6 @@ export function CameraPage({ navigation }: Props): React.ReactElement { }, [isPressingButton], ) - // Camera callbacks const onError = useCallback((error: CameraRuntimeError) => { console.error(error) }, [])