Skip to content

Commit 04851b3

Browse files
authored
Merge pull request #20870 from wordpress-mobile/andy/sentry-issue-5299933951
[Bug] Remove flow and use callbacks instead. I know, pain.
2 parents 0254e9e + 3329d74 commit 04851b3

File tree

5 files changed

+64
-84
lines changed

5 files changed

+64
-84
lines changed

WordPress/src/main/java/org/wordpress/android/ui/barcodescanner/BarcodeScanner.kt

+25-25
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,20 @@ import androidx.compose.ui.platform.LocalLifecycleOwner
1818
import androidx.compose.ui.tooling.preview.Preview
1919
import androidx.compose.ui.viewinterop.AndroidView
2020
import androidx.core.content.ContextCompat
21-
import kotlinx.coroutines.flow.Flow
22-
import kotlinx.coroutines.flow.flowOf
2321
import org.wordpress.android.ui.compose.theme.AppTheme
2422
import androidx.camera.core.Preview as CameraPreview
2523

2624
@Composable
2725
fun BarcodeScanner(
2826
codeScanner: CodeScanner,
29-
onScannedResult: (Flow<CodeScannerStatus>) -> Unit
27+
onScannedResult: CodeScannerCallback
3028
) {
3129
val context = LocalContext.current
3230
val lifecycleOwner = LocalLifecycleOwner.current
3331
val cameraProviderFuture = remember {
3432
ProcessCameraProvider.getInstance(context)
3533
}
34+
3635
Column(
3736
modifier = Modifier.fillMaxSize()
3837
) {
@@ -51,30 +50,27 @@ fun BarcodeScanner(
5150
.setBackpressureStrategy(STRATEGY_KEEP_ONLY_LATEST)
5251
.build()
5352
imageAnalysis.setAnalyzer(ContextCompat.getMainExecutor(context)) { imageProxy ->
54-
onScannedResult(codeScanner.startScan(imageProxy))
53+
val callback = object : CodeScannerCallback {
54+
override fun run(status: CodeScannerStatus?) {
55+
status?.let { onScannedResult.run(it) }
56+
}
57+
}
58+
codeScanner.startScan(imageProxy, callback)
5559
}
5660
try {
5761
cameraProviderFuture.get().bindToLifecycle(lifecycleOwner, selector, preview, imageAnalysis)
5862
} catch (e: IllegalStateException) {
59-
onScannedResult(
60-
flowOf(
61-
CodeScannerStatus.Failure(
62-
e.message
63-
?: "Illegal state exception while binding camera provider to lifecycle",
64-
CodeScanningErrorType.Other(e)
65-
)
66-
)
67-
)
63+
onScannedResult.run(CodeScannerStatus.Failure(
64+
e.message
65+
?: "Illegal state exception while binding camera provider to lifecycle",
66+
CodeScanningErrorType.Other(e)
67+
))
6868
} catch (e: IllegalArgumentException) {
69-
onScannedResult(
70-
flowOf(
71-
CodeScannerStatus.Failure(
72-
e.message
73-
?: "Illegal argument exception while binding camera provider to lifecycle",
74-
CodeScanningErrorType.Other(e)
75-
)
76-
)
77-
)
69+
onScannedResult.run(CodeScannerStatus.Failure(
70+
e.message
71+
?: "Illegal argument exception while binding camera provider to lifecycle",
72+
CodeScanningErrorType.Other(e)
73+
))
7874
}
7975
previewView
8076
},
@@ -84,8 +80,8 @@ fun BarcodeScanner(
8480
}
8581

8682
class DummyCodeScanner : CodeScanner {
87-
override fun startScan(imageProxy: ImageProxy): Flow<CodeScannerStatus> {
88-
return flowOf(CodeScannerStatus.Success("", GoogleBarcodeFormatMapper.BarcodeFormat.FormatUPCA))
83+
override fun startScan(imageProxy: ImageProxy, callback: CodeScannerCallback) {
84+
callback.run(CodeScannerStatus.Success("", GoogleBarcodeFormatMapper.BarcodeFormat.FormatUPCA))
8985
}
9086
}
9187

@@ -94,6 +90,10 @@ class DummyCodeScanner : CodeScanner {
9490
@Composable
9591
private fun BarcodeScannerScreenPreview() {
9692
AppTheme {
97-
BarcodeScanner(codeScanner = DummyCodeScanner(), onScannedResult = {})
93+
BarcodeScanner(codeScanner = DummyCodeScanner(), onScannedResult = object : CodeScannerCallback {
94+
override fun run(status: CodeScannerStatus?) {
95+
// no-ops
96+
}
97+
})
9898
}
9999
}

WordPress/src/main/java/org/wordpress/android/ui/barcodescanner/BarcodeScannerScreen.kt

+1-2
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,14 @@ import androidx.compose.ui.res.stringResource
1515
import androidx.compose.ui.tooling.preview.Preview
1616
import androidx.compose.ui.unit.dp
1717
import org.wordpress.android.R
18-
import kotlinx.coroutines.flow.Flow
1918
import org.wordpress.android.ui.compose.theme.AppTheme
2019

2120
@Composable
2221
fun BarcodeScannerScreen(
2322
codeScanner: CodeScanner,
2423
permissionState: BarcodeScanningViewModel.PermissionState,
2524
onResult: (Boolean) -> Unit,
26-
onScannedResult: (Flow<CodeScannerStatus>) -> Unit,
25+
onScannedResult: CodeScannerCallback,
2726
) {
2827
val cameraPermissionLauncher = rememberLauncherForActivityResult(
2928
contract = ActivityResultContracts.RequestPermission(),

WordPress/src/main/java/org/wordpress/android/ui/barcodescanner/BarcodeScanningFragment.kt

+4-10
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,7 @@ import androidx.core.os.bundleOf
1212
import androidx.fragment.app.Fragment
1313
import androidx.fragment.app.setFragmentResult
1414
import androidx.fragment.app.viewModels
15-
import androidx.lifecycle.Lifecycle
16-
import androidx.lifecycle.lifecycleScope
17-
import androidx.lifecycle.repeatOnLifecycle
1815
import dagger.hilt.android.AndroidEntryPoint
19-
import kotlinx.coroutines.launch
2016
import org.wordpress.android.ui.compose.theme.AppTheme
2117
import org.wordpress.android.util.WPPermissionUtils
2218
import javax.inject.Inject
@@ -52,12 +48,10 @@ class BarcodeScanningFragment : Fragment() {
5248
shouldShowRequestPermissionRationale(KEY_CAMERA_PERMISSION)
5349
)
5450
},
55-
onScannedResult = { codeScannerStatus ->
56-
viewLifecycleOwner.lifecycleScope.launch {
57-
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
58-
codeScannerStatus.collect { status ->
59-
setResultAndPopStack(status)
60-
}
51+
onScannedResult = object : CodeScannerCallback {
52+
override fun run(status: CodeScannerStatus?) {
53+
if (status != null) {
54+
setResultAndPopStack(status)
6155
}
6256
}
6357
},

WordPress/src/main/java/org/wordpress/android/ui/barcodescanner/CodeScanner.kt

+5-2
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@ package org.wordpress.android.ui.barcodescanner
22

33
import android.os.Parcelable
44
import androidx.camera.core.ImageProxy
5-
import kotlinx.coroutines.flow.Flow
65
import kotlinx.parcelize.Parcelize
76

87
interface CodeScanner {
9-
fun startScan(imageProxy: ImageProxy): Flow<CodeScannerStatus>
8+
fun startScan(imageProxy: ImageProxy, callback: CodeScannerCallback)
9+
}
10+
11+
interface CodeScannerCallback {
12+
fun run(status: CodeScannerStatus?)
1013
}
1114

1215
sealed class CodeScannerStatus : Parcelable {

WordPress/src/main/java/org/wordpress/android/ui/barcodescanner/GoogleMLKitCodeScanner.kt

+29-45
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@ package org.wordpress.android.ui.barcodescanner
33
import androidx.camera.core.ImageProxy
44
import com.google.mlkit.vision.barcode.BarcodeScanner
55
import com.google.mlkit.vision.barcode.common.Barcode
6-
import kotlinx.coroutines.channels.ProducerScope
7-
import kotlinx.coroutines.channels.awaitClose
8-
import kotlinx.coroutines.flow.Flow
9-
import kotlinx.coroutines.flow.callbackFlow
106
import javax.inject.Inject
117

128
class GoogleMLKitCodeScanner @Inject constructor(
@@ -17,53 +13,41 @@ class GoogleMLKitCodeScanner @Inject constructor(
1713
) : CodeScanner {
1814
private var barcodeFound = false
1915
@androidx.camera.core.ExperimentalGetImage
20-
override fun startScan(imageProxy: ImageProxy): Flow<CodeScannerStatus> {
21-
return callbackFlow {
22-
val barcodeTask = barcodeScanner.process(inputImageProvider.provideImage(imageProxy))
23-
barcodeTask.addOnCompleteListener {
24-
// We must call image.close() on received images when finished using them.
25-
// Otherwise, new images may not be received or the camera may stall.
26-
imageProxy.close()
27-
}
28-
barcodeTask.addOnSuccessListener { barcodeList ->
29-
// The check for barcodeFound is done because the startScan method will be called
30-
// continuously by the library as long as we are in the scanning screen.
31-
// There will be a good chance that the same barcode gets identified multiple times and as a result
32-
// success callback will be called multiple times.
33-
if (!barcodeList.isNullOrEmpty() && !barcodeFound) {
34-
barcodeFound = true
35-
handleScanSuccess(barcodeList.firstOrNull())
36-
this@callbackFlow.close()
37-
}
38-
}
39-
barcodeTask.addOnFailureListener { exception ->
40-
this@callbackFlow.trySend(
41-
CodeScannerStatus.Failure(
42-
error = exception.message,
43-
type = errorMapper.mapGoogleMLKitScanningErrors(exception)
44-
)
45-
)
46-
this@callbackFlow.close()
16+
override fun startScan(imageProxy: ImageProxy, callback: CodeScannerCallback) {
17+
val barcodeTask = barcodeScanner.process(inputImageProvider.provideImage(imageProxy))
18+
barcodeTask.addOnCompleteListener {
19+
// We must call image.close() on received images when finished using them.
20+
// Otherwise, new images may not be received or the camera may stall.
21+
imageProxy.close()
22+
}
23+
barcodeTask.addOnSuccessListener { barcodeList ->
24+
// The check for barcodeFound is done because the startScan method will be called
25+
// continuously by the library as long as we are in the scanning screen.
26+
// There will be a good chance that the same barcode gets identified multiple times and as a result
27+
// success callback will be called multiple times.
28+
if (!barcodeList.isNullOrEmpty() && !barcodeFound) {
29+
barcodeFound = true
30+
callback.run(handleScanSuccess(barcodeList.firstOrNull()))
4731
}
48-
49-
awaitClose()
32+
}
33+
barcodeTask.addOnFailureListener { exception ->
34+
callback.run(CodeScannerStatus.Failure(
35+
error = exception.message,
36+
type = errorMapper.mapGoogleMLKitScanningErrors(exception)
37+
))
5038
}
5139
}
5240

53-
private fun ProducerScope<CodeScannerStatus>.handleScanSuccess(code: Barcode?) {
54-
code?.rawValue?.let {
55-
trySend(
56-
CodeScannerStatus.Success(
57-
it,
58-
barcodeFormatMapper.mapBarcodeFormat(code.format)
59-
)
41+
private fun handleScanSuccess(code: Barcode?): CodeScannerStatus {
42+
return code?.rawValue?.let {
43+
CodeScannerStatus.Success(
44+
it,
45+
barcodeFormatMapper.mapBarcodeFormat(code.format)
6046
)
6147
} ?: run {
62-
trySend(
63-
CodeScannerStatus.Failure(
64-
error = "Failed to find a valid raw value!",
65-
type = CodeScanningErrorType.Other(Throwable("Empty raw value"))
66-
)
48+
CodeScannerStatus.Failure(
49+
error = "Failed to find a valid raw value!",
50+
type = CodeScanningErrorType.Other(Throwable("Empty raw value"))
6751
)
6852
}
6953
}

0 commit comments

Comments
 (0)