This commit is contained in:
2026-09-20 23:06:27 +08:00
parent be8420e173
commit cec0027376
3 changed files with 85 additions and 20 deletions
+2 -2
View File
@@ -2,8 +2,8 @@
"name" : "柏悦物流 PDA 海外版", "name" : "柏悦物流 PDA 海外版",
"appid" : "__UNI__936D079", "appid" : "__UNI__936D079",
"description" : "柏悦物流海外仓 PDA 作业端", "description" : "柏悦物流海外仓 PDA 作业端",
"versionName" : "0.0.3", "versionName" : "0.0.4",
"versionCode" : 3, "versionCode" : 4,
"transformPx" : false, "transformPx" : false,
"app-plus" : { "app-plus" : {
"usingComponents" : true, "usingComponents" : true,
+36 -16
View File
@@ -1,5 +1,11 @@
export const SCAN_BROADCAST_ACTION = 'android.intent.action.SCANRESULT' export const SCAN_BROADCAST_ACTION = 'android.intent.action.SCANRESULT'
export const SCAN_BROADCAST_EXTRA = 'value' export const SCAN_BROADCAST_EXTRA = 'value'
export const SCAN_BROADCAST_ACTIONS = Object.freeze([
SCAN_BROADCAST_ACTION,
'com.android.server.scannerservice.broadcast',
'com.sunmi.scanner.ACTION_DATA_CODE_RECEIVED'
])
const SCAN_BROADCAST_EXTRAS = Object.freeze(['value', 'data', 'barcode', 'SCAN_BARCODE1'])
const DUPLICATE_WINDOW_MS = 80 const DUPLICATE_WINDOW_MS = 80
@@ -29,17 +35,23 @@ export function startScanBroadcast({ onScan, onError } = {}) {
try { try {
if (receiver) stopScanBroadcast() if (receiver) stopScanBroadcast()
activity = plus.android.runtimeMainActivity() const registeredActivity = plus.android.runtimeMainActivity()
const IntentFilter = plus.android.importClass('android.content.IntentFilter') const IntentFilter = plus.android.importClass('android.content.IntentFilter')
const filter = new IntentFilter() const filter = new IntentFilter()
filter.addAction(SCAN_BROADCAST_ACTION) SCAN_BROADCAST_ACTIONS.forEach((action) => filter.addAction(action))
receiver = plus.android.implements('io.dcloud.feature.internal.reflect.BroadcastReceiver', { const registeredReceiver = plus.android.implements('io.dcloud.feature.internal.reflect.BroadcastReceiver', {
onReceive(context, intent) { onReceive(context, intent) {
try { try {
plus.android.importClass(intent) plus.android.importClass(intent)
const rawValue = intent.getStringExtra(SCAN_BROADCAST_EXTRA) let rawValue = ''
for (const extra of SCAN_BROADCAST_EXTRAS) {
try {
const candidate = intent.getStringExtra(extra)
if (candidate !== null && candidate !== undefined && String(candidate).trim()) { rawValue = candidate; break }
} catch (_) {}
}
const value = normalizeScanValue(rawValue) const value = normalizeScanValue(rawValue)
console.info('[Overseas PDA broadcast] received', { action: intent.getAction(), extra: SCAN_BROADCAST_EXTRA, rawValue, value }) console.info('[Overseas PDA broadcast] received', { action: intent.getAction(), extras: SCAN_BROADCAST_EXTRAS, rawValue, value })
if (!value) { console.warn('[Overseas PDA broadcast] ignored empty value'); return } if (!value) { console.warn('[Overseas PDA broadcast] ignored empty value'); return }
if (isDuplicateScan(value)) { console.info('[Overseas PDA broadcast] ignored duplicate', { value }); return } if (isDuplicateScan(value)) { console.info('[Overseas PDA broadcast] ignored duplicate', { value }); return }
onScan(value) onScan(value)
@@ -53,23 +65,26 @@ export function startScanBroadcast({ onScan, onError } = {}) {
const sdkInt = Number(Build.VERSION.SDK_INT) const sdkInt = Number(Build.VERSION.SDK_INT)
if (Build.VERSION.SDK_INT >= 33) { if (Build.VERSION.SDK_INT >= 33) {
const Context = plus.android.importClass('android.content.Context') const Context = plus.android.importClass('android.content.Context')
activity.registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED) registeredActivity.registerReceiver(registeredReceiver, filter, Context.RECEIVER_EXPORTED)
} else { } else {
activity.registerReceiver(receiver, filter) registeredActivity.registerReceiver(registeredReceiver, filter)
} }
activity = registeredActivity
receiver = registeredReceiver
let packageName = '' let packageName = ''
try { packageName = String(activity.getPackageName()) } catch (_) {} try { packageName = String(registeredActivity.getPackageName()) } catch (_) {}
console.info('[Overseas PDA broadcast] receiver registered', { console.info('[Overseas PDA broadcast] receiver registered', {
action: SCAN_BROADCAST_ACTION, actions: SCAN_BROADCAST_ACTIONS,
extra: SCAN_BROADCAST_EXTRA, extras: SCAN_BROADCAST_EXTRAS,
packageName, packageName,
sdkInt, sdkInt,
exported: sdkInt >= 33 exported: sdkInt >= 33
}) })
return () => stopRegisteredBroadcast(registeredActivity, registeredReceiver)
} catch (error) { } catch (error) {
console.error('[Overseas PDA broadcast] registration failed', { console.error('[Overseas PDA broadcast] registration failed', {
action: SCAN_BROADCAST_ACTION, actions: SCAN_BROADCAST_ACTIONS,
extra: SCAN_BROADCAST_EXTRA, extras: SCAN_BROADCAST_EXTRAS,
message: error?.message || String(error) message: error?.message || String(error)
}) })
receiver = null receiver = null
@@ -81,15 +96,20 @@ export function startScanBroadcast({ onScan, onError } = {}) {
return stopScanBroadcast return stopScanBroadcast
} }
export function stopScanBroadcast() { function stopRegisteredBroadcast(registeredActivity, registeredReceiver) {
// #ifdef APP-PLUS // #ifdef APP-PLUS
if (activity && receiver) { if (registeredActivity && registeredReceiver) {
try { try {
activity.unregisterReceiver(receiver) registeredActivity.unregisterReceiver(registeredReceiver)
} catch (_) {} } catch (_) {}
} }
// #endif // #endif
if (activity !== registeredActivity || receiver !== registeredReceiver) return
receiver = null receiver = null
activity = null activity = null
resetScanDeduplication() resetScanDeduplication()
} }
export function stopScanBroadcast() {
stopRegisteredBroadcast(activity, receiver)
}
+47 -2
View File
@@ -4,15 +4,23 @@ import { readFile } from 'node:fs/promises'
import { import {
SCAN_BROADCAST_ACTION, SCAN_BROADCAST_ACTION,
SCAN_BROADCAST_ACTIONS,
SCAN_BROADCAST_EXTRA, SCAN_BROADCAST_EXTRA,
isDuplicateScan, isDuplicateScan,
normalizeScanValue, normalizeScanValue,
resetScanDeduplication resetScanDeduplication,
startScanBroadcast,
stopScanBroadcast
} from '../src/utils/scan-broadcast.js' } from '../src/utils/scan-broadcast.js'
test('scanner uses the confirmed PDA broadcast contract', () => { test('scanner uses the confirmed PDA broadcast contract', () => {
assert.equal(SCAN_BROADCAST_ACTION, 'android.intent.action.SCANRESULT') assert.equal(SCAN_BROADCAST_ACTION, 'android.intent.action.SCANRESULT')
assert.equal(SCAN_BROADCAST_EXTRA, 'value') assert.equal(SCAN_BROADCAST_EXTRA, 'value')
assert.deepEqual(SCAN_BROADCAST_ACTIONS, [
'android.intent.action.SCANRESULT',
'com.android.server.scannerservice.broadcast',
'com.sunmi.scanner.ACTION_DATA_CODE_RECEIVED'
])
assert.equal(normalizeScanValue(' EL-38201\r\n'), 'EL-38201') assert.equal(normalizeScanValue(' EL-38201\r\n'), 'EL-38201')
assert.equal(normalizeScanValue(null), '') assert.equal(normalizeScanValue(null), '')
}) })
@@ -25,7 +33,7 @@ test('scanner source keeps receiver diagnostics before page routing', async () =
assert.match(source, /receive failed/) assert.match(source, /receive failed/)
assert.match(source, /receiver registered/) assert.match(source, /receiver registered/)
assert.match(source, /registration failed/) assert.match(source, /registration failed/)
assert.match(source, /activity\.getPackageName\(\)/) assert.match(source, /registeredActivity\.getPackageName\(\)/)
}) })
test('scanner drops only immediate duplicate broadcasts', () => { test('scanner drops only immediate duplicate broadcasts', () => {
@@ -35,3 +43,40 @@ test('scanner drops only immediate duplicate broadcasts', () => {
assert.equal(isDuplicateScan('EL-38201', 1100), false) assert.equal(isDuplicateScan('EL-38201', 1100), false)
assert.equal(isDuplicateScan('EL-38202', 1110), false) assert.equal(isDuplicateScan('EL-38202', 1110), false)
}) })
test('an old page disposer cannot unregister the receiver owned by the newly shown page', () => {
const registered = []
const unregistered = []
class IntentFilter { addAction(action) { this.action = action } }
const activity = {
registerReceiver(value) { registered.push(value) },
unregisterReceiver(value) { unregistered.push(value) },
getPackageName() { return 'com.baiyuelogistics.pda.overseas' }
}
globalThis.plus = { android: {
runtimeMainActivity: () => activity,
importClass(name) {
if (name === 'android.content.IntentFilter') return IntentFilter
if (name === 'android.os.Build') return { VERSION: { SDK_INT: 32 } }
return name
},
implements: (_name, implementation) => implementation
} }
try {
const stopFirst = startScanBroadcast({ onScan() {} })
const first = registered[0]
const stopSecond = startScanBroadcast({ onScan() {} })
const second = registered[1]
assert.deepEqual(unregistered, [first])
stopFirst()
assert.deepEqual(unregistered, [first, first])
assert.ok(!unregistered.includes(second))
stopSecond()
assert.equal(unregistered.at(-1), second)
} finally {
stopScanBroadcast()
delete globalThis.plus
}
})