import Activity from 'android.app.Activity' import Intent from 'android.content.Intent' import { OpenCodeScan, CodeScanErrorCode, CodeScanOptions, CodeScanSuccess } from '../interface.uts' import { CodeScanFailImpl } from '../unierror.uts' const LAUNCH_REQ: Int = 53011 const EXTRA_PAYLOAD = 'mas_content' const EXTRA_FORMAT = 'mas_format' const EXTRA_TITLE = 'mas_title' const EXTRA_HINT = 'mas_hint' const EXTRA_SUCCESS_HINT = 'mas_success_hint' const EXTRA_EMPTY_HINT = 'mas_empty_hint' const EXTRA_SMART_ZOOM = 'mas_smart_zoom' const EXTRA_TORCH_MODE = 'mas_torch_mode' const EXTRA_RESULT_MODE = 'mas_result_mode' const EXTRA_SHOW_PAUSE_BUTTON = 'mas_show_pause_button' const EXTRA_VIBRATE = 'mas_vibrate' const EXTRA_CONTINUOUS = 'mas_continuous' const EXTRA_FROM_ALBUM = 'mas_from_album' const EXTRA_FORMATS = 'mas_formats' const EXTRA_SCAN_REGION_ENABLED = 'mas_scan_region_enabled' const EXTRA_SCAN_REGION = 'mas_scan_region' const EXTRA_SCAN_STYLE_SHOW_MASK = 'mas_scan_style_show_mask' const EXTRA_SCAN_STYLE_SHOW_BEAM = 'mas_scan_style_show_beam' const EXTRA_SCAN_STYLE_BORDER_COLOR = 'mas_scan_style_border_color' const EXTRA_SCAN_STYLE_MASK_COLOR = 'mas_scan_style_mask_color' const EXTRA_SCAN_STYLE_BEAM_COLOR = 'mas_scan_style_beam_color' const EXTRA_SCAN_STYLE_BEAM_DURATION = 'mas_scan_style_beam_duration' const EXTRA_SCAN_STYLE_CORNER = 'mas_scan_style_corner' const EXTRA_SCAN_STYLE_CORNER_RADIUS = 'mas_scan_style_corner_radius' const EXTRA_SCAN_TIMEOUT = 'mas_scan_timeout' const EXTRA_CONTINUOUS_SUCCESS_DURATION = 'mas_continuous_success_duration' const EXTRA_ERR = 'mas_err' const EXTRA_FROM_ALBUM_RESULT = 'mas_result_from_album' const EXTRA_HAD_HITS = 'mas_had_hits' const CAPTURE_ACTIVITY = 'uts.sdk.modules.masCodeScan.CodeCaptureActivity' let activityResultHook: ((requestCode: Int, resultCode: Int, data?: Intent) => void) | null = null let activeOptions: CodeScanOptions | null = null let continuousHitCount: Int = 0 let pollTimerId: number = -1 let sessionContinuous: boolean = false let continuousSuccess: ((res: CodeScanSuccess) => void) | null = null @UTSJS.keepAlive export function openCodeScan(options?: CodeScanOptions | null): void { const opts = options != null ? options : null const host = UTSAndroid.getUniActivity() if (host == null) { emitFailure(opts, 9021001) return } launchCapture(host, opts) } function clearPollTimer() { if (pollTimerId >= 0) { clearInterval(pollTimerId) pollTimerId = -1 } } function drainHits() { const current = activeOptions const success = continuousSuccess if (current == null || success == null) return while (true) { const hit = CodeScanHitBus.pollHit() if (hit == null) break const content = hit[0] const format = hit[1] const fromAlbumFlag = hit[2] if (content == null || format == null || fromAlbumFlag == null) continue continuousHitCount++ const ok: CodeScanSuccess = { content: content, format: format, fromAlbum: fromAlbumFlag == '1' } success(ok) } } function launchCapture(host: Activity, opts: CodeScanOptions | null): void { if (activityResultHook != null) { UTSAndroid.offAppActivityResult(activityResultHook) activityResultHook = null } clearPollTimer() CodeScanHitBus.detach() activeOptions = opts continuousHitCount = 0 sessionContinuous = opts != null && opts.continuous == true continuousSuccess = sessionContinuous && opts != null ? opts.success : null if (sessionContinuous) { pollTimerId = setInterval(() => { drainHits() }, 120) } const onResult = (requestCode: Int, resultCode: Int, data?: Intent) => { if (requestCode != LAUNCH_REQ) { return } UTSAndroid.offAppActivityResult() activityResultHook = null drainHits() clearPollTimer() CodeScanHitBus.detach() const current = activeOptions const wasContinuous = sessionContinuous sessionContinuous = false if (resultCode != Activity.RESULT_OK) { let errCode: CodeScanErrorCode = 9021003 if (data != null) { const nativeErr = data.getIntExtra(EXTRA_ERR, 0) if (nativeErr >= 9021002 && nativeErr <= 9021011) { errCode = nativeErr as CodeScanErrorCode } } // 连续扫已有命中:关闭只 complete,不 fail if (wasContinuous && continuousHitCount > 0) { if (current != null) { current.complete?.({ closed: true, hitCount: continuousHitCount }) } activeOptions = null continuousSuccess = null return } if (wasContinuous && data != null && data.getBooleanExtra(EXTRA_HAD_HITS, false)) { if (current != null) { current.complete?.({ closed: true, hitCount: continuousHitCount }) } activeOptions = null continuousSuccess = null return } if (wasContinuous && current != null) { emitFailure(current, errCode) const complete = current.complete if (complete != null) { complete({ closed: true, hitCount: continuousHitCount, failed: true, errCode: errCode }) } activeOptions = null continuousSuccess = null return } emitFailure(current, errCode) activeOptions = null continuousSuccess = null return } if (wasContinuous) { if (current != null) { current.complete?.({ closed: true, hitCount: continuousHitCount }) } activeOptions = null continuousSuccess = null return } let payload = '' let formatName = '' let fromAlbum = false if (data != null) { const rawPayload = data.getStringExtra(EXTRA_PAYLOAD) const rawFormat = data.getStringExtra(EXTRA_FORMAT) payload = rawPayload != null ? rawPayload : '' formatName = rawFormat != null ? rawFormat : '' fromAlbum = data.getBooleanExtra(EXTRA_FROM_ALBUM_RESULT, false) } if (payload == '') { emitFailure(current, 9021004) activeOptions = null continuousSuccess = null return } const ok: CodeScanSuccess = { content: payload, format: formatName, fromAlbum: fromAlbum } if (current != null) { current.success?.(ok) current.complete?.(ok) } activeOptions = null continuousSuccess = null } activityResultHook = onResult UTSAndroid.onAppActivityResult(onResult) let pageTitle = '扫一扫' let scanHint = '请将条码放入扫描框内' let successHint = '扫码成功' let emptyHint = '未识别到有效条码' let useSmartZoom = true let useTorchMode = 'off' let useResultMode = 'immediate' let useShowPauseButton = false let useVibrate = true let useContinuous = false let useFromAlbum = false let useScanRegion = false let regionWidth = 0.74 let regionHeight = 0.74 let regionCenterX = 0.5 let regionCenterY = 0.5 let formatsCsv = '' let styleShowMask = true let styleShowBeam = true let styleBorderColor = '#19be6b' let styleMaskColor = 'rgba(0, 0, 0, 0.45)' let styleBeamColor = '#12e880' let styleBeamDuration = 1800 let styleCorner = 'corner' let styleCornerRadius = 20 let scanTimeout = 0 let continuousSuccessDuration = 1000 if (opts != null) { if (opts.title != null && opts.title!.trim().length > 0) { pageTitle = opts.title!.trim() } if (opts.hint != null && opts.hint!.trim().length > 0) { scanHint = opts.hint!.trim() } if (opts.successHint != null && opts.successHint!.trim().length > 0) { successHint = opts.successHint!.trim() } if (opts.emptyHint != null && opts.emptyHint!.trim().length > 0) { emptyHint = opts.emptyHint!.trim() } if (opts.smartZoom != null) { useSmartZoom = opts.smartZoom! } if (opts.torch == 'on' || opts.torch == 'auto' || opts.torch == 'off') { useTorchMode = opts.torch! } if (opts.resultMode == 'confirm' || opts.resultMode == 'pause' || opts.resultMode == 'immediate') { useResultMode = opts.resultMode! } if (opts.showPauseButton != null) { useShowPauseButton = opts.showPauseButton! } if (opts.vibrate != null) { useVibrate = opts.vibrate! } if (opts.continuous != null) { useContinuous = opts.continuous! } if (opts.fromAlbum != null) { useFromAlbum = opts.fromAlbum! } if (opts.scanRegion != null) { useScanRegion = opts.scanRegion!.fullScreen != true if (opts.scanRegion!.width != null) { regionWidth = opts.scanRegion!.width! } if (opts.scanRegion!.height != null) { regionHeight = opts.scanRegion!.height! } if (opts.scanRegion!.centerX != null) { regionCenterX = opts.scanRegion!.centerX! } if (opts.scanRegion!.centerY != null) { regionCenterY = opts.scanRegion!.centerY! } } if (opts.scanTimeout != null) { scanTimeout = Math.floor(opts.scanTimeout!) } if (opts.continuousSuccessDuration != null) { continuousSuccessDuration = Math.max(0, Math.min(600000, Math.floor(opts.continuousSuccessDuration!))) } if (opts.scanStyle != null) { if (opts.scanStyle!.showMask != null) { styleShowMask = opts.scanStyle!.showMask! } if (opts.scanStyle!.showBeam != null) { styleShowBeam = opts.scanStyle!.showBeam! } if (opts.scanStyle!.borderColor != null) { styleBorderColor = opts.scanStyle!.borderColor! } if (opts.scanStyle!.maskColor != null) { styleMaskColor = opts.scanStyle!.maskColor! } if (opts.scanStyle!.beamColor != null) { styleBeamColor = opts.scanStyle!.beamColor! } if (opts.scanStyle!.beamDuration != null) { styleBeamDuration = opts.scanStyle!.beamDuration! } if (opts.scanStyle!.cornerStyle == 'full' || opts.scanStyle!.cornerStyle == 'corner' || opts.scanStyle!.cornerStyle == 'rounded' || opts.scanStyle!.cornerStyle == 'rounded-corner') { styleCorner = opts.scanStyle!.cornerStyle! } if (opts.scanStyle!.cornerRadius != null) { styleCornerRadius = Math.max(0, Math.min(160, Math.floor(opts.scanStyle!.cornerRadius!))) } } if (opts.formats != null && opts.formats!.length > 0) { const parts: string[] = [] for (let i = 0; i < opts.formats!.length; i++) { const item = opts.formats![i] if (item != null && item.length > 0) { parts.push(item) } } formatsCsv = parts.join(',') } } const intent = new Intent() intent.setClassName(host, CAPTURE_ACTIVITY) intent.putExtra(EXTRA_TITLE, pageTitle) intent.putExtra(EXTRA_HINT, scanHint) intent.putExtra(EXTRA_SUCCESS_HINT, successHint) intent.putExtra(EXTRA_EMPTY_HINT, emptyHint) intent.putExtra(EXTRA_SMART_ZOOM, useSmartZoom) intent.putExtra(EXTRA_TORCH_MODE, useTorchMode) intent.putExtra(EXTRA_RESULT_MODE, useResultMode) intent.putExtra(EXTRA_SHOW_PAUSE_BUTTON, useShowPauseButton) intent.putExtra(EXTRA_VIBRATE, useVibrate) intent.putExtra(EXTRA_CONTINUOUS, useContinuous) intent.putExtra(EXTRA_FROM_ALBUM, useFromAlbum) intent.putExtra(EXTRA_FORMATS, formatsCsv) intent.putExtra(EXTRA_SCAN_REGION_ENABLED, useScanRegion) intent.putExtra(EXTRA_SCAN_REGION, `${regionWidth},${regionHeight},${regionCenterX},${regionCenterY}`) intent.putExtra(EXTRA_SCAN_STYLE_SHOW_MASK, styleShowMask) intent.putExtra(EXTRA_SCAN_STYLE_SHOW_BEAM, styleShowBeam) intent.putExtra(EXTRA_SCAN_STYLE_BORDER_COLOR, styleBorderColor) intent.putExtra(EXTRA_SCAN_STYLE_MASK_COLOR, styleMaskColor) intent.putExtra(EXTRA_SCAN_STYLE_BEAM_COLOR, styleBeamColor) intent.putExtra(EXTRA_SCAN_STYLE_BEAM_DURATION, styleBeamDuration) intent.putExtra(EXTRA_SCAN_STYLE_CORNER, styleCorner) intent.putExtra(EXTRA_SCAN_STYLE_CORNER_RADIUS, styleCornerRadius) intent.putExtra(EXTRA_SCAN_TIMEOUT, scanTimeout) intent.putExtra(EXTRA_CONTINUOUS_SUCCESS_DURATION, continuousSuccessDuration) host.startActivityForResult(intent, LAUNCH_REQ) } function emitFailure(opts: CodeScanOptions | null, code: CodeScanErrorCode): void { const err = new CodeScanFailImpl(code) if (opts != null) { opts.fail?.(err) opts.complete?.(err) } }