@@ -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,20 +67,23 @@ 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!
86+ // This is not what I observed, it is called before for me. Potentially leading to an issue.
7887 override fun initialize () {
7988 super .initialize()
8089 cameraManager.registerAvailabilityCallback(callback, null )
@@ -88,8 +97,9 @@ class CameraDevicesManager(private val reactContext: ReactApplicationContext) :
8897
8998 private fun getDevicesJson (): ReadableArray {
9099 val devices = Arguments .createArray()
91- val cameraProvider = cameraProvider ? : return devices
92- val extensionsManager = extensionsManager ? : return devices
100+
101+ val cameraProvider = cameraInitializationFuture.get().cameraProvider ? : return devices
102+ val extensionsManager = cameraInitializationFuture.get().extensionsManager ? : return devices
93103
94104 cameraProvider.availableCameraInfos.forEach { cameraInfo ->
95105 val device = CameraDeviceDetails (cameraInfo, extensionsManager)
@@ -98,12 +108,14 @@ class CameraDevicesManager(private val reactContext: ReactApplicationContext) :
98108 return devices
99109 }
100110
111+ // Called by CameraManager.AvailabilityCallback registered after initialize()
101112 fun sendAvailableDevicesChangedEvent () {
102113 val eventEmitter = reactContext.getJSModule(RCTDeviceEventEmitter ::class .java)
103114 val devices = getDevicesJson()
104115 eventEmitter.emit(" CameraDevicesChanged" , devices)
105116 }
106117
118+ // Called by react native only once
107119 override fun getConstants (): MutableMap <String , Any ?> {
108120 val devices = getDevicesJson()
109121 val preferredDevice = if (devices.size() > 0 ) devices.getMap(0 ) else null
0 commit comments