Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Use CameraX for device details #2624

Merged
merged 6 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,33 @@ package com.mrousavy.camera
import android.content.Context
import android.hardware.camera2.CameraManager
import android.util.Log
import androidx.camera.extensions.ExtensionsManager
import androidx.camera.lifecycle.ProcessCameraProvider
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContext.RCTDeviceEventEmitter
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.modules.core.DeviceEventManagerModule
import com.mrousavy.camera.core.CameraDeviceDetails
import com.mrousavy.camera.core.CameraQueues
import com.mrousavy.camera.extensions.await
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.launch

class CameraDevicesManager(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
companion object {
private const val TAG = "CameraDevices"
}
private val executor = CameraQueues.cameraExecutor
private val coroutineScope = CoroutineScope(executor.asCoroutineDispatcher())
private val cameraManager = reactContext.getSystemService(Context.CAMERA_SERVICE) as CameraManager
private var cameraProvider: ProcessCameraProvider? = null
private var extensionsManager: ExtensionsManager? = null

private val callback = object : CameraManager.AvailabilityCallback() {
private var devices = cameraManager.cameraIdList.toMutableList()
private var deviceIds = cameraManager.cameraIdList.toMutableList()

// Check if device is still physically connected (even if onCameraUnavailable() is called)
private fun isDeviceConnected(cameraId: String): Boolean =
Expand All @@ -31,24 +42,36 @@ class CameraDevicesManager(private val reactContext: ReactApplicationContext) :

override fun onCameraAvailable(cameraId: String) {
Log.i(TAG, "Camera #$cameraId is now available.")
if (!devices.contains(cameraId)) {
devices.add(cameraId)
if (!deviceIds.contains(cameraId)) {
deviceIds.add(cameraId)
sendAvailableDevicesChangedEvent()
}
}

override fun onCameraUnavailable(cameraId: String) {
Log.i(TAG, "Camera #$cameraId is now unavailable.")
if (devices.contains(cameraId) && !isDeviceConnected(cameraId)) {
devices.remove(cameraId)
if (deviceIds.contains(cameraId) && !isDeviceConnected(cameraId)) {
deviceIds.remove(cameraId)
sendAvailableDevicesChangedEvent()
}
}
}

init {
coroutineScope.launch {
Log.i(TAG, "Initializing ProcessCameraProvider...")
cameraProvider = ProcessCameraProvider.getInstance(reactContext).await(executor)
Log.i(TAG, "Initializing ExtensionsManager...")
extensionsManager = ExtensionsManager.getInstanceAsync(reactContext, cameraProvider!!).await(executor)
Log.i(TAG, "Successfully initialized!")
sendAvailableDevicesChangedEvent()
}
}

override fun getName(): String = TAG

override fun initialize() {
super.initialize()
cameraManager.registerAvailabilityCallback(callback, null)
}

Expand All @@ -59,16 +82,20 @@ class CameraDevicesManager(private val reactContext: ReactApplicationContext) :

private fun getDevicesJson(): ReadableArray {
val devices = Arguments.createArray()
cameraManager.cameraIdList.forEach { cameraId ->
val device = CameraDeviceDetails(cameraManager, cameraId)
val cameraProvider = cameraProvider ?: return devices
val extensionsManager = extensionsManager ?: return devices

cameraProvider.availableCameraInfos.forEach { cameraInfo ->
val device = CameraDeviceDetails(cameraInfo, extensionsManager, reactContext)
devices.pushMap(device.toMap())
}
return devices
}

fun sendAvailableDevicesChangedEvent() {
val eventEmitter = reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
eventEmitter.emit("CameraDevicesChanged", getDevicesJson())
val eventEmitter = reactContext.getJSModule(RCTDeviceEventEmitter::class.java)
val devices = getDevicesJson()
eventEmitter.emit("CameraDevicesChanged", devices)
}

override fun getConstants(): MutableMap<String, Any?> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.mrousavy.camera

import android.annotation.SuppressLint
import android.content.Context
import android.hardware.camera2.CameraManager
import android.util.Log
import android.view.Gravity
import android.view.ScaleGestureDetector
Expand Down Expand Up @@ -103,7 +102,6 @@ class CameraView(context: Context) :
// private properties
private var isMounted = false
private val mainCoroutineScope = CoroutineScope(Dispatchers.Main)
private val cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager

// session
internal val cameraSession: CameraSession
Expand Down Expand Up @@ -135,7 +133,7 @@ class CameraView(context: Context) :
}
}
}
cameraSession = CameraSession(context, cameraManager, this)
cameraSession = CameraSession(context, this)
}

override fun onAttachedToWindow() {
Expand Down
Loading
Loading