936 lines
30 KiB
Plaintext
936 lines
30 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"
|
|
import Gravity from 'android.view.Gravity';
|
|
|
|
// x
|
|
import ProcessCameraProvider from "androidx.camera.lifecycle.ProcessCameraProvider"
|
|
import PreviewView from "androidx.camera.view.PreviewView"
|
|
import CameraController from "androidx.camera.view.CameraController"
|
|
import CameraSelector from "androidx.camera.core.CameraSelector"
|
|
import Preview from 'androidx.camera.core.Preview'
|
|
import Camera from 'androidx.camera.core.Camera'
|
|
import LifecycleObserver from "androidx.lifecycle.LifecycleObserver"
|
|
import LifecycleOwner from "androidx.lifecycle.LifecycleOwner"
|
|
import OnLifecycleEvent from "androidx.lifecycle.OnLifecycleEvent"
|
|
import ImageAnalysis from 'androidx.camera.core.ImageAnalysis'
|
|
import ImageProxy from 'androidx.camera.core.ImageProxy'
|
|
import NonNull from 'androidx.annotation.NonNull'
|
|
import ExecutorService from 'java.util.concurrent.ExecutorService'
|
|
import Executors from 'java.util.concurrent.Executors'
|
|
import ContextCompat from 'androidx.core.content.ContextCompat';
|
|
import LifecycleRegistry from "androidx.lifecycle.LifecycleRegistry"
|
|
import Lifecycle from "androidx.lifecycle.Lifecycle"
|
|
|
|
|
|
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 implements LifecycleOwner {
|
|
scanSpeed = 500 //识别速度单位ms
|
|
scanPrevwSize = 800 //预期识别的像素大小,这是预期,会根据这个值找到相机支持的相近的相机尺寸。
|
|
lifecycleRegistry:LifecycleRegistry|null = null
|
|
context : Context;
|
|
camera_service : CameraManager;
|
|
cameraPermissioPass = false;
|
|
camerManIds = null as string | null
|
|
cameraDevice = null as null | CameraDevice
|
|
preview = null as null | LinearLayout;
|
|
captureSession = null as null | CameraCaptureSession
|
|
|
|
// 相机区域视图
|
|
cameraView : PreviewView | null = null;
|
|
cameraProvider : ProcessCameraProvider | null = null
|
|
camera : Camera | null = null;
|
|
cameraPreview : Preview | null = null;
|
|
|
|
|
|
|
|
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.lifecycleRegistry = new LifecycleRegistry(this);
|
|
this.lifecycleRegistry!.markState(Lifecycle.State.CREATED);
|
|
|
|
this.hideTitleBar()
|
|
this.setContentView(R.layout.demo_activity);
|
|
|
|
this.overridePendingTransition(R.anim.enter_anim, R.anim.exit_anim);
|
|
|
|
this.preview = this.findViewById<LinearLayout>(R.id.surface_camera_box);
|
|
this.preview!.removeView(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
|
|
)
|
|
|
|
|
|
this._createCameraView()
|
|
|
|
|
|
//监听尺寸变化。
|
|
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();
|
|
|
|
}
|
|
|
|
/** 创建相机预览区域 */
|
|
private _createCameraView() {
|
|
|
|
let sv = new PreviewView(this.context)
|
|
sv.scaleType = PreviewView.ScaleType.FILL_CENTER
|
|
// 让相机的高和宽与组件对齐。
|
|
let layaout = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
|
|
sv.setLayoutParams(layaout)
|
|
this.cameraView = sv;
|
|
|
|
this.preview!.addView(this.cameraView!)
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
// 在需要隐藏标题栏的地方调用
|
|
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)
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
|
|
|
|
// 设置相机对焦模式
|
|
// 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 {
|
|
|
|
return 0
|
|
}
|
|
|
|
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
|
|
}
|
|
getCorrectionMatrix(imageProxy : ImageProxy, previewView : PreviewView) : Matrix {
|
|
let cropRect = imageProxy.cropRect
|
|
let rotationDegrees = imageProxy.imageInfo.rotationDegrees
|
|
let matrix = new Matrix()
|
|
|
|
// A float array of the source vertices (crop rect) in clockwise order.
|
|
new FloatArray(8)
|
|
let source = floatArrayOf(cropRect.left.toFloat(),
|
|
cropRect.top.toFloat(),
|
|
cropRect.right.toFloat(),
|
|
cropRect.top.toFloat(),
|
|
cropRect.right.toFloat(),
|
|
cropRect.bottom.toFloat(),
|
|
cropRect.left.toFloat(),
|
|
cropRect.bottom.toFloat())
|
|
|
|
let d0 = (0).toFloat()
|
|
// A float array of the destination vertices in clockwise order.
|
|
let destination = floatArrayOf(d0,
|
|
d0,
|
|
previewView.width.toFloat(),
|
|
d0,
|
|
previewView.width.toFloat(),
|
|
previewView.height.toFloat(),
|
|
d0,
|
|
previewView.height.toFloat())
|
|
|
|
// The destination vertexes need to be shifted based on rotation degrees. The
|
|
// rotation degree represents the clockwise rotation needed to correct the image.
|
|
|
|
// Each vertex is represented by 2 float numbers in the vertices array.
|
|
let vertexSize = 2
|
|
// The destination needs to be shifted 1 vertex for every 90° rotation.
|
|
let shiftOffset = rotationDegrees / 90 * vertexSize;
|
|
let tempArray = destination.clone()
|
|
for (toIndex in source.indices) {
|
|
let fromIndex = (toIndex + shiftOffset) % source.size
|
|
destination[toIndex] = tempArray[fromIndex.toInt()]
|
|
}
|
|
let success = matrix.setPolyToPoly(source, 0, destination, 0, 4)
|
|
|
|
return matrix
|
|
}
|
|
|
|
qrDecoder(img : ImageProxy) {
|
|
let t = this;
|
|
let image = img.getImage();
|
|
if (!this.isDecodreIng&&image!=null) {
|
|
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 ) {
|
|
let matrix = t.getCorrectionMatrix(img, t.cameraView!)
|
|
if (res.text.length > 0) {
|
|
t.vibatarLong()
|
|
|
|
|
|
|
|
let ruslt = res as SCANNING_PHOTO_RESULT;
|
|
for (let i = 0; i < ruslt.bounds.length; i++) {
|
|
let item = ruslt.bounds[i]
|
|
let ponit = floatArrayOf(item.centerX.toFloat(), item.centerY.toFloat())
|
|
|
|
matrix.mapPoints(ponit)
|
|
|
|
item.centerX = ponit[0]
|
|
item.centerY = ponit[1]
|
|
|
|
}
|
|
|
|
t.setImageViewRusletBox(t.previewWidth, t.previewWidth, width, height, ruslt)
|
|
uni.setStorageSync('xMlkitScannigUResult_time_start', 'end:'+(new Date().getTime() - starttime).toString()+'ms')
|
|
t.closeCamra()
|
|
|
|
|
|
if (ruslt.text.length == 1) {
|
|
uni.setStorageSync('xMlkitScannigUResult', ruslt.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);
|
|
|
|
}
|
|
img.close();
|
|
}
|
|
closeCamra() {
|
|
|
|
|
|
|
|
this.cameraProvider?.unbindAll()
|
|
this.cameraPreview?.setSurfaceProvider(null)
|
|
|
|
|
|
|
|
this.clearImageView()
|
|
|
|
}
|
|
|
|
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.closeCamra();
|
|
t.openCamrae();
|
|
})
|
|
}
|
|
}
|
|
|
|
this.preview!.addOnLayoutChangeListener(new previewChangeRectadd())
|
|
} else {
|
|
t.isScannig = true
|
|
t.closeCamra();
|
|
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;
|
|
let btnwidth = 100;
|
|
let btnwidth2 = 50;
|
|
|
|
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 = (item.centerY- btnwidth2).toInt() // 设置顶部边距
|
|
layoutParams.leftMargin = (item.centerX- btnwidth2).toInt() // 设置左边距
|
|
} else {
|
|
layoutParams.topMargin = (hRatio * item.centerY-btnwidth2).toInt() // 设置顶部边距
|
|
layoutParams.leftMargin = (wRatio * item.centerX-btnwidth2).toInt() // 设置左边距
|
|
}
|
|
let relativeLayout = new RelativeLayout(t.context)
|
|
relativeLayout.layoutParams = layoutParams
|
|
|
|
const rtxt = jieguo.text[i]
|
|
|
|
|
|
let v = new xView(t.context)
|
|
v.setSize(btnwidth , btnwidth)
|
|
.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 onStart() {
|
|
super.onStart();
|
|
lifecycleRegistry!.markState(Lifecycle.State.STARTED);
|
|
}
|
|
|
|
override onDestroy() : void {
|
|
super.onDestroy();
|
|
this.closeCamra()
|
|
this.showTitleBar();
|
|
}
|
|
|
|
override onStop() {
|
|
lifecycleRegistry!.markState(Lifecycle.State.CREATED);
|
|
super.onStop();
|
|
}
|
|
|
|
|
|
override onPause() : void {
|
|
super.onPause();
|
|
lifecycleRegistry!.markState(Lifecycle.State.CREATED);
|
|
console.log("暂停扫码")
|
|
this.closeCamra()
|
|
}
|
|
|
|
override onResume() : void {
|
|
super.onResume();
|
|
lifecycleRegistry!.markState(Lifecycle.State.RESUMED);
|
|
let t = this;
|
|
if (this.captureSession != null || t.imageUrl != null || t.isSelectedImging) {
|
|
return;
|
|
}
|
|
console.log("恢复扫码")
|
|
this.closeCamra()
|
|
this.openCamra();
|
|
|
|
}
|
|
override getLifecycle(): Lifecycle {
|
|
return this.lifecycleRegistry!
|
|
}
|
|
openCamrae() {
|
|
let _this = this;
|
|
|
|
let cameraProviderFuture = ProcessCameraProvider.getInstance(this.context)
|
|
|
|
class IntentRunable extends Runnable {
|
|
override run() {
|
|
let cameraProvider : ProcessCameraProvider = cameraProviderFuture.get()
|
|
let previewView = _this.cameraView! as PreviewView
|
|
let cameraSelector : CameraSelector = new CameraSelector.Builder()
|
|
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
|
|
.build();
|
|
let preview : Preview = new Preview.Builder().build();
|
|
|
|
preview.setSurfaceProvider(previewView.getSurfaceProvider())
|
|
|
|
let imageAnalysis = new ImageAnalysis.Builder()
|
|
// .setTargetResolution(new Size(_this.viewBox_width.toInt(), _this.viewBox_height.toInt()))
|
|
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
|
|
.build();
|
|
class MyImageAnalyzer implements ImageAnalysis.Analyzer {
|
|
constructor() {
|
|
super()
|
|
}
|
|
override analyze(image : ImageProxy) {
|
|
_this.qrDecoder(image)
|
|
}
|
|
override getDefaultTargetResolution() : Size {
|
|
return new Size(_this.previewWidth.toInt(), _this.previewHeight.toInt());
|
|
}
|
|
|
|
}
|
|
let cameraExecutor = Executors.newSingleThreadExecutor();
|
|
imageAnalysis.setAnalyzer(cameraExecutor, new MyImageAnalyzer());
|
|
let camera : Camera = cameraProvider.bindToLifecycle(
|
|
_this as LifecycleOwner, cameraSelector, preview, imageAnalysis);
|
|
|
|
_this.camera = camera
|
|
_this.cameraPreview = preview
|
|
|
|
_this.cameraProvider = cameraProvider
|
|
_this.isDecodreIng = false
|
|
_this.isScannig = true
|
|
|
|
|
|
}
|
|
}
|
|
|
|
cameraProviderFuture.addListener(new IntentRunable(), ContextCompat.getMainExecutor(this.context))
|
|
}
|
|
}
|
|
|
|
|
|
@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;
|
|
|
|
} |