821 lines
26 KiB
Plaintext
821 lines
26 KiB
Plaintext
import ListView from 'android.widget.ListView';
|
|
import FrameLayout from 'android.widget.FrameLayout';
|
|
import CameraManager from 'android.hardware.camera2.CameraManager'
|
|
import CameraDevice from 'android.hardware.camera2.CameraDevice'
|
|
import StateCallback from 'android.hardware.camera2.CameraDevice.StateCallback'
|
|
import CameraCaptureSession from 'android.hardware.camera2.CameraCaptureSession'
|
|
|
|
import CaptureRequest from 'android.hardware.camera2.CaptureRequest'
|
|
import TotalCaptureResult from 'android.hardware.camera2.TotalCaptureResult'
|
|
import StreamConfigurationMap from 'android.hardware.camera2.params.StreamConfigurationMap'
|
|
import MeteringRectangle from 'android.hardware.camera2.params.MeteringRectangle'
|
|
import OutputConfiguration from 'android.hardware.camera2.params.OutputConfiguration'
|
|
import SessionConfiguration from 'android.hardware.camera2.params.SessionConfiguration'
|
|
import CameraCharacteristics from 'android.hardware.camera2.CameraCharacteristics'
|
|
import CameraMetadata from 'android.hardware.camera2.CameraMetadata'
|
|
import Range from 'android.util.Range';
|
|
|
|
import Executor from 'java.util.concurrent.Executor'
|
|
import Looper from 'android.os.Looper'
|
|
import ImageFormat from 'android.graphics.ImageFormat'
|
|
import Size from 'android.util.Size'
|
|
import SurfaceTexture from 'android.graphics.SurfaceTexture'
|
|
import Handler from 'android.os.Handler'
|
|
import Surface from 'android.view.Surface'
|
|
import Image from 'android.media.Image'
|
|
import LinearLayout from 'android.widget.LinearLayout';
|
|
import Intent from 'android.content.Intent';
|
|
import Integer from 'java.lang.Integer';
|
|
import ByteOrder from 'java.nio.ByteOrder';
|
|
|
|
import SurfaceHolder from 'android.view.SurfaceHolder'
|
|
import SurfaceView from 'android.view.SurfaceView'
|
|
import TextureView from 'android.view.TextureView'
|
|
import View from 'android.view.View'
|
|
import Context from 'android.content.Context'
|
|
import Manifest from "android.Manifest";
|
|
import Uri from 'android.net.Uri';
|
|
import File from 'java.io.File'
|
|
import System from 'java.lang.System'
|
|
import Activity from 'android.app.Activity';
|
|
import Bundle from 'android.os.Bundle';
|
|
import Intent from 'android.content.Intent';
|
|
import IntentFilter from 'android.content.IntentFilter';
|
|
|
|
import Rect from 'android.graphics.Rect';
|
|
import Matrix from 'android.graphics.Matrix'
|
|
import ImageReader from 'android.media.ImageReader'
|
|
import OnImageAvailableListener from 'android.media.ImageReader.OnImageAvailableListener'
|
|
|
|
|
|
import ByteBuffer from 'java.nio.ByteBuffer'
|
|
import MediaStore from 'android.provider.MediaStore'
|
|
import Plane from 'android.media.Image.Plane'
|
|
import Bitmap from "android.graphics.Bitmap"
|
|
import BitmapFactory from "android.graphics.BitmapFactory"
|
|
import Vibrator from "android.os.Vibrator"
|
|
import Animation from "android.view.animation.Animation"
|
|
import ScaleAnimation from "android.view.animation.ScaleAnimation"
|
|
import AnimationUtils from "android.view.animation.AnimationUtils"
|
|
|
|
|
|
import Color from "android.graphics.Color"
|
|
import Window from "android.view.Window"
|
|
import WindowManager from "android.view.WindowManager"
|
|
import TextView from "android.widget.TextView"
|
|
import RelativeLayout from "android.widget.RelativeLayout"
|
|
import ImageView from "android.widget.ImageView"
|
|
import HandlerThread from 'android.os.HandlerThread'
|
|
import YuvImage from 'android.graphics.YuvImage'
|
|
import ByteArrayOutputStream from 'java.io.ByteArrayOutputStream'
|
|
import GradientDrawable from 'android.graphics.drawable.GradientDrawable'
|
|
import MotionEvent from 'android.view.MotionEvent';
|
|
import ViewGroup from 'android.view.ViewGroup';
|
|
|
|
|
|
import IOException from 'java.io.IOException'
|
|
import InputStream from 'java.io.InputStream'
|
|
import BarcodeScanner from "com.google.mlkit.vision.barcode.BarcodeScanner"
|
|
import BarcodeScannerOptions from "com.google.mlkit.vision.barcode.BarcodeScannerOptions"
|
|
import BarcodeScanning from "com.google.mlkit.vision.barcode.BarcodeScanning"
|
|
import ZoomSuggestionOptions from "com.google.mlkit.vision.barcode.ZoomSuggestionOptions"
|
|
import ZoomCallback from "com.google.mlkit.vision.barcode.ZoomSuggestionOptions.ZoomCallback"
|
|
import Barcode from "com.google.mlkit.vision.barcode.common.Barcode"
|
|
import InputImage from "com.google.mlkit.vision.common.InputImage"
|
|
import xView from "./libview/view.uts"
|
|
import Task from "com.google.android.gms.tasks.Task"
|
|
import List from "java.util.List"
|
|
type buffoptsPlanesType = {
|
|
data : ByteBuffer,
|
|
getRowStride : number,
|
|
getPixelStride : number,
|
|
}
|
|
type buffoptsType = {
|
|
width : number,
|
|
height : number,
|
|
format : number,
|
|
rect : Rect,
|
|
planes : buffoptsPlanesType[],
|
|
}
|
|
|
|
import { SCANNING_OPTIONS, CAMERA_PHOTO_SIZE, CallFunType, SCANNING_PHOTO_RESULT, SCANNING_PHOTO_RESULT_BOUND } from "../interface.uts"
|
|
import { decoderUriPathToQr, decoderBuffToQr } from "./index.uts"
|
|
|
|
type cameraListNameType = { id : string, device : CameraCharacteristics }
|
|
|
|
function px2dp(n : number) : number {
|
|
const mets = UTSAndroid.getAppContext()!.resources!.getDisplayMetrics()
|
|
return mets.density * n
|
|
}
|
|
|
|
export class xCamera {
|
|
parentView : FrameLayout;
|
|
// 相机区域视图
|
|
cameraView : SurfaceView | null = null;
|
|
// 识别点的区域
|
|
resultView : RelativeLayout | null = null;
|
|
|
|
|
|
viewBox_width = 0
|
|
viewBox_height = 0
|
|
|
|
imgPointsBycanmarea = [] as RelativeLayout[]
|
|
|
|
options = {
|
|
width: 800,
|
|
height: 800,
|
|
cameraDevice: 'BACK',
|
|
scanSpeedduration: 450,
|
|
scanningQrCode: true,
|
|
isSavePhoto: false,
|
|
qrCodeType: null,
|
|
vibatar: true,
|
|
zoom: 3,
|
|
continuousScan: false
|
|
} as SCANNING_OPTIONS
|
|
|
|
/** 手动震动服务 */
|
|
vibrator = null as null | Vibrator
|
|
context : Context;
|
|
// 相机旋转的角度。
|
|
cameraRate = new Map<Number, Number>([
|
|
[Surface.ROTATION_0, 0],
|
|
[Surface.ROTATION_90, 0],
|
|
[Surface.ROTATION_180, 0],
|
|
[Surface.ROTATION_270, 0]
|
|
])
|
|
// 相机服务
|
|
cameraService : CameraManager
|
|
// 相机列表,name,id
|
|
cameraListName = new Map<string, cameraListNameType>();
|
|
//
|
|
cameraPreviewSize = { width: 800, height: 800 } as CAMERA_PHOTO_SIZE
|
|
// 当前预览的什么是设备
|
|
cameraDevice : CameraDevice | null = null
|
|
cameraDeviceCaptureSession : CameraCaptureSession | null = null
|
|
sufaceHolderView : Surface | null = null
|
|
// 当前是否在解析码制中。
|
|
isDecoderQring = false
|
|
// 当前相机是否正在预览中。
|
|
isOpeningCameraing = false;
|
|
|
|
callFunEvent : CallFunType = (str : string[], type : 'auto' | 'click') => { }
|
|
|
|
// 临时存放的识别成功的结果,防止重复扫码。
|
|
tempresultList = [] as string[];
|
|
|
|
isAppPause = false
|
|
|
|
constructor(GroupView : FrameLayout, options : SCANNING_OPTIONS | null = null) {
|
|
this.context = UTSAndroid.getAppContext()! as Context;
|
|
this.parentView = GroupView;
|
|
if (options != null) {
|
|
this.options = {
|
|
width: options.width,
|
|
height: options.height,
|
|
cameraDevice: options.cameraDevice,
|
|
scanSpeedduration: options.scanSpeedduration == null ? this.options.scanSpeedduration : options.scanSpeedduration,
|
|
scanningQrCode: options.scanningQrCode == null ? this.options.scanningQrCode : options.scanningQrCode,
|
|
isSavePhoto: options.isSavePhoto == null ? this.options.isSavePhoto : options.isSavePhoto,
|
|
qrCodeType: options.qrCodeType == null ? this.options.qrCodeType : options.qrCodeType,
|
|
vibatar: options.vibatar == null ? this.options.vibatar : options.vibatar,
|
|
zoom: options.zoom == null ? this.options.zoom : options.zoom,
|
|
continuousScan: options.continuousScan == null ? this.options.continuousScan : options.continuousScan
|
|
} as SCANNING_OPTIONS;
|
|
}
|
|
this.cameraService = this.context!.getSystemService(Context.CAMERA_SERVICE) as CameraManager
|
|
this.vibrator = this.context!.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator;
|
|
this._createCameraView()
|
|
this._creatCameraResultView()
|
|
}
|
|
//震动手机
|
|
setVibatar() {
|
|
if (this.vibrator == null) return
|
|
// 检查设备是否支持震动
|
|
if (this.vibrator!.hasVibrator()) {
|
|
// 触发短暂的震动,持续时间为200毫秒
|
|
this.vibrator!.vibrate(80);
|
|
}
|
|
}
|
|
/** 创建相机预览区域 */
|
|
private _createCameraView() {
|
|
let sv = new SurfaceView(this.context)
|
|
// 让相机的高和宽与组件对齐。
|
|
let layaout = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
|
|
sv.setLayoutParams(layaout)
|
|
this.cameraView = sv;
|
|
|
|
this.parentView.addView(this.cameraView!)
|
|
}
|
|
// 创建一个动画层容器
|
|
private _creatCameraResultView() {
|
|
let t = this;
|
|
let resultView = new RelativeLayout(this.context)
|
|
let layaout = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
|
|
resultView.setLayoutParams(layaout)
|
|
// resultView.setBackgroundColor(Color.argb(30,255,0,0))
|
|
this.resultView = resultView;
|
|
this.parentView.addView(this.resultView!)
|
|
|
|
//监听尺寸变化。
|
|
class previewChangeRectadd extends View.OnLayoutChangeListener {
|
|
override onLayoutChange(v : View, left : Int, top : Int, right : Int, bottom : Int,
|
|
oldLeft : Int, oldTop : Int, oldRight : Int, oldBottom : Int
|
|
) {
|
|
t.viewBox_width = right - left;
|
|
t.viewBox_height = bottom - top;
|
|
|
|
|
|
// t.parentView!.removeOnLayoutChangeListener(this);
|
|
}
|
|
}
|
|
this.parentView!.addOnLayoutChangeListener(new previewChangeRectadd())
|
|
|
|
|
|
}
|
|
openCamera() : Promise<boolean> {
|
|
let _this = this;
|
|
if (this.isOpeningCameraing) {
|
|
this.closeCamera()
|
|
}
|
|
return new Promise((res, rej) => {
|
|
_this.checkPermissions()
|
|
.then((res : boolean) => {
|
|
if (!res) {
|
|
uni.showToast({ title: "未授权相机使用权", icon: "none" })
|
|
return;
|
|
}
|
|
try {
|
|
_this.getCamreaListName()
|
|
_this.getCamreaDeviceSizeList()
|
|
_this.setCamreaSize()
|
|
_this._openCamera();
|
|
_this.setCamreaFocalLength(1)
|
|
_this.addListenApp()
|
|
} catch (e) {
|
|
//TODO handle the exception
|
|
}
|
|
res(true)
|
|
})
|
|
.catch(() => {
|
|
uni.showToast({ title: "未授权相机使用权", icon: "none" })
|
|
rej(false)
|
|
})
|
|
})
|
|
|
|
}
|
|
|
|
addListenApp() {
|
|
let t = this;
|
|
let listener : (() => void) | null = null;
|
|
listener = () => {
|
|
t.isAppPause = true;
|
|
// console.log(UTSAndroid.getDispatcher())
|
|
if (t.isOpeningCameraing) {
|
|
t.pauseCamera()
|
|
}
|
|
if (listener != null) {
|
|
UTSAndroid.offAppActivityPause(listener!);
|
|
}
|
|
}
|
|
UTSAndroid.onAppActivityPause(listener);
|
|
|
|
let listener2 : (() => void) | null = null;
|
|
listener2 = () => {
|
|
t.isAppPause = false
|
|
if (t.isOpeningCameraing) {
|
|
t.openCamera()
|
|
}
|
|
if (listener2 != null) {
|
|
UTSAndroid.offAppActivityResume(listener2!);
|
|
}
|
|
|
|
}
|
|
UTSAndroid.onAppActivityResume(listener2);
|
|
|
|
}
|
|
pauseCamera() {
|
|
this.sufaceHolderView?.release()
|
|
this.cameraDeviceCaptureSession?.close()
|
|
this.cameraDevice?.close()
|
|
this.cameraDevice = null;
|
|
this.cameraDeviceCaptureSession = null;
|
|
this.sufaceHolderView = null;
|
|
}
|
|
closeCamera() {
|
|
this.sufaceHolderView?.release()
|
|
this.cameraDeviceCaptureSession?.close()
|
|
this.cameraDevice?.close()
|
|
this.cameraDevice = null;
|
|
this.cameraDeviceCaptureSession = null;
|
|
this.isOpeningCameraing = false;
|
|
this.sufaceHolderView = null;
|
|
for (let i = 0; i < this.imgPointsBycanmarea.length; i++) {
|
|
this.resultView?.removeView(this.imgPointsBycanmarea[i])
|
|
}
|
|
|
|
|
|
}
|
|
private _openCamera() {
|
|
let _this = this;
|
|
if (this.cameraListName.size == 0) return;
|
|
let camreaNow = this.cameraListName.get(this.options.cameraDevice);
|
|
if (camreaNow == null) return;
|
|
class CamerCallTstasBack implements StateCallback {
|
|
constructor() {
|
|
|
|
}
|
|
override onOpened(camera : CameraDevice) : void {
|
|
console.log('打开成功')
|
|
_this.cameraDevice = camera
|
|
_this.createCameraPreviewSession()
|
|
|
|
}
|
|
override onDisconnected(camera : CameraDevice) : void {
|
|
console.log('连接失败')
|
|
_this.cameraDevice?.close()
|
|
_this.cameraDevice = null
|
|
}
|
|
override onError(camera : CameraDevice, error : Int) : void {
|
|
console.log('出错')
|
|
_this.cameraDevice?.close()
|
|
_this.cameraDevice = null
|
|
}
|
|
override onClosed(camera : CameraDevice) : void {
|
|
console.log('关闭')
|
|
_this.cameraDevice?.close()
|
|
_this.cameraDevice = null
|
|
}
|
|
|
|
}
|
|
this.cameraService.openCamera(camreaNow.id! as string, new CamerCallTstasBack(), null);
|
|
}
|
|
|
|
/**
|
|
* 检查相机权限
|
|
*/
|
|
checkPermissions() : Promise<boolean> {
|
|
let permissionCheck = ["android.permission.CAMERA"]
|
|
let _this = this;
|
|
// 请求拍照权限
|
|
return new Promise(res => {
|
|
if (UTSAndroid.checkSystemPermissionGranted(UTSAndroid.getUniActivity()!, permissionCheck)) {
|
|
// _this.cameraPermissioPass = true
|
|
res(true)
|
|
} else {
|
|
console.log("当前不具备指定权限")
|
|
// 请求拍照权限
|
|
UTSAndroid.requestSystemPermission(UTSAndroid.getUniActivity()!, permissionCheck, function (allRight : boolean, _ : string[]) {
|
|
// _this.cameraPermissioPass = allRight
|
|
res(true)
|
|
}, function (_ : boolean, _ : string[]) {
|
|
//用户拒绝了部分权限
|
|
// _this.cameraPermissioPass = false
|
|
res(false)
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 获取相机设备列表
|
|
*/
|
|
getCamreaListName(deviceName : 'FRONT' | 'BACK' | null = null) {
|
|
// 前置还是后置。
|
|
let deviceLens = CameraCharacteristics.LENS_FACING_BACK;
|
|
let dname = this.options.cameraDevice
|
|
if (deviceName != null) {
|
|
dname = deviceName!
|
|
}
|
|
if (dname == 'FRONT') {
|
|
deviceLens = CameraCharacteristics.LENS_FACING_FRONT;
|
|
}
|
|
if (dname == 'BACK') {
|
|
deviceLens = CameraCharacteristics.LENS_FACING_BACK;
|
|
}
|
|
this.cameraListName.clear()
|
|
|
|
|
|
for (caIds in this.cameraService.getCameraIdList()) {
|
|
let ps = this.cameraService.getCameraCharacteristics(caIds as string);
|
|
let facing = ps.get(CameraCharacteristics.LENS_FACING);
|
|
if (facing == CameraCharacteristics.LENS_FACING_FRONT) {
|
|
this.cameraListName.set('FRONT', { id: caIds as string, device: ps } as cameraListNameType)
|
|
}
|
|
if (facing == CameraCharacteristics.LENS_FACING_BACK) {
|
|
this.cameraListName.set('BACK', { id: caIds as string, device: ps } as cameraListNameType)
|
|
}
|
|
}
|
|
|
|
this.setCamreaSize();
|
|
}
|
|
/**
|
|
* 获取相机设备尺寸列表
|
|
* 调用此函数的前提是要先获取设备列表,要先调用getCamreaListName函数
|
|
*/
|
|
getCamreaDeviceSizeList() : CAMERA_PHOTO_SIZE[] {
|
|
let sizelist = [] as CAMERA_PHOTO_SIZE[]
|
|
if (this.cameraListName.size == 0) {
|
|
console.error("请先调用getCamreaListName获取设备列表")
|
|
return sizelist;
|
|
}
|
|
let camrea = this.cameraListName.get(this.options.cameraDevice) as cameraListNameType;
|
|
if (camrea != null) {
|
|
let maps = camrea!.device!.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) as StreamConfigurationMap
|
|
if (maps != null) {
|
|
|
|
let testSize = maps!.getOutputSizes(ImageFormat.JPEG)
|
|
for (sz in testSize) {
|
|
let w = sz.getWidth()
|
|
let h = sz.getHeight()
|
|
sizelist.push({ width: w, height: h } as CAMERA_PHOTO_SIZE)
|
|
}
|
|
|
|
}
|
|
}
|
|
return sizelist;
|
|
}
|
|
/**
|
|
* 设置相机焦距
|
|
* 0-1之间。
|
|
* 暂未实现。
|
|
*/
|
|
setCamreaFocalLength(focal : number) {
|
|
|
|
if (this.cameraListName.size == 0) {
|
|
console.error("请先调用getCamreaListName获取设备列表")
|
|
return
|
|
}
|
|
let camrea = this.cameraListName.get(this.options.cameraDevice) as cameraListNameType;
|
|
|
|
if (camrea != null) {
|
|
let zoomMax = camrea!.device!.get(CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE)
|
|
let nowZoom = camrea!.device!.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE)
|
|
|
|
}
|
|
}
|
|
/**
|
|
* 设置相机前置还是后置,设置后需要重新打开相机。
|
|
* BACK 后置
|
|
* FRONT 前置
|
|
*/
|
|
setCamreaDevice(name : 'FRONT' | 'BACK') {
|
|
this.options.cameraDevice = name;
|
|
}
|
|
/**
|
|
* 设置相机摄像尺寸,通过options中的width,height来匹配设置
|
|
* 返回的不一定是真实的options中的宽高,只是与其相近的值。
|
|
* 以设置后要重新打开。相机生效。
|
|
* 返回相机生效的尺寸
|
|
*/
|
|
setCamreaSize(width : number | null = null, height : number | null = null) : CAMERA_PHOTO_SIZE {
|
|
// 宽高列表
|
|
const dimensions = this.getCamreaDeviceSizeList()
|
|
// 目标宽高
|
|
const targetWidth = width != null ? width! : this.options.width;
|
|
const targetHeight = height != null ? height! : this.options.height;
|
|
const targetRatio = targetWidth / targetHeight;
|
|
// 找到最接近目标比例的宽高值
|
|
const closestDimension = dimensions.reduce((prev : CAMERA_PHOTO_SIZE, curr : CAMERA_PHOTO_SIZE) : CAMERA_PHOTO_SIZE => {
|
|
const currRatio = curr.width / curr.height;
|
|
const prevRatio = prev.width / prev.height;
|
|
const currDiffRatio = Math.abs(currRatio - targetRatio);
|
|
const prevDiffRatio = Math.abs(prevRatio - targetRatio);
|
|
|
|
// 计算宽高值与目标值的差的平方和
|
|
const currDiffValue = Math.pow(curr.width - targetWidth, 2) + Math.pow(curr.height - targetHeight, 2);
|
|
const prevDiffValue = Math.pow(prev.width - targetWidth, 2) + Math.pow(prev.height - targetHeight, 2);
|
|
|
|
// 给比例差和值差赋予不同的权重,这里假设两者权重相同
|
|
const currTotalDiff = currDiffRatio + currDiffValue;
|
|
const prevTotalDiff = prevDiffRatio + prevDiffValue;
|
|
|
|
return currTotalDiff < prevTotalDiff ? curr : prev;
|
|
}, dimensions[0]);
|
|
this.cameraPreviewSize = closestDimension
|
|
|
|
return closestDimension;
|
|
}
|
|
|
|
// 获取当前相机的旋转角度。
|
|
getCamraRotation() : Number {
|
|
let sensorOrientation = 0
|
|
let tempra = 0
|
|
if (this.cameraListName.size == 0) return sensorOrientation;
|
|
let camreaNow = this.cameraListName.get(this.options.cameraDevice);
|
|
if (camreaNow == null) return sensorOrientation;
|
|
|
|
let windowManager = UTSAndroid.getUniActivity()!.getWindowManager() as WindowManager
|
|
|
|
let deviceRotation = windowManager!.getDefaultDisplay().getRotation()
|
|
let rotationCompensation = this.cameraRate.get(deviceRotation) as number
|
|
|
|
tempra = camreaNow.device.get(CameraCharacteristics.SENSOR_ORIENTATION)!! as number
|
|
|
|
if (this.options.cameraDevice == 'FRONT') {
|
|
tempra = ((tempra + rotationCompensation) % 360)
|
|
} else {
|
|
tempra = ((tempra - rotationCompensation + 360) % 360)
|
|
}
|
|
return tempra
|
|
}
|
|
|
|
// 设置识别成功的回调函数
|
|
setCallEvent(call : CallFunType) {
|
|
this.callFunEvent = call;
|
|
}
|
|
|
|
// 创建视频预览流。
|
|
private createCameraPreviewSession() {
|
|
if (this.cameraView == null) return;
|
|
|
|
// 设置预览区域
|
|
let previewAspectRatio = this.cameraPreviewSize.width / this.cameraPreviewSize.height;
|
|
let layoutWidth = this.viewBox_width
|
|
let layoutHeight = this.viewBox_height
|
|
let layoutAspectRatio = layoutWidth / layoutHeight;
|
|
|
|
|
|
let holder = this.cameraView!.getHolder()
|
|
let suface = holder!.getSurface();
|
|
this.sufaceHolderView = suface
|
|
|
|
if (this.cameraDevice == null) return;
|
|
let t = this;
|
|
let previewRequestBuilder = this.cameraDevice!.createCaptureRequest(CameraDevice.PREVIEW)
|
|
previewRequestBuilder?.addTarget(suface)
|
|
|
|
let totalWidth = this.cameraPreviewSize.width
|
|
let totalHeight = this.cameraPreviewSize.height
|
|
|
|
|
|
// 再添加一个image来预览
|
|
let minh = Math.min(this.options.width, this.options.height)
|
|
let reader = ImageReader.newInstance(minh.toInt(), minh.toInt(), ImageFormat.YUV_420_888, 1)
|
|
previewRequestBuilder?.addTarget(reader.getSurface())
|
|
let outsufaces = [suface, reader.getSurface()] as Surface[]
|
|
|
|
|
|
class sessionCallback extends CameraCaptureSession.StateCallback {
|
|
constructor() { }
|
|
override onConfigured(session : CameraCaptureSession) {
|
|
t.cameraDeviceCaptureSession = session
|
|
t.isOpeningCameraing = true;
|
|
try {
|
|
t.cameraDeviceCaptureSession?.setRepeatingRequest(previewRequestBuilder.build(), null, null)
|
|
} catch (e) {
|
|
console.log("Failed to start camera preview session.", e)
|
|
}
|
|
}
|
|
override onConfigureFailed(session : CameraCaptureSession) {
|
|
|
|
}
|
|
override onClosed(session : CameraCaptureSession) {
|
|
t.closeCamera()
|
|
}
|
|
override onSurfacePrepared(session : CameraCaptureSession, testSuface : Surface) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
reader.setOnImageAvailableListener(this.createCameraImageRenderLisen() as OnImageAvailableListener, null)
|
|
this.cameraDevice!.createCaptureSession(outsufaces, new sessionCallback(), null)
|
|
|
|
|
|
}
|
|
|
|
private createCameraImageRenderLisen() : any {
|
|
let t = this;
|
|
class imageReaderCallback extends ImageReader.OnImageAvailableListener {
|
|
private currentImage = null as Image | null;
|
|
override onImageAvailable(irader : ImageReader) {
|
|
if (this.currentImage != null) {
|
|
this.currentImage!.close(); // 先关闭上一帧的Image
|
|
}
|
|
let curimage = irader.acquireNextImage();
|
|
|
|
@Suppress("DEPRECATION")
|
|
class IntentRunable extends Runnable {
|
|
override run() {
|
|
if (t.isDecoderQring || !t.isOpeningCameraing || t.isAppPause) return;
|
|
try {
|
|
t.qrDecoder(curimage!)
|
|
} catch (e) {
|
|
t.closeCamera()
|
|
console.error('应用异常:', e)
|
|
}
|
|
}
|
|
}
|
|
this.currentImage = curimage
|
|
if (this.currentImage != null) {
|
|
new Thread(new IntentRunable()).start()
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return new imageReaderCallback()
|
|
}
|
|
|
|
// 设置图片上的小圆的识别位置。
|
|
setImageViewRusletBox(width : number, height : number, realWidth : number, realHeight : number, jieguo : SCANNING_PHOTO_RESULT) {
|
|
let t = this;
|
|
let wRatio = t.viewBox_width / realWidth;
|
|
let hRatio = t.viewBox_height / realHeight;
|
|
let btnwidth = 120;
|
|
let zoom = 10 - this.options.zoom!
|
|
|
|
for (let i = 0; i < jieguo.bounds.length; i++) {
|
|
let item = jieguo.bounds[i];
|
|
let layoutParams = new LinearLayout.LayoutParams(
|
|
LinearLayout.LayoutParams.WRAP_CONTENT,
|
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
|
)
|
|
|
|
|
|
let x = wRatio * item.centerX
|
|
let y = hRatio * item.centerY
|
|
|
|
layoutParams.topMargin = (y - 60).toInt() // 设置顶部边距
|
|
layoutParams.leftMargin = (x - 60).toInt() // 设置左边距
|
|
let relativeLayout = new RelativeLayout(t.context)
|
|
relativeLayout.layoutParams = layoutParams
|
|
|
|
const rtxt = jieguo.text[i]
|
|
let v = new xView(t.context)
|
|
v.setSize(120, 120)
|
|
.setRadius(100)
|
|
.setBackgroundColor('#21d429')
|
|
.setBorder(12, "#ffffff", false)
|
|
.setClick((event : MotionEvent) => {
|
|
try {
|
|
t.callFunEvent([rtxt], 'click')
|
|
} catch (e) {
|
|
//TODO handle the exception
|
|
}
|
|
})
|
|
let ov = v.getView();
|
|
|
|
|
|
// 创建缩放动画
|
|
let scaleAnimation = new ScaleAnimation(1.0.toFloat(), 0.7.toFloat(), 1.0.toFloat(), 0.7.toFloat(), Animation.RELATIVE_TO_SELF, 0.7.toFloat(), Animation.RELATIVE_TO_SELF, 0.7.toFloat());
|
|
scaleAnimation.setDuration(600); // 设置动画持续时间
|
|
scaleAnimation.setRepeatMode(Animation.REVERSE); // 设置重复模式为反向
|
|
scaleAnimation.setRepeatCount(Animation.INFINITE); // 设置重复次数为无限
|
|
// 启动动画
|
|
ov.startAnimation(scaleAnimation);
|
|
|
|
relativeLayout.addView(ov)
|
|
|
|
this.imgPointsBycanmarea.push(relativeLayout)
|
|
this.resultView!.addView(relativeLayout)
|
|
}
|
|
|
|
|
|
}
|
|
private YUV_420_888toNV21(opts : buffoptsType) : ByteArray {
|
|
|
|
let crop = opts.rect;
|
|
let format = opts.format;
|
|
let width = crop.width();
|
|
let height = crop.height();
|
|
let planes = opts.planes!;
|
|
let data = new ByteArray((width * height * ImageFormat.getBitsPerPixel(format.toInt()) / 8).toInt());
|
|
let rowData = new ByteArray((planes[0].getRowStride).toInt());
|
|
let channelOffset = 0;
|
|
let outputStride = 1;
|
|
for (let i = 0; i < planes.length; i++) {
|
|
switch (i) {
|
|
case 0:
|
|
channelOffset = 0;
|
|
outputStride = 1;
|
|
break;
|
|
case 1:
|
|
channelOffset = width * height;
|
|
outputStride = 1;
|
|
break;
|
|
case 2:
|
|
channelOffset = width * height * 1.25;
|
|
outputStride = 1;
|
|
break;
|
|
}
|
|
let buffer = planes[i.toInt()].data!;
|
|
let rowStride = planes[i.toInt()].getRowStride;
|
|
let pixelStride = planes[i.toInt()].getPixelStride;
|
|
|
|
let shift = (i == 0) ? 0 : 1;
|
|
let w = width >> shift;
|
|
let h = height >> shift;
|
|
buffer.position((rowStride * (crop.top >> shift) + pixelStride * (crop.left >> shift)).toInt());
|
|
for (let row = 0; row < h; row++) {
|
|
let length = 0;
|
|
if (pixelStride == 1 && outputStride == 1) {
|
|
length = w;
|
|
buffer.get(data, channelOffset.toInt(), length.toInt());
|
|
channelOffset += length;
|
|
} else {
|
|
length = (w - 1) * pixelStride + 1;
|
|
buffer.get(rowData, 0, length.toInt());
|
|
for (let col = 0; col < w; col++) {
|
|
data[channelOffset.toInt()] = rowData[(col * pixelStride).toInt()];
|
|
channelOffset += outputStride;
|
|
}
|
|
}
|
|
if (row < h - 1) {
|
|
buffer.position((buffer.position() + rowStride - length).toInt());
|
|
}
|
|
}
|
|
}
|
|
return data;
|
|
}
|
|
|
|
private qrDecoder(image : Image) {
|
|
let t = this;
|
|
if (!this.isDecoderQring && !this.isAppPause) {
|
|
this.isDecoderQring = true;
|
|
|
|
let width = image.getWidth();
|
|
let height = image.getHeight();
|
|
let frameSize = width * height;
|
|
let qFrameSize = frameSize / 4;
|
|
// 获取各平面的数据与信息
|
|
let planes = image.getPlanes();
|
|
|
|
let yBufferCopy = [] as ByteArray[]
|
|
let yrurvrNumber = [] as number[]
|
|
|
|
let yr = planes[0]
|
|
let ur = planes[1]
|
|
let vr = planes[2]
|
|
let oraingMax = yr.getBuffer().limit()
|
|
|
|
for (let i = 0; i < 3; i++) {
|
|
let buffer = planes[i.toInt()].getBuffer();
|
|
yBufferCopy.push(new ByteArray(buffer.remaining().toInt()))
|
|
buffer.get(yBufferCopy[i.toInt()]);
|
|
}
|
|
let y = ByteBuffer.wrap(yBufferCopy[0]!)
|
|
let u = ByteBuffer.wrap(yBufferCopy[1]!)
|
|
let v = ByteBuffer.wrap(yBufferCopy[2]!)
|
|
|
|
let opts = {
|
|
width,
|
|
height,
|
|
format: image.getFormat(),
|
|
rect: image.getCropRect(),
|
|
planes: [
|
|
{
|
|
data: y,
|
|
getRowStride: yr.getRowStride(),
|
|
getPixelStride: yr.getPixelStride(),
|
|
},
|
|
{
|
|
data: u,
|
|
getRowStride: ur.getRowStride(),
|
|
getPixelStride: ur.getPixelStride(),
|
|
},
|
|
{
|
|
data: v,
|
|
getRowStride: vr.getRowStride(),
|
|
getPixelStride: vr.getPixelStride(),
|
|
}
|
|
] as buffoptsPlanesType[]
|
|
} as buffoptsType
|
|
|
|
decoderBuffToQr(this.YUV_420_888toNV21(opts), width, height, this.getCamraRotation())
|
|
.then((res) => {
|
|
|
|
if (res != null && t.isOpeningCameraing && !t.isAppPause) {
|
|
let texts = res.text as string[]
|
|
if (texts.length > 0) {
|
|
// texts.some((el:string):boolean=>t.tempresultList.includes(el))
|
|
|
|
if (t.options.vibatar == true) {
|
|
t.setVibatar()
|
|
}
|
|
if (t.options.continuousScan == false) {
|
|
t.closeCamera()
|
|
}
|
|
t.callFunEvent(texts, 'auto')
|
|
t.setImageViewRusletBox(t.options.width, t.options.height, width, height, res as SCANNING_PHOTO_RESULT)
|
|
}
|
|
t.isDecoderQring = false
|
|
} else {
|
|
t.isDecoderQring = false
|
|
}
|
|
// image.close();
|
|
})
|
|
.catch(() => {
|
|
// this.imageUrl = null
|
|
})
|
|
// setTimeout(function () {
|
|
// t.isDecoderQring = false
|
|
// }, t.options!.scanSpeedduration!);
|
|
// image.close();
|
|
}
|
|
image.close();
|
|
}
|
|
|
|
|
|
} |