Skip to content

Commit 557f606

Browse files
authored
feat(barcode-scanning): Android implementation (#249 by @frankcalise)
* fix(barcode-scanning): android builds * feat(barcode-scanning): wip, scanning working
1 parent 46b17be commit 557f606

File tree

7 files changed

+121
-32
lines changed

7 files changed

+121
-32
lines changed

apps/ExampleApp/app/screens/BarcodeScanningScreen.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { AppStackScreenProps } from "../navigators"
66
import { Text, Icon, ImageSelector, Screen } from "../components"
77
import { useTypedNavigation } from "../navigators/useTypedNavigation"
88

9-
import RNMLKitBarcodeScanningModule from "@infinitered/react-native-mlkit-barcode-scanning"
9+
import RNMLKitBarcodeScanningModule from "@infinitered/react-native-mlkit-barcode-scanning"
1010
import type { BarcodeScannerResult } from "@infinitered/react-native-mlkit-barcode-scanning"
1111
import { UseExampleImageStatus, SelectedImage } from "../utils/useExampleImage"
1212

@@ -60,6 +60,7 @@ export const BarcodeScanningScreen: FC<BarcodeScanningScreenProps> = observer(
6060
if (!image?.uri) return
6161
setIsScanning(true)
6262
try {
63+
// await RNMLKitBarcodeScanningModule.initialize()
6364
const scanResult = await RNMLKitBarcodeScanningModule.process(image.uri)
6465
setResult(scanResult)
6566
setStatus("done")

modules/react-native-mlkit-barcode-scanning/android/build.gradle

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ if (useManagedAndroidSdkVersions) {
2121
ext.safeExtGet = { prop, fallback ->
2222
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
2323
}
24+
25+
// Ensures backward compatibility
26+
ext.getKotlinVersion = {
27+
if (ext.has("kotlinVersion")) {
28+
ext.kotlinVersion()
29+
} else {
30+
ext.safeExtGet("kotlinVersion", "1.8.10")
31+
}
32+
}
2433
}
2534
project.android {
2635
compileSdkVersion safeExtGet("compileSdkVersion", 36)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import android.net.Uri
2+
import com.google.mlkit.vision.common.InputImage
3+
import com.google.mlkit.vision.barcode.BarcodeScanning
4+
import com.google.mlkit.vision.barcode.BarcodeScanner
5+
import expo.modules.kotlin.AppContext
6+
import kotlinx.coroutines.CompletableDeferred
7+
import red.infinite.reactnativemlkit.core.RNMLKitImage
8+
import red.infinite.reactnativemlkit.barcodescanning.RNMLKitBarcodeScannerResult
9+
10+
// private var options: RNMLKitBarcodeScannerOptions
11+
class RNMLKitBarcodeScanner() {
12+
13+
private var barcodeScanner: BarcodeScanner? = BarcodeScanning.getClient()
14+
15+
suspend fun detectInImage(imagePath: String, appContext: AppContext): Result<RNMLKitBarcodeScannerResult> {
16+
val result = CompletableDeferred<Result<RNMLKitBarcodeScannerResult>>()
17+
18+
try {
19+
if(appContext.reactContext == null) {
20+
val exception = Exception("RNMLKitBarcodeScanner: React Context is null")
21+
result.complete(Result.failure(exception))
22+
return result.await()
23+
}
24+
25+
var image: InputImage = RNMLKitImage(Uri.parse(imagePath), appContext.reactContext!!).image
26+
27+
barcodeScanner?.process(image)?.addOnSuccessListener { barcodes ->
28+
val expoBarcodes = RNMLKitBarcodeScannerResult(barcodes, imagePath)
29+
result.complete(Result.success(expoBarcodes))
30+
}?.addOnFailureListener { e ->
31+
result.complete(Result.failure(e))
32+
}
33+
} catch (e: Exception) {
34+
result.complete(Result.failure(e))
35+
}
36+
return result.await()
37+
}
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package red.infinite.reactnativemlkit.barcodescanning
2+
3+
import expo.modules.kotlin.records.Record
4+
import expo.modules.kotlin.records.Field
5+
import com.google.mlkit.vision.barcode.common.Barcode
6+
import red.infinite.reactnativemlkit.core.RNMLKitRect
7+
8+
data class RNMLKitBarcodeScannerResultRecord(
9+
@Field var barcodes: List<RNMLKitBarcode> = mutableListOf(),
10+
@Field var imagePath: String
11+
) : Record
12+
13+
class RNMLKitBarcodeScannerResult(private val barcodes: List<Barcode>, private val imagePath: String) {
14+
val record: RNMLKitBarcodeScannerResultRecord
15+
get() = RNMLKitBarcodeScannerResultRecord(
16+
barcodes = barcodes.map { barcode ->
17+
RNMLKitBarcode().apply {
18+
//frame = RNMLKitRect.fromRect(barcode.boundingBox ?? RNMLKitRect.zero())
19+
rawValue = barcode.rawValue
20+
displayValue = barcode.displayValue
21+
}
22+
},
23+
imagePath = imagePath
24+
)
25+
}
26+
27+
data class RNMLKitBarcode(
28+
@Field var frame: RNMLKitRect = RNMLKitRect.zero(),
29+
@Field var rawValue: String? = null,
30+
@Field var displayValue: String? = null
31+
) : Record
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
1-
package red.infinite.reactnativemlkit
1+
package red.infinite.reactnativemlkit.barcodescanning
22

3+
import RNMLKitBarcodeScanner
4+
5+
import android.net.Uri
6+
7+
import expo.modules.kotlin.Promise
8+
import expo.modules.kotlin.exception.CodedException
39
import expo.modules.kotlin.modules.Module
410
import expo.modules.kotlin.modules.ModuleDefinition
5-
import java.net.URL
11+
import kotlinx.coroutines.runBlocking
12+
import red.infinite.reactnativemlkit.core.RNMLKitModule
613

7-
class RNMLKitBarcodeScanningModule : Module() {
14+
class RNMLKitBarcodeScanningModule : RNMLKitModule("RNMLKitBarcodeScanning") {
15+
private var barcodeScanner: RNMLKitBarcodeScanner? = null
816
// Each module class must implement the definition function. The definition consists of components
917
// that describes the module's functionality and behavior.
1018
// See https://docs.expo.dev/modules/module-api for more details about available components.
@@ -14,37 +22,38 @@ class RNMLKitBarcodeScanningModule : Module() {
1422
// The module will be accessible from `requireNativeModule('RNMLKitBarcodeScanning')` in JavaScript.
1523
Name("RNMLKitBarcodeScanning")
1624

17-
// Defines constant property on the module.
18-
Constant("PI") {
19-
Math.PI
20-
}
21-
22-
// Defines event names that the module can send to JavaScript.
23-
Events("onChange")
2425

25-
// Defines a JavaScript synchronous function that runs the native code on the JavaScript thread.
26-
Function("hello") {
27-
"Hello world! 👋"
26+
// options: RNMLKitFaceDetectorOptionsRecord,
27+
AsyncFunction("initialize") { promise: Promise ->
28+
log.d("initialize: Initializing Barcode Scanner")
29+
barcodeScanner = RNMLKitBarcodeScanner()
30+
log.d("initialize: Initializing Barcode Scanner")
31+
promise.resolve(null)
2832
}
2933

3034
// Defines a JavaScript function that always returns a Promise and whose native code
3135
// is by default dispatched on the different thread than the JavaScript runtime runs on.
32-
AsyncFunction("setValueAsync") { value: String ->
33-
// Send an event to JavaScript.
34-
sendEvent("onChange", mapOf(
35-
"value" to value
36-
))
37-
}
36+
AsyncFunction("process") { imagePath: String, promise: Promise ->
37+
val imageUri = Uri.parse(imagePath)
38+
39+
runBlocking suspend@{
40+
val barcodeScanner = barcodeScanner
41+
if (barcodeScanner == null) {
42+
handleException(promise, message = "Barcode Scanner not initialized")
43+
return@suspend
44+
}
45+
try {
46+
var result =
47+
barcodeScanner.detectInImage(imagePath, appContext).getOrElse {
48+
handleException(promise, it)
49+
throw it
50+
}
51+
promise.resolve(result.record)
52+
} catch (e: Exception) {
53+
handleException(promise, e)
54+
}
3855

39-
// Enables the module to be used as a native view. Definition components that are accepted as part of
40-
// the view definition: Prop, Events.
41-
View(RNMLKitBarcodeScanningView::class) {
42-
// Defines a setter for the `url` prop.
43-
Prop("url") { view: RNMLKitBarcodeScanningView, url: URL ->
44-
view.webView.loadUrl(url.toString())
4556
}
46-
// Defines an event that the view can send to JavaScript.
47-
Events("onLoad")
4857
}
4958
}
5059
}

modules/react-native-mlkit-barcode-scanning/expo-module.config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44
"modules": ["RNMLKitBarcodeScanningModule"]
55
},
66
"android": {
7-
"modules": ["red.infinite.reactnativemlkit.RNMLKitBarcodeScanningModule"]
7+
"modules": ["red.infinite.reactnativemlkit.barcodescanning.RNMLKitBarcodeScanningModule"]
88
}
99
}
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import { NativeModule, requireNativeModule } from 'expo';
1+
import { NativeModule, requireNativeModule } from "expo";
22

3-
import { BarcodeScannerResult } from './RNMLKitBarcodeScanning.types';
3+
import { BarcodeScannerResult } from "./RNMLKitBarcodeScanning.types";
44

55
declare class RNMLKitBarcodeScanningModule extends NativeModule {
6+
initialize(): Promise<void>;
67
process(imagePath: string): Promise<BarcodeScannerResult>;
78
}
89

910
// This call loads the native module object from the JSI.
10-
export default requireNativeModule<RNMLKitBarcodeScanningModule>('RNMLKitBarcodeScanning');
11+
export default requireNativeModule<RNMLKitBarcodeScanningModule>("RNMLKitBarcodeScanning");

0 commit comments

Comments
 (0)