diff --git a/apps/ExampleApp/app/screens/BarcodeScanningScreen.tsx b/apps/ExampleApp/app/screens/BarcodeScanningScreen.tsx index caa18d4e..1060212a 100644 --- a/apps/ExampleApp/app/screens/BarcodeScanningScreen.tsx +++ b/apps/ExampleApp/app/screens/BarcodeScanningScreen.tsx @@ -6,7 +6,7 @@ import { AppStackScreenProps } from "../navigators" import { Text, Icon, ImageSelector, Screen } from "../components" import { useTypedNavigation } from "../navigators/useTypedNavigation" -import RNMLKitBarcodeScanningModule from "@infinitered/react-native-mlkit-barcode-scanning" +import RNMLKitBarcodeScanningModule from "@infinitered/react-native-mlkit-barcode-scanning" import type { BarcodeScannerResult } from "@infinitered/react-native-mlkit-barcode-scanning" import { UseExampleImageStatus, SelectedImage } from "../utils/useExampleImage" @@ -60,6 +60,7 @@ export const BarcodeScanningScreen: FC = observer( if (!image?.uri) return setIsScanning(true) try { + // await RNMLKitBarcodeScanningModule.initialize() const scanResult = await RNMLKitBarcodeScanningModule.process(image.uri) setResult(scanResult) setStatus("done") diff --git a/modules/react-native-mlkit-barcode-scanning/android/build.gradle b/modules/react-native-mlkit-barcode-scanning/android/build.gradle index 4198bf94..68340ee8 100644 --- a/modules/react-native-mlkit-barcode-scanning/android/build.gradle +++ b/modules/react-native-mlkit-barcode-scanning/android/build.gradle @@ -21,6 +21,15 @@ if (useManagedAndroidSdkVersions) { ext.safeExtGet = { prop, fallback -> rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback } + + // Ensures backward compatibility + ext.getKotlinVersion = { + if (ext.has("kotlinVersion")) { + ext.kotlinVersion() + } else { + ext.safeExtGet("kotlinVersion", "1.8.10") + } + } } project.android { compileSdkVersion safeExtGet("compileSdkVersion", 36) diff --git a/modules/react-native-mlkit-barcode-scanning/android/src/main/java/expo/modules/mlkitbarcodescanning/RNMLKitBarcodeScanner.kt b/modules/react-native-mlkit-barcode-scanning/android/src/main/java/expo/modules/mlkitbarcodescanning/RNMLKitBarcodeScanner.kt new file mode 100644 index 00000000..66d90e2e --- /dev/null +++ b/modules/react-native-mlkit-barcode-scanning/android/src/main/java/expo/modules/mlkitbarcodescanning/RNMLKitBarcodeScanner.kt @@ -0,0 +1,38 @@ +import android.net.Uri +import com.google.mlkit.vision.common.InputImage +import com.google.mlkit.vision.barcode.BarcodeScanning +import com.google.mlkit.vision.barcode.BarcodeScanner +import expo.modules.kotlin.AppContext +import kotlinx.coroutines.CompletableDeferred +import red.infinite.reactnativemlkit.core.RNMLKitImage +import red.infinite.reactnativemlkit.barcodescanning.RNMLKitBarcodeScannerResult + +// private var options: RNMLKitBarcodeScannerOptions +class RNMLKitBarcodeScanner() { + + private var barcodeScanner: BarcodeScanner? = BarcodeScanning.getClient() + + suspend fun detectInImage(imagePath: String, appContext: AppContext): Result { + val result = CompletableDeferred>() + + try { + if(appContext.reactContext == null) { + val exception = Exception("RNMLKitBarcodeScanner: React Context is null") + result.complete(Result.failure(exception)) + return result.await() + } + + var image: InputImage = RNMLKitImage(Uri.parse(imagePath), appContext.reactContext!!).image + + barcodeScanner?.process(image)?.addOnSuccessListener { barcodes -> + val expoBarcodes = RNMLKitBarcodeScannerResult(barcodes, imagePath) + result.complete(Result.success(expoBarcodes)) + }?.addOnFailureListener { e -> + result.complete(Result.failure(e)) + } + } catch (e: Exception) { + result.complete(Result.failure(e)) + } + return result.await() + } +} diff --git a/modules/react-native-mlkit-barcode-scanning/android/src/main/java/expo/modules/mlkitbarcodescanning/RNMLKitBarcodeScannerResult.kt b/modules/react-native-mlkit-barcode-scanning/android/src/main/java/expo/modules/mlkitbarcodescanning/RNMLKitBarcodeScannerResult.kt new file mode 100644 index 00000000..bb230240 --- /dev/null +++ b/modules/react-native-mlkit-barcode-scanning/android/src/main/java/expo/modules/mlkitbarcodescanning/RNMLKitBarcodeScannerResult.kt @@ -0,0 +1,31 @@ +package red.infinite.reactnativemlkit.barcodescanning + +import expo.modules.kotlin.records.Record +import expo.modules.kotlin.records.Field +import com.google.mlkit.vision.barcode.common.Barcode +import red.infinite.reactnativemlkit.core.RNMLKitRect + +data class RNMLKitBarcodeScannerResultRecord( + @Field var barcodes: List = mutableListOf(), + @Field var imagePath: String +) : Record + +class RNMLKitBarcodeScannerResult(private val barcodes: List, private val imagePath: String) { + val record: RNMLKitBarcodeScannerResultRecord + get() = RNMLKitBarcodeScannerResultRecord( + barcodes = barcodes.map { barcode -> + RNMLKitBarcode().apply { + //frame = RNMLKitRect.fromRect(barcode.boundingBox ?? RNMLKitRect.zero()) + rawValue = barcode.rawValue + displayValue = barcode.displayValue + } + }, + imagePath = imagePath + ) +} + +data class RNMLKitBarcode( + @Field var frame: RNMLKitRect = RNMLKitRect.zero(), + @Field var rawValue: String? = null, + @Field var displayValue: String? = null +) : Record diff --git a/modules/react-native-mlkit-barcode-scanning/android/src/main/java/expo/modules/mlkitbarcodescanning/RNMLKitBarcodeScanningModule.kt b/modules/react-native-mlkit-barcode-scanning/android/src/main/java/expo/modules/mlkitbarcodescanning/RNMLKitBarcodeScanningModule.kt index 844a7c4e..3825f50a 100644 --- a/modules/react-native-mlkit-barcode-scanning/android/src/main/java/expo/modules/mlkitbarcodescanning/RNMLKitBarcodeScanningModule.kt +++ b/modules/react-native-mlkit-barcode-scanning/android/src/main/java/expo/modules/mlkitbarcodescanning/RNMLKitBarcodeScanningModule.kt @@ -1,10 +1,18 @@ -package red.infinite.reactnativemlkit +package red.infinite.reactnativemlkit.barcodescanning +import RNMLKitBarcodeScanner + +import android.net.Uri + +import expo.modules.kotlin.Promise +import expo.modules.kotlin.exception.CodedException import expo.modules.kotlin.modules.Module import expo.modules.kotlin.modules.ModuleDefinition -import java.net.URL +import kotlinx.coroutines.runBlocking +import red.infinite.reactnativemlkit.core.RNMLKitModule -class RNMLKitBarcodeScanningModule : Module() { +class RNMLKitBarcodeScanningModule : RNMLKitModule("RNMLKitBarcodeScanning") { + private var barcodeScanner: RNMLKitBarcodeScanner? = null // Each module class must implement the definition function. The definition consists of components // that describes the module's functionality and behavior. // See https://docs.expo.dev/modules/module-api for more details about available components. @@ -14,37 +22,38 @@ class RNMLKitBarcodeScanningModule : Module() { // The module will be accessible from `requireNativeModule('RNMLKitBarcodeScanning')` in JavaScript. Name("RNMLKitBarcodeScanning") - // Defines constant property on the module. - Constant("PI") { - Math.PI - } - - // Defines event names that the module can send to JavaScript. - Events("onChange") - // Defines a JavaScript synchronous function that runs the native code on the JavaScript thread. - Function("hello") { - "Hello world! 👋" + // options: RNMLKitFaceDetectorOptionsRecord, + AsyncFunction("initialize") { promise: Promise -> + log.d("initialize: Initializing Barcode Scanner") + barcodeScanner = RNMLKitBarcodeScanner() + log.d("initialize: Initializing Barcode Scanner") + promise.resolve(null) } // Defines a JavaScript function that always returns a Promise and whose native code // is by default dispatched on the different thread than the JavaScript runtime runs on. - AsyncFunction("setValueAsync") { value: String -> - // Send an event to JavaScript. - sendEvent("onChange", mapOf( - "value" to value - )) - } + AsyncFunction("process") { imagePath: String, promise: Promise -> + val imageUri = Uri.parse(imagePath) + + runBlocking suspend@{ + val barcodeScanner = barcodeScanner + if (barcodeScanner == null) { + handleException(promise, message = "Barcode Scanner not initialized") + return@suspend + } + try { + var result = + barcodeScanner.detectInImage(imagePath, appContext).getOrElse { + handleException(promise, it) + throw it + } + promise.resolve(result.record) + } catch (e: Exception) { + handleException(promise, e) + } - // Enables the module to be used as a native view. Definition components that are accepted as part of - // the view definition: Prop, Events. - View(RNMLKitBarcodeScanningView::class) { - // Defines a setter for the `url` prop. - Prop("url") { view: RNMLKitBarcodeScanningView, url: URL -> - view.webView.loadUrl(url.toString()) } - // Defines an event that the view can send to JavaScript. - Events("onLoad") } } } diff --git a/modules/react-native-mlkit-barcode-scanning/expo-module.config.json b/modules/react-native-mlkit-barcode-scanning/expo-module.config.json index fae1de7c..7bcb8bf3 100644 --- a/modules/react-native-mlkit-barcode-scanning/expo-module.config.json +++ b/modules/react-native-mlkit-barcode-scanning/expo-module.config.json @@ -4,6 +4,6 @@ "modules": ["RNMLKitBarcodeScanningModule"] }, "android": { - "modules": ["red.infinite.reactnativemlkit.RNMLKitBarcodeScanningModule"] + "modules": ["red.infinite.reactnativemlkit.barcodescanning.RNMLKitBarcodeScanningModule"] } } diff --git a/modules/react-native-mlkit-barcode-scanning/src/RNMLKitBarcodeScanningModule.ts b/modules/react-native-mlkit-barcode-scanning/src/RNMLKitBarcodeScanningModule.ts index 281534be..d9f6ee81 100644 --- a/modules/react-native-mlkit-barcode-scanning/src/RNMLKitBarcodeScanningModule.ts +++ b/modules/react-native-mlkit-barcode-scanning/src/RNMLKitBarcodeScanningModule.ts @@ -1,10 +1,11 @@ -import { NativeModule, requireNativeModule } from 'expo'; +import { NativeModule, requireNativeModule } from "expo"; -import { BarcodeScannerResult } from './RNMLKitBarcodeScanning.types'; +import { BarcodeScannerResult } from "./RNMLKitBarcodeScanning.types"; declare class RNMLKitBarcodeScanningModule extends NativeModule { + initialize(): Promise; process(imagePath: string): Promise; } // This call loads the native module object from the JSI. -export default requireNativeModule('RNMLKitBarcodeScanning'); +export default requireNativeModule("RNMLKitBarcodeScanning");