feat(intial auth): intial auth setup for the scanner
Some checks failed
Build and Push LST Docker Image / docker (push) Has been cancelled

This commit is contained in:
2026-05-05 19:48:36 -05:00
parent 4e0cf8c54c
commit cd13360cfb
46 changed files with 7847 additions and 206 deletions

View File

@@ -0,0 +1,317 @@
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"
sendCommand(
"com.symbol.datawedge.api.CREATE_PROFILE",
profileName
)
Thread.sleep(500)
val barcodeConfig = Bundle().apply {
putString("PLUGIN_NAME", "BARCODE")
putString("RESET_CONFIG", "true")
val props = Bundle().apply {
putString("scanner_input_enabled", "true")
// Auto-select internal scanner
putString("scanner_selection", "auto")
putString("scanner_selection_by_identifier", "AUTO")
// Hardware trigger behavior
putString("hardware_trigger_enabled", "true")
putString("trigger_mode", "2") // 2 = HARD trigger
// Disable Zebra's loud initial decode feedback
putString("decode_audio_feedback_uri", "")
putString("decode_haptic_feedback", "false")
putString("decode_led_feedback", "false")
// add in wake on trigger
putString("trigger_wakeup_scan", "true");
}
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")
}
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)
}
val profileConfig = Bundle().apply {
putString("PROFILE_NAME", profileName)
putString("PROFILE_ENABLED", "true")
putString("CONFIG_MODE", "CREATE_IF_NOT_EXIST")
putParcelableArrayList(
"PLUGIN_CONFIG",
arrayListOf(barcodeConfig, intentConfig, keystrokeConfig)
)
}
sendCommand("com.symbol.datawedge.api.SET_CONFIG", profileConfig)
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)
// Runtime nudge: make sure scanner input is enabled for the active profile
sendCommand(
"com.symbol.datawedge.api.SCANNER_INPUT_PLUGIN",
"ENABLE_PLUGIN"
)
}
}
`;
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<NativeModule> {
return listOf(ZebraScannerModule(reactContext))
}
override fun createViewManagers(
reactContext: ReactApplicationContext
): List<ViewManager<*, *>> {
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;
},
]);
};