const { withDangerousMod } = require("@expo/config-plugins"); const fs = require("fs"); const path = require("path"); // const packageName = "net.alpla.lst.mobile"; // const packagePath = "com/alpla/lst/mobile"; const packageName = "net.alpla.lst.mobile"; const packagePath = "net/alpla/lst/mobile"; // const packageName = config.android?.package; // const packagePath = packageName.replace(/\./g, "/"); const moduleCode = `package ${packageName}.scanner import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter import android.os.Bundle import com.facebook.react.bridge.* import com.facebook.react.modules.core.DeviceEventManagerModule class ZebraScannerModule( private val reactContext: ReactApplicationContext ) : ReactContextBaseJavaModule(reactContext) { override fun getName(): String = "ZebraScanner" private val scanAction = "com.lst.mobile.SCAN" private var receiverRegistered = false private val scanReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { println("LST SCANNER: received intent -> \${intent?.action}") if (intent?.action != scanAction) { println("LST SCANNER: wrong action") return } val barcodeData: String? = intent.getStringExtra("com.symbol.datawedge.data_string") val labelType: String? = intent.getStringExtra("com.symbol.datawedge.label_type") val source: String? = intent.getStringExtra("com.symbol.datawedge.source") println("LST SCANNER: data=$barcodeData label=$labelType source=$source") if (barcodeData.isNullOrBlank()) { println("LST SCANNER: empty barcode") return } val payload = Arguments.createMap().apply { putString("data", barcodeData) putString("labelType", labelType) putString("source", source) putDouble("timestamp", System.currentTimeMillis().toDouble()) } sendEvent("barcodeScanned", payload) } } @ReactMethod fun startListening() { if (receiverRegistered) return reactContext.registerReceiver( scanReceiver, IntentFilter(scanAction), Context.RECEIVER_EXPORTED ) receiverRegistered = true } @ReactMethod fun stopListening() { if (!receiverRegistered) return try { reactContext.unregisterReceiver(scanReceiver) } catch (_: Exception) {} receiverRegistered = false } /** * Required for React Native NativeEventEmitter */ @ReactMethod fun addListener(eventName: String) { // No-op } /** * Required for React Native NativeEventEmitter */ @ReactMethod fun removeListeners(count: Int) { // No-op } @ReactMethod fun triggerScan() { val intent = Intent().apply { action = "com.symbol.datawedge.api.ACTION" putExtra("com.symbol.datawedge.api.SOFT_SCAN_TRIGGER", "TOGGLE_SCANNING") } reactContext.sendBroadcast(intent) } private fun sendCommand(command: String, value: Any) { val intent = Intent().apply { action = "com.symbol.datawedge.api.ACTION" when (value) { is String -> putExtra(command, value) is Bundle -> putExtra(command, value) } } reactContext.sendBroadcast(intent) } private fun sendEvent(eventName: String, payload: WritableMap) { reactContext .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) .emit(eventName, payload) } // @ReactMethod fun ensureProfile() { val profileName = "LST_MOBILE" // Create profile (safe to call even if it exists) sendCommand( "com.symbol.datawedge.api.CREATE_PROFILE", profileName ) Thread.sleep(500) // Configure profile val profileConfig = Bundle().apply { putString("PROFILE_NAME", profileName) putString("PROFILE_ENABLED", "true") putString("CONFIG_MODE", "CREATE_IF_NOT_EXIST") val barcodeConfig = Bundle().apply { putString("PLUGIN_NAME", "BARCODE") putString("RESET_CONFIG", "true") val props = Bundle().apply { putString("scanner_input_enabled", "true") putString("scanner_selection", "auto") putString("trigger_mode", "2") // 2 = HARD trigger only (recommended) wakes scanner up } putBundle("PARAM_LIST", props) } val intentConfig = Bundle().apply { putString("PLUGIN_NAME", "INTENT") putString("RESET_CONFIG", "true") val props = Bundle().apply { putString("intent_output_enabled", "true") putString("intent_action", scanAction) putString("intent_delivery", "2") // broadcast putString("intent_use_content_provider", "false") // optional but helps } putBundle("PARAM_LIST", props) } val keystrokeConfig = Bundle().apply { putString("PLUGIN_NAME", "KEYSTROKE") putString("RESET_CONFIG", "true") val props = Bundle().apply { putString("keystroke_output_enabled", "false") } putBundle("PARAM_LIST", props) } putParcelableArrayList( "PLUGIN_CONFIG", arrayListOf(barcodeConfig, intentConfig, keystrokeConfig) ) } sendCommand("com.symbol.datawedge.api.SET_CONFIG", profileConfig) // Associate with your app val appConfig = Bundle().apply { putString("PACKAGE_NAME", reactContext.packageName) putStringArray("ACTIVITY_LIST", arrayOf("*")) } val associateConfig = Bundle().apply { putString("PROFILE_NAME", profileName) putString("CONFIG_MODE", "UPDATE") putParcelableArray("APP_LIST", arrayOf(appConfig)) } sendCommand("com.symbol.datawedge.api.SET_CONFIG", associateConfig) } } `; const packageCode = `package ${packageName}.scanner import com.facebook.react.ReactPackage import com.facebook.react.bridge.NativeModule import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.uimanager.ViewManager class ZebraScannerPackage : ReactPackage { override fun createNativeModules( reactContext: ReactApplicationContext ): List { return listOf(ZebraScannerModule(reactContext)) } override fun createViewManagers( reactContext: ReactApplicationContext ): List> { return emptyList() } } `; function patchMainApplication(mainApplicationPath) { let contents = fs.readFileSync(mainApplicationPath, "utf8"); const importLine = `import ${packageName}.scanner.ZebraScannerPackage`; if (!contents.includes(importLine)) { contents = contents.replace( /import com\.facebook\.react\.PackageList/, `import com.facebook.react.PackageList\n${importLine}`, ); } if (!contents.includes("add(ZebraScannerPackage())")) { contents = contents.replace( /PackageList\(this\)\.packages\.apply\s*\{/, `PackageList(this).packages.apply {\n add(ZebraScannerPackage())`, ); } fs.writeFileSync(mainApplicationPath, contents); } module.exports = function withZebraScanner(config) { return withDangerousMod(config, [ "android", async (config) => { const androidRoot = config.modRequest.platformProjectRoot; const scannerDir = path.join( androidRoot, "app/src/main/java", packagePath, "scanner", ); fs.mkdirSync(scannerDir, { recursive: true }); fs.writeFileSync( path.join(scannerDir, "ZebraScannerModule.kt"), moduleCode, ); fs.writeFileSync( path.join(scannerDir, "ZebraScannerPackage.kt"), packageCode, ); const mainApplicationPath = path.join( androidRoot, "app/src/main/java", packagePath, "MainApplication.kt", ); patchMainApplication(mainApplicationPath); return config; }, ]); };