Files
2026-09-24 16:25:22 +08:00

979 lines
31 KiB
Plaintext

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 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 ViewGroup from 'android.view.ViewGroup'
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 R from 'io.dcloud.uni_modules.xMlkitScannigS.R';
import Rect from 'android.graphics.Rect';
import Matrix from 'android.graphics.Matrix'
import ImageReader from 'android.media.ImageReader'
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 Bundle from 'android.os.Bundle'
import Handler from 'android.os.Handler'
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 { decoderUriPathToQr, decoderBuffToQr,decoderUriPathToQrByUri } from "./index.uts"
import { SCANNING_PHOTO_RESULT } from "../interface.uts"
import xView from "./libview/view.uts"
type Dimension = number[];
let textStr = null as null | string
function findClosestRatio(dimensions : Dimension[], targetWidth : number, targetHeight : number) : Dimension | null {
let minDiff = Integer.MAX_VALUE;
let closestPair : Dimension | null = null;
for (let i = 0; i < dimensions.length; i++) {
const width = dimensions[i][0]!
const height = dimensions[i][1]!
const currentRatio = width / height;
const targetRatio = targetWidth / targetHeight;
// const diff = Math.abs(currentRatio - targetRatio);
const diff = Math.abs(width - targetWidth) + Math.abs(height - targetHeight);
if (diff < minDiff) {
minDiff = diff.toInt();
closestPair = [width, height];
}
}
return closestPair;
}
class DemoActivity extends Activity {
scanSpeed = 500 //识别速度单位ms
scanPrevwSize = 800 //预期识别的像素大小,这是预期,会根据这个值找到相机支持的相近的相机尺寸。
context : Context;
camera_service : CameraManager;
cameraPermissioPass = false;
camerManIds = null as string | null
cameraDevice = null as null | CameraDevice
preview = null as null | SurfaceView;
captureSession = null as null | CameraCaptureSession
previewWidth = 0 as Int;
previewHeight = 0 as Int;
prviewTop = 0
prviewLeft = 0
okScal = [0, 0] as Dimension
imageHandler = null as null | Handler;
isDecodreIng = false
scanBoxView = null as null | View
scanBoxViewDrawble = null as null | GradientDrawable
//选择相册按钮
xiangCeBtn = null as TextView | null
paiZhaoBtn = null as TextView | null
shaoyishaoBtn = null as TextView | null
//相册的标识点容器
xiangjiBox = null as RelativeLayout | null
//相册图片预览父viewout
imageYuranBox = null as RelativeLayout | null
imageYuranBox_width = 0 as Int;
imageYuranBox_height = 0 as Int;
imageView = null as null | ImageView
imageUrl = null as null | Uri
isSelectedImging = false;
// 结果集的标注点。
imgPoints = [] as RelativeLayout[]
imgPointsBycanmarea = [] as RelativeLayout[]
ORIENTATIONS = new Map<Number, Number>()
// 当前是否正在扫码中
isScannig = true
tid = 0;
vibrator = null as null|Vibrator
constructor() {
super();
this.context = UTSAndroid.getAppContext() as Context;
this.camera_service = this.context!.getSystemService(Context.CAMERA_SERVICE) as CameraManager
this.ORIENTATIONS.set(Surface.ROTATION_0, 0)
this.ORIENTATIONS.set(Surface.ROTATION_90, 90)
this.ORIENTATIONS.set(Surface.ROTATION_180, 180)
this.ORIENTATIONS.set(Surface.ROTATION_270, 270)
// 在你的Activity或者Fragment中
this.vibrator = this.context!.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator;
}
override onCreate(savedInstanceState ?: Bundle) : void {
super.onCreate(savedInstanceState)
this.hideTitleBar()
this.setContentView(R.layout.demo_activity);
this.overridePendingTransition(R.anim.enter_anim, R.anim.exit_anim);
this.preview = this.findViewById<SurfaceView>(R.id.surface_camera);
this.xiangCeBtn = this.findViewById<TextView>(R.id.footer_asml);
this.paiZhaoBtn = this.findViewById<TextView>(R.id.footer_camre);
this.shaoyishaoBtn = this.findViewById<TextView>(R.id.footer_scanning);
//相机的图片预览
this.imageYuranBox = this.findViewById<RelativeLayout>(R.id.scan_box);
//相机的遮罩标记点的容器
this.xiangjiBox = this.findViewById<RelativeLayout>(R.id.camre_center_preview);
let t = this;
this.steBtnLisents()
// 创建图片预览的组件。
this.imageView = new ImageView(this.context);
this.imageView!.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT))
this.imageYuranBox!.addView(this.imageView)
let layoutParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
//监听尺寸变化。
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.imageYuranBox_width = right - left;
t.imageYuranBox_height = bottom - top;
v.removeOnLayoutChangeListener(this);
}
}
this.imageYuranBox!.addOnLayoutChangeListener(new previewChangeRectadd())
// this.scanBoxView = this.findViewById<View>(R.id.scan_view_canvas);
// this.scanBoxViewDrawble = new GradientDrawable();
// this.scanBoxView.setOnDragListener
// this.customStyleSacnBox();
}
steBtnLisents() {
let t = this;
class xiangCheCallClick extends View.OnClickListener {
override onClick(v ?: View) : void {
t.closeCamra()
t.isSelectedImging = true;
let intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
t.startActivityForResult(intent, 1)
}
}
// 相同选择时。
this.xiangCeBtn!.setOnClickListener(new xiangCheCallClick())
class shaoyishaoBtnCallClick extends View.OnClickListener {
override onClick(v ?: View) : void {
t.isSelectedImging = false;
t.clearImageView()
t.openCamra();
}
}
//实时扫一扫时
this.shaoyishaoBtn!.setOnClickListener(new shaoyishaoBtnCallClick())
let backBtn = this.findViewById<TextView>(R.id.header_back);
class backCallClick extends View.OnClickListener {
override onClick(v ?: View) : void {
t.finish()
t.overridePendingTransition(R.anim.enter_anim, R.anim.exit_anim);
}
}
//实时扫一扫时
backBtn!.setOnClickListener(new backCallClick())
}
// 设置扫码框的样式。
customStyleSacnBox() {
if (this.scanBoxView == null) return;
// this.scanBoxViewDrawble!.setShape(GradientDrawable.RECTANGLE)
// this.scanBoxViewDrawble!.setStroke((4).toInt(),Color.GREEN)
// this.scanBoxViewDrawble!.mutate()
// this.scanBoxView!.setBackground(this!.scanBoxViewDrawble)
}
//震动手机
vibatarLong(){
if(this.vibrator == null) return
// 检查设备是否支持震动
if (this.vibrator!.hasVibrator()) {
// 触发短暂的震动,持续时间为200毫秒
this.vibrator!.vibrate(80);
}
}
override onDestroy() : void {
super.onDestroy();
if (this.captureSession != null) {
this.captureSession!.close();
this.cameraDevice?.close()
this.captureSession = null;
this.cameraDevice = null;
console.log("关闭相机");
}
this.showTitleBar();
}
// 在需要隐藏标题栏的地方调用
hideTitleBar() : void {
requestWindowFeature(Window.FEATURE_NO_TITLE);
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
// 在需要显示标题栏的地方调用
showTitleBar() : void {
this.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
/**
* 检查相机权限
*/
isCheckPermissions() : Promise<boolean> {
let permissionCheck = ["android.permission.CAMERA"]
// 请求拍照权限
return new Promise(res => {
if (UTSAndroid.checkSystemPermissionGranted(UTSAndroid.getUniActivity()!, permissionCheck)) {
console.log("当前已具备指定权限")
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)
})
}
})
}
getCameraById() {
// const cameraIds = this.camera_service.cameraIdList
let caIds = null as null | string;
for (id in this.camera_service.getCameraIdList()) {
let ps = this.camera_service.getCameraCharacteristics(id);
let facing = ps.get(CameraCharacteristics.LENS_FACING);
// 后置摄像头
if (facing == CameraCharacteristics.LENS_FACING_BACK) {
caIds = id;
let rulest = null as Dimension | null
// 获取尺寸
let maps = ps.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
if (maps != null) {
let testSize = maps!.getOutputSizes(ImageFormat.JPEG)
let psdd = [] as Array<Dimension>
for (sz in testSize) {
let w = sz.getWidth()
let h = sz.getHeight()
psdd.push([w, h] as Dimension)
}
// console.log(psdd)
// 这里使用一个800*800视野区域图片大概64万像素的图片进行识别,这样可以提高性能减少手机识别的负担。
let pd = findClosestRatio(psdd, this.scanPrevwSize, this.scanPrevwSize)
// 如果想要高清识别或者加缩小焦距(就景深远,视野大,就开启下面的)
// let pd = findClosestRatio(psdd, this.previewWidth, this.previewHeight)
if (pd != null) {
this.okScal = pd as Dimension;
}
}
break;
}
}
this.camerManIds = caIds
// characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
// console.log(this.camera_service.cameraIdList)
// this.camerListIds = this.camera_service.getCameraIdList();
// for(let i=0;i<this.camerListIds.length;i++){
// console.log(this.camerListIds[i])
// }
}
//裁剪区域的缩放比。
calculateCropRegion(cameraOutputWidth : Number, cameraOutputHeight : Number, previewWidth : Number, previewHeight : Number) : Rect {
let targetAspectRatio = previewWidth / previewHeight
let cropWidth = 0
let cropHeight = 0
// 根据目标宽高比确定裁剪宽度或高度,并确保不超过相机输出尺寸
if (cameraOutputWidth * previewHeight > cameraOutputHeight * previewWidth) {
// 裁剪高度为相机输出高度,根据宽高比计算裁剪宽度
cropHeight = cameraOutputHeight
cropWidth = (cropHeight * targetAspectRatio)
cropWidth = Math.min(cropWidth, cameraOutputWidth)
} else {
// 裁剪宽度为相机输出宽度,根据宽高比计算裁剪高度
cropWidth = cameraOutputWidth
cropHeight = (cropWidth / targetAspectRatio)
cropHeight = Math.min(cropHeight, cameraOutputHeight)
}
// 计算居中对齐的裁剪区域
let centerX = (cameraOutputWidth - cropWidth) / 2
let centerY = (cameraOutputHeight - cropHeight) / 2
return Rect(
centerX.toInt(), // 左边界
centerY.toInt(), // 上边界
(centerX + cropWidth).toInt(), // 右边界
(centerY + cropHeight).toInt() // 下边界
)
}
createCameraPreviewSession() {
if (this.preview == null) return;
let holder = this.preview!.getHolder()
let suface = holder!.getSurface();
if (this.cameraDevice == null) return;
let t = this;
let previewRequestBuilder = this.cameraDevice!.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
previewRequestBuilder?.addTarget(suface)
let totalWidth = this.okScal[0].toInt()
let totalHeight = this.okScal[1].toInt()
console.log(totalWidth,totalHeight)
previewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, new Rect(0, 0, totalWidth*3, totalHeight*3));
// previewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, this.calculateCropRegion(totalWidth, totalHeight, this.previewWidth, this.previewWidth));
// 再添加一个image来预览
let minh = Math.min(this.previewWidth, this.previewHeight)
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.captureSession = session
try {
t.captureSession?.setRepeatingRequest(previewRequestBuilder.build(), null, null)
} catch (e) {
console.log("Failed to start camera preview session.", e)
}
}
override onConfigureFailed(session : CameraCaptureSession) {
}
override onClosed(session : CameraCaptureSession) {
}
override onSurfacePrepared(session : CameraCaptureSession, testSuface : Surface) {
}
}
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.isScannig ) return;
t.qrDecoder(curimage!)
}
}
// curimage.close()
// 跳转到一个新的界面
// UTSAndroid.getUniActivity()!.runOnUiThread(new IntentRunable())
this.currentImage = curimage
if (this.currentImage != null) {
new Thread(new IntentRunable()).start()
}
}
}
reader.setOnImageAvailableListener(new imageReaderCallback(), null)
this.cameraDevice!.createCaptureSession(outsufaces, new sessionCallback(), null)
// previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_VIDEO);
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
previewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
}
// 设置相机对焦模式
// setCamraFoucsAuto(){
// let cameraManager = this.getSystemService(Context.CAMERA_SERVICE) as CameraManager
// let characteristics = cameraManager.getCameraCharacteristics(this.camerManIds!)
// // console.log(CaptureRequest.CONTROL_AF_MODE)
// captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_VIDEO);
// }
// 获取当前相机的旋转角度。
getCamraRotation() : Int {
let cameraId = this.camerManIds!;
let isFrontFacing = false;
let deviceRotation = this.windowManager.defaultDisplay.rotation
let rotationCompensation = ORIENTATIONS.get(deviceRotation) as number
// Get the device's sensor orientation.
let cameraManager = this.getSystemService(Context.CAMERA_SERVICE) as CameraManager
let sensorOrientation = cameraManager
.getCameraCharacteristics(cameraId)
.get(CameraCharacteristics.SENSOR_ORIENTATION)!! as number
if (isFrontFacing) {
rotationCompensation = ((sensorOrientation + rotationCompensation) % 360).toInt()
} else { // back-facing
rotationCompensation = ((sensorOrientation - rotationCompensation + 360) % 360).toInt()
}
return rotationCompensation
}
YUV42088ToNV21(image : Image) : ByteArray {
// 获取图像的宽度、高度以及各平面的行间距
let width = image.getWidth();
let height = image.getHeight();
// 获取各平面的数据与信息
let planes = image.getPlanes();
let yBuffer = planes[0].getBuffer().duplicate();
let uBuffer = planes[1].getBuffer().duplicate();
let vBuffer = planes[2].getBuffer().duplicate();
yBuffer.rewind();
uBuffer.rewind();
vBuffer.rewind();
let yRowStride = planes[0].getRowStride();
let uvRowStride = planes[1].getRowStride();
let uvPixelStride = planes[1].getPixelStride(); // U 和 V 的像素间距通常是一样的
// 创建一个足够大的NV21输出数组
let nv21Data = new ByteArray(width * height * 3 / 2);
// 指针用于遍历输出数组
let yIndex = 0;
let uvIndex = width * height;
let bytesPerPixel = 1
try {
// 遍历Y平面
for (let row = 0; row < height; row++) {
for (let col = 0; col < width; col++) {
nv21Data[(yIndex++).toInt()] = yBuffer.get();
}
// 跳过未使用的行数据(如果有)
let rowRemainder = yRowStride - width * bytesPerPixel;
if (rowRemainder > 0) {
yBuffer.position((yBuffer.position() + rowRemainder).toInt());
} else {
yBuffer.position((yBuffer.position() + width * bytesPerPixel).toInt()); // 防止由于计算错误导致的负数
}
}
} catch (e) {
//TODO handle the exception
}
// // 处理UV平面
try {
for (let row = 0; row < height / 2; row++) {
for (let col = 0; col < width / 2; col++) {
// 先读取U分量
nv21Data[uvIndex++] = uBuffer.get();
// 如果UV像素不是交错存储,则跳过一个位置
if (uvPixelStride == 2) {
uBuffer.position(uBuffer.position() + 1);
}
// 再读取V分量
nv21Data[uvIndex++] = vBuffer.get();
// 如果UV像素是交错存储,这里也需要相应调整位置
if (uvPixelStride == 2) {
vBuffer.position(vBuffer.position() + 1);
}
}
// 跳过未使用的行数据(如果有)
let uvRowSkip = uvRowStride - (width / 2) * uvPixelStride;
if (uvRowSkip > 0) {
uBuffer.position(uBuffer.position() + uvRowSkip);
vBuffer.position(vBuffer.position() + uvRowSkip);
}
}
} catch (e) {
//TODO handle the exception
}
return nv21Data;
}
YUV42088ToNV21Copy(a:ByteBuffer,b:ByteBuffer,c:ByteBuffer,widths:number,heights:number,yr:number,ur:number,vr:number) : ByteArray {
// 获取图像的宽度、高度以及各平面的行间距
let width = widths
let height = heights;
// 获取各平面的数据与信息
let yBuffer = a;
let uBuffer = b;
let vBuffer = c;
yBuffer.rewind();
uBuffer.rewind();
vBuffer.rewind();
let yRowStride = yr;
let uvRowStride = ur;
let uvPixelStride = vr; // U 和 V 的像素间距通常是一样的
// 创建一个足够大的NV21输出数组
let nv21Data = new ByteArray((width * height * 3 / 2).toInt());
// 指针用于遍历输出数组
let yIndex = 0;
let uvIndex = width * height;
let bytesPerPixel = 1
try {
// 遍历Y平面
for (let row = 0; row < height; row++) {
for (let col = 0; col < width; col++) {
nv21Data[(yIndex++).toInt()] = yBuffer.get();
}
// 跳过未使用的行数据(如果有)
let rowRemainder = yRowStride - width * bytesPerPixel;
if (rowRemainder > 0) {
yBuffer.position((yBuffer.position() + rowRemainder).toInt());
} else {
yBuffer.position((yBuffer.position() + width * bytesPerPixel).toInt()); // 防止由于计算错误导致的负数
}
}
} catch (e) {
//TODO handle the exception
}
// // 处理UV平面
try {
for (let row = 0; row < height / 2; row++) {
for (let col = 0; col < width / 2; col++) {
// 先读取U分量
nv21Data[(uvIndex++).toInt()] = uBuffer.get();
// 如果UV像素不是交错存储,则跳过一个位置
if (uvPixelStride == 2) {
uBuffer.position((uBuffer.position() + 1).toInt());
}
// 再读取V分量
nv21Data[(uvIndex++).toInt()] = vBuffer.get();
// 如果UV像素是交错存储,这里也需要相应调整位置
if (uvPixelStride == 2) {
vBuffer.position((vBuffer.position() + 1).toInt());
}
}
// 跳过未使用的行数据(如果有)
let uvRowSkip = uvRowStride - (width / 2) * uvPixelStride;
if (uvRowSkip > 0) {
uBuffer.position((uBuffer.position() + uvRowSkip).toInt());
vBuffer.position((vBuffer.position() + uvRowSkip).toInt());
}
}
} catch (e) {
//TODO handle the exception
}
return nv21Data;
}
copyImagePlan(plane:Plane):ByteBuffer{
// 创建一个新的ByteBuffer来存储复制的数据
let copiedBuffer = ByteBuffer.allocateDirect(plane.getBuffer().capacity());
copiedBuffer.order(ByteOrder.nativeOrder()); // 确保字节顺序与原Buffer一致
// 将原始Plane的ByteBuffer内容复制到新的ByteBuffer中
plane.getBuffer().rewind(); // 移动缓冲区指针到开始位置(可选,如果已经知道在开始处则无需此步)
copiedBuffer.put(plane.getBuffer());
// 复制完成后,可以移动新ByteBuffer的指针回到起始位置以便读取
copiedBuffer.rewind();
return copiedBuffer
}
qrDecoder(image : Image) {
let t = this;
if (!this.isDecodreIng) {
this.isDecodreIng = true;
let width = image.getWidth();
let height = image.getHeight();
// 获取各平面的数据与信息
let planes = image.getPlanes();
let a = this.copyImagePlan(planes[0]);
let b = this.copyImagePlan(planes[1]);
let c = this.copyImagePlan(planes[2]);
let yr = planes[0].getRowStride()
let ur = planes[1].getRowStride()
let vr = planes[1].getPixelStride()
let starttime = new Date().getTime()
let av = this.YUV42088ToNV21Copy(a,b,c,width,height,yr,ur,vr)
decoderBuffToQr(av, width, height, this.getCamraRotation())
.then((res) => {
if (res != null&&t.isScannig ) {
if (res.text.length > 0) {
t.vibatarLong()
t.clearImageView();
t.setImageViewRusletBox(t.previewWidth, t.previewWidth, width, height, res as SCANNING_PHOTO_RESULT)
uni.setStorageSync('xMlkitScannigUResult_time_start', 'end:'+(new Date().getTime() - starttime).toString()+'ms')
t.closeCamra()
if (res.text.length == 1) {
uni.setStorageSync('xMlkitScannigUResult', res.text[0])
t.finish();
t.overridePendingTransition(R.anim.enter_anim, R.anim.exit_anim);
}
}
}
// image.close();
})
.catch(() => {
// this.imageUrl = null
})
setTimeout(function () {
this.isDecodreIng = false
}, this.scanSpeed);
image.close();
}
}
closeCamra() {
this.isScannig = false
if (this.captureSession != null) {
this.captureSession!.close();
this.captureSession = null;
this.cameraDevice?.close()
this.cameraDevice = null;
console.log("手动关闭相机");
}
}
openCamra() {
let t = this;
if (this.captureSession != null) {
return;
}
if (t.previewWidth == 0) {
//监听尺寸变化。
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.previewWidth = right - left;
t.previewHeight = bottom - top;
t.prviewTop = top
t.prviewLeft = left
v.removeOnLayoutChangeListener(this);
t.isCheckPermissions()
.then(() => {
let layoutParams = t.preview!.getLayoutParams() as ViewGroup.LayoutParams
layoutParams.height = t.previewWidth
t.preview!.layoutParams = layoutParams;
let layoutParamsbx = t.xiangjiBox!.getLayoutParams() as ViewGroup.LayoutParams
layoutParamsbx.height = t.previewWidth
t.xiangjiBox!.layoutParams = layoutParamsbx;
t.isScannig = true
t.clearImageView()
t.getCameraById();
t.openCamrae();
})
}
}
this.preview!.addOnLayoutChangeListener(new previewChangeRectadd())
} else {
t.isScannig = true
t.clearImageView()
t.getCameraById();
t.openCamrae();
}
}
// 设置图片上的小圆的识别位置。
setImageViewRusletBox(width : number, height : number, realWidth : number, realHeight : number, jieguo : SCANNING_PHOTO_RESULT) {
let t = this;
let wRatio = width / realWidth;
let hRatio = height / realHeight;
this.imageYuranBox!.setBackgroundColor(Color.argb(0,0,0,0))
this.xiangjiBox!.setBackgroundColor(Color.argb(0,0,0,0))
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
)
if (this.isScannig) {
layoutParams.topMargin = (hRatio * (item.centerY-60)).toInt() // 设置顶部边距
layoutParams.leftMargin = (wRatio * (item.centerX-60)).toInt() // 设置左边距
} else {
layoutParams.topMargin = (hRatio * item.centerY-60).toInt() // 设置顶部边距
layoutParams.leftMargin = (wRatio * item.centerX-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 {
uni.setStorageSync('xMlkitScannigUResult', rtxt)
t.finish()
t.overridePendingTransition(R.anim.enter_anim, R.anim.exit_anim);
} 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(v.getView())
if (this.isScannig) {
this.xiangjiBox!.addView(relativeLayout)
this.imgPointsBycanmarea.push(relativeLayout)
this.xiangjiBox!.setBackgroundColor(Color.argb(100,0,0,0))
} else {
this.imageYuranBox!.addView(relativeLayout)
this.imgPoints.push(relativeLayout)
this.imageYuranBox!.setBackgroundColor(Color.argb(100,0,0,0))
}
}
}
clearImageView() {
for (let i = 0; i < this.imgPointsBycanmarea.length; i++) {
this.xiangjiBox!.removeView(this.imgPointsBycanmarea[i])
}
for (let i = 0; i < this.imgPoints.length; i++) {
this.imageYuranBox!.removeView(this.imgPoints[i])
}
if (!this.isScannig) {
this.imageView!.setImageDrawable(null)
}
}
override onActivityResult(requestCode : Int, resultCode : Int, data ?: Intent) {
super.onActivityResult(requestCode, resultCode, data)
let t = this;
if (resultCode == Activity.RESULT_OK && data != null && data?.data != null) {
let selectedImageUri = data.data!
this.imageUrl = selectedImageUri
this.isSelectedImging = false
decoderUriPathToQrByUri(this.imageUrl!,(res:SCANNING_PHOTO_RESULT)=>{
t.isScannig = false
t.clearImageView();
t.imageView!.setImageURI(t.imageUrl!);
let reaLwidth = t.imageView!.drawable?.intrinsicWidth;
let reaLheight = t.imageView!.drawable?.intrinsicHeight;
if (reaLwidth != null && reaLheight != null) {
t.vibatarLong()
t.setImageViewRusletBox(t.imageView!.width, t.imageView!.height, reaLwidth!, reaLheight!, res!)
if (res.text.length == 1) {
uni.setStorageSync('xMlkitScannigUResult', res.text[0])
t.finish()
t.overridePendingTransition(R.anim.enter_anim, R.anim.exit_anim);
}
}
})
} else {
this.isSelectedImging = false
}
}
override onPause() : void {
super.onPause();
console.log("暂停扫码")
this.isScannig = false
if (this.captureSession != null) {
this.captureSession!.close();
this.captureSession = null;
this.cameraDevice?.close()
this.cameraDevice = null;
console.log("关闭相机");
}
// 停止图像处理线程
if (this.imageHandler != null) {
this.imageHandler!.getLooper()!.quitSafely();
}
}
override onResume() : void {
super.onResume();
let t = this;
if (this.captureSession != null || t.imageUrl != null || t.isSelectedImging) {
return;
}
console.log("恢复扫码")
this.openCamra();
}
openCamrae() {
if (this.camerManIds == null) return;
// let t = this;
class CamerCallTstasBack implements StateCallback {
constructor() {
}
override onOpened(camera : CameraDevice) : void {
console.log('打开成功')
cameraDevice = camera
createCameraPreviewSession()
}
override onDisconnected(camera : CameraDevice) : void {
console.log('连接失败')
cameraDevice?.close()
cameraDevice = null
}
override onError(camera : CameraDevice, error : Int) : void {
console.log('出错')
cameraDevice?.close()
cameraDevice = null
}
override onClosed(camera : CameraDevice) : void {
console.log('关闭')
cameraDevice?.close()
cameraDevice = null
}
}
this.camera_service.openCamera(this!.camerManIds as string, new CamerCallTstasBack(), null);
}
}
@Suppress("DEPRECATION")
class IntentRunable extends Runnable {
override run() {
let intent = new Intent(UTSAndroid.getUniActivity(), DemoActivity().javaClass);
UTSAndroid.getUniActivity()!.startActivityForResult(intent,1);
}
}
@Suppress("DEPRECATION")
export function gotoDemoActivity() : boolean {
// 跳转到一个新的界面
UTSAndroid.getUniActivity()!.runOnUiThread(new IntentRunable())
return true;
}