@@ -13,20 +13,26 @@ import com.facebook.react.bridge.ReadableArray
1313import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter
1414import com.mrousavy.camera.core.CameraDeviceDetails
1515import com.mrousavy.camera.core.CameraQueues
16- import com.mrousavy.camera.core.extensions.await
17- import kotlinx.coroutines.CoroutineScope
18- import kotlinx.coroutines.asCoroutineDispatcher
19- import kotlinx.coroutines.launch
16+ import java.util.concurrent.Callable
17+ import java.util.concurrent.Future
2018
2119class CameraDevicesManager (private val reactContext : ReactApplicationContext ) : ReactContextBaseJavaModule(reactContext) {
2220 companion object {
2321 private const val TAG = " CameraDevices"
2422 }
2523 private val executor = CameraQueues .cameraExecutor
26- private val coroutineScope = CoroutineScope (executor.asCoroutineDispatcher())
24+
25+ // Because getConstants() and initialize() are only called once for the entire life of the app process by react native.
26+ // We have to make sure that everything is initialized, otherwise no devices are going to be retrieved ever.
27+ // Either because getConstants() returns empty, or that the sendAvailableDevicesChangedEvent inside initialize() sends empty as well.
28+ // We still give the opportunity for device to be initialized between init call and react initialization.
29+ private val cameraInitializationFuture: Future <CameraInitialization >
2730 private val cameraManager = reactContext.getSystemService(Context .CAMERA_SERVICE ) as CameraManager
28- private var cameraProvider: ProcessCameraProvider ? = null
29- private var extensionsManager: ExtensionsManager ? = null
31+
32+ private class CameraInitialization {
33+ var cameraProvider: ProcessCameraProvider ? = null
34+ var extensionsManager: ExtensionsManager ? = null
35+ }
3036
3137 private val callback = object : CameraManager .AvailabilityCallback () {
3238 private var deviceIds = cameraManager.cameraIdList.toMutableList()
@@ -61,17 +67,19 @@ class CameraDevicesManager(private val reactContext: ReactApplicationContext) :
6167
6268 // Init cameraProvider + manager as early as possible
6369 init {
64- coroutineScope.launch {
70+ cameraInitializationFuture = executor.submit(Callable {
71+ val cameraInitialization = CameraInitialization ()
6572 try {
6673 Log .i(TAG , " Initializing ProcessCameraProvider..." )
67- cameraProvider = ProcessCameraProvider .getInstance(reactContext).await(executor )
74+ cameraInitialization. cameraProvider = ProcessCameraProvider .getInstance(reactContext).get( )
6875 Log .i(TAG , " Initializing ExtensionsManager..." )
69- extensionsManager = ExtensionsManager .getInstanceAsync(reactContext, cameraProvider!! ).await(executor )
76+ cameraInitialization. extensionsManager = ExtensionsManager .getInstanceAsync(reactContext, cameraInitialization. cameraProvider!! ).get( )
7077 Log .i(TAG , " Successfully initialized!" )
7178 } catch (error: Throwable ) {
7279 Log .e(TAG , " Failed to initialize ProcessCameraProvider/ExtensionsManager! Error: ${error.message} " , error)
7380 }
74- }
81+ return @Callable cameraInitialization
82+ })
7583 }
7684
7785 // Note: initialize() will be called after getConstants on new arch!
@@ -88,8 +96,9 @@ class CameraDevicesManager(private val reactContext: ReactApplicationContext) :
8896
8997 private fun getDevicesJson (): ReadableArray {
9098 val devices = Arguments .createArray()
91- val cameraProvider = cameraProvider ? : return devices
92- val extensionsManager = extensionsManager ? : return devices
99+
100+ val cameraProvider = cameraInitializationFuture.get().cameraProvider ? : return devices
101+ val extensionsManager = cameraInitializationFuture.get().extensionsManager ? : return devices
93102
94103 cameraProvider.availableCameraInfos.forEach { cameraInfo ->
95104 val device = CameraDeviceDetails (cameraInfo, extensionsManager)
@@ -98,12 +107,14 @@ class CameraDevicesManager(private val reactContext: ReactApplicationContext) :
98107 return devices
99108 }
100109
110+ // Called by CameraManager.AvailabilityCallback registered after initialize()
101111 fun sendAvailableDevicesChangedEvent () {
102112 val eventEmitter = reactContext.getJSModule(RCTDeviceEventEmitter ::class .java)
103113 val devices = getDevicesJson()
104114 eventEmitter.emit(" CameraDevicesChanged" , devices)
105115 }
106116
117+ // Called by react native only once
107118 override fun getConstants (): MutableMap <String , Any ?> {
108119 val devices = getDevicesJson()
109120 val preferredDevice = if (devices.size() > 0 ) devices.getMap(0 ) else null
0 commit comments