Skip to content

Commit 4d64766

Browse files
committed
fix: Make sure that camera is initialized when getting devices
1 parent 0d7b939 commit 4d64766

File tree

1 file changed

+24
-13
lines changed

1 file changed

+24
-13
lines changed

package/android/src/main/java/com/mrousavy/camera/react/CameraDevicesManager.kt

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,26 @@ import com.facebook.react.bridge.ReadableArray
1313
import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter
1414
import com.mrousavy.camera.core.CameraDeviceDetails
1515
import 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

2119
class 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

Comments
 (0)