This commit is contained in:
2026-09-24 16:25:22 +08:00
commit 7428184f01
1198 changed files with 314515 additions and 0 deletions
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
package="io.dcloud.uni_modules.xMlkitScannigS">
<uses-permission android:name="android.permission.VIBRATE" />
<uses-feature android:name="android.hardware.camera.any" />
<uses-permission android:name="android.permission.CAMERA" />
<application>
<meta-data android:name="UTS_MLKIT_SCANNIG_xMlkitScannigS" android:value="xMlkitScannigS" />
<activity android:name="uts.sdk.modules.xMlkitScannigS.DemoActivity"></activity>
</application>
</manifest>
@@ -0,0 +1,951 @@
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 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"
import FrameLayout from 'android.widget.FrameLayout';
import ViewPropertyAnimator from 'android.view.ViewPropertyAnimator';
import { SCANNING_OPTIONS, CAMERA_PHOTO_SIZE, CALL_RESULT_FUN, Call_BACK, Call_BACK_TYPE, SCANNING_PHOTO_RESULT, SCANNING_PHOTO_RESULT_BOUND } from "../interface.uts"
type buffoptsPlanesType = {
data : ByteBuffer,
getRowStride : number,
getPixelStride : number,
}
type buffoptsType = {
width : number,
height : number,
format : number,
rect : Rect,
planes : buffoptsPlanesType[],
}
type ConverXy = {
x : number,
y : number,
minx : number,
maxx : number,
miny : number,
maxy : number,
}
type contaiaSize = {
width : number,
height : number
}
type contaiaCalseSize = {
scale : number,
offsetX : number,
offsetY : number
}
type contaiPointXy = {
x : number,
y : number
}
let parentView : RelativeLayout | null = null;
let maskDomId = 8586
function px2dp(n : number) : number {
const mets = UTSAndroid.getAppContext()!.resources!.getDisplayMetrics()
return mets.density * n
}
export class xCamera {
/** 手动震动服务 */
vibrator = null as null | Vibrator
context : Context;
// 识别点的区域
resultView : RelativeLayout | null = null;
// 存放小绿点的数组
imgPointsBycanmarea = [] as RelativeLayout[]
//相机容器
camreaLayouView:RelativeLayout|null = null;
// 图片容器层.
imageLayouView:RelativeLayout|null = null;
viewBox_width = 0
viewBox_height = 0
imageView = null as null | ImageView
imageUrl = null as null | Uri
isSelectedImging = false;
// 相机区域视图
cameraView : PreviewView | null = null;
cameraProvider : ProcessCameraProvider | null = null
camera : Camera | null = null;
cameraPreview : Preview | null = null;
// 当前是否在解析码制中。
isDecoderQring = false
// 当前相机是否正在预览中。
isOpeningCameraing = false;
callFunEvent : CALL_RESULT_FUN = (str : string) => { }
onlyCamera:boolean = false;
// 临时存放的识别成功的结果,防止重复扫码。
tempresultList = [] as string[];
activityresult = (requestCode : Int, resultCode : Int, data ?: Intent)=>{
let t = this;
if (resultCode == Activity.RESULT_OK && data != null && data?.data != null) {
let selectedImageUri = data.data!
this.camreaLayouView!.setVisibility(View.GONE)
this.imageUrl = selectedImageUri
this.isSelectedImging = false
decoderUriPathToQrByUri(this.imageUrl!,(res:SCANNING_PHOTO_RESULT)=>{
t.isOpeningCameraing = false
t.clearImageView();
t.imageView!.setImageURI(t.imageUrl!);
let matrix = t.getCorrectionMatrixByimgUri(t.imageView!, t.cameraView!,90)
let ruslt = res as SCANNING_PHOTO_RESULT;
if(ruslt.text.length>0){
t.setVibatar()
}
for (let i = 0; i < ruslt.bounds.length; i++) {
let item = ruslt.bounds[i]
let result = t.calcPoint(t.imageView!, t.cameraView!,item.centerX,item.centerY,0)
// let ponit = floatArrayOf(item.centerX.toFloat(), item.centerY.toFloat())
// matrix.mapPoints(ponit)
// item.centerX = ponit[0]
// item.centerY = ponit[1]
item.centerX = result.x
item.centerY = result.y
}
t.setImageViewRusletBox(ruslt)
if (ruslt.text.length == 1) {
t.callFunEvent(ruslt.text[0])
t.close()
}
})
} else {
this.isSelectedImging = false
}
}
constructor() {
this.context = UTSAndroid.getAppContext()! as Context
this.vibrator = this.context!.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator;
}
clearImageView() {
this.clearImagePonit();
if (!this.isOpeningCameraing) {
this.imageView!.setImageDrawable(null)
}
}
setVibatar() {
if (this.vibrator == null) return
// 检查设备是否支持震动
if (this.vibrator!.hasVibrator()) {
// 触发短暂的震动,持续时间为200毫秒
this.vibrator!.vibrate(80);
}
}
openCamera(call:CALL_RESULT_FUN,onlyCameras:boolean) {
this.onlyCamera = onlyCameras;
let t = this;
this.callFunEvent = call
if (parentView == null) {
this.createView();
t._openCamera()
} else {
this._openCamera()
}
}
close() {
UTSAndroid.offAppActivityResult(this.activityresult)
let decorView = UTSAndroid.getUniActivity()!.window.decorView as ViewGroup
this.closeCamera();
this.clearImagePonit();
if (parentView != null) {
decorView.removeView(parentView)
}
parentView = null;
}
private _openCamera() {
let _this = this;
this.closeCamera();
this.clearImagePonit();
let cameraProviderFuture = ProcessCameraProvider.getInstance(this.context)
this.camreaLayouView!.setVisibility(View.VISIBLE)
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(800, 800))
.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.viewBox_width.toInt(), _this.viewBox_height.toInt());
}
}
let cameraExecutor = Executors.newSingleThreadExecutor();
imageAnalysis.setAnalyzer(cameraExecutor, new MyImageAnalyzer());
let camera : Camera = cameraProvider.bindToLifecycle(
UTSAndroid.getUniActivity()! as LifecycleOwner, cameraSelector, preview, imageAnalysis);
_this.camera = camera
_this.cameraPreview = preview
_this.cameraProvider = cameraProvider
_this.isDecoderQring = false
_this.isOpeningCameraing = true
}
}
cameraProviderFuture.addListener(new IntentRunable(), ContextCompat.getMainExecutor(this.context))
}
closeCamera() {
this.cameraProvider?.unbindAll()
this.cameraPreview?.setSurfaceProvider(null)
this.isOpeningCameraing = false;
}
clearImagePonit(){
for (let i = 0; i < this.imgPointsBycanmarea.length; i++) {
this.resultView?.removeView(this.imgPointsBycanmarea[i])
}
}
private createView() {
let t = this;
let decorView = UTSAndroid.getUniActivity()!.window.decorView as ViewGroup
// 创建父容器。
parentView = new RelativeLayout(this.context);
parentView!.setId((maskDomId).toInt())
// parentView!.setOrientation(LinearLayout.VERTICAL)
parentView!.setBackgroundColor(Color.BLACK)
parentView!.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
))
class MaskerDomClickListsner extends View.OnTouchListener {
constructor() {
super();
}
//阻止事件向下穿透。
override onTouch(view : View, event : MotionEvent) : Boolean {
return true;
}
}
parentView!.setOnTouchListener(new MaskerDomClickListsner())
let continaer = new LinearLayout(this.context);
continaer.setOrientation(LinearLayout.VERTICAL)
continaer.setBackgroundColor(Color.TRANSPARENT)
let continaerLayparams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
continaer.setLayoutParams(continaerLayparams)
// 创建一个中间容器
let boxWrapParent = new LinearLayout(this.context);
boxWrapParent.setOrientation(LinearLayout.VERTICAL)
let boxLayparams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
boxLayparams.weight = (1).toFloat();
boxLayparams.height = 0;
boxWrapParent.setLayoutParams(boxLayparams)
// 创建一个定位容器
let boxWrap = new RelativeLayout(this.context);
boxWrap.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
))
boxWrapParent.addView(boxWrap)
//监听尺寸变化。
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;
}
}
boxWrap.addOnLayoutChangeListener(new previewChangeRectadd())
let rongqitop = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
rongqitop.topMargin = 0
rongqitop.leftMargin = 0
// 创建容器内小绿点容器。
this.resultView = new RelativeLayout(this.context);
this.resultView!.setLayoutParams(rongqitop)
// 图片容器
let imgPview = new RelativeLayout(this.context);
let rongqitop2 = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
rongqitop2.topMargin = 0
rongqitop2.leftMargin = 0
imgPview.setLayoutParams(rongqitop2)
// 创建一张图片
this.imageView = new ImageView(this.context);
this.imageView!.setScaleType(ImageView.ScaleType.CENTER_INSIDE)
this.imageView!.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT))
imgPview.addView(this.imageView!)
this.imageView!.setImageDrawable(null)
// 相机容器。
let cview = new RelativeLayout(this.context);
let rongqitop3 = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
// rongqitop3.width = 1000
// rongqitop3.height = 1000
cview.setLayoutParams(rongqitop3)
// 相机预览层。
let sv = new PreviewView(this.context)
// https://developer.android.google.cn/reference/androidx/camera/view/PreviewView.ScaleType?hl=en
sv.setScaleType(PreviewView.ScaleType.FILL_CENTER)
// 让相机的高和宽与组件对齐。
let layaout = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
sv.setLayoutParams(layaout)
this.cameraView = sv;
cview.addView(this.cameraView!)
// this.imageView!.setVisibility(View.INVISIBLE)
//相机容器
this.camreaLayouView = cview;
// 图片容器层.
this.imageLayouView= imgPview;
boxWrap.addView(imgPview)
boxWrap.addView(cview)
boxWrap.addView(this.resultView!)
// 创建底
let footerWrap = new LinearLayout(this.context);
footerWrap.setOrientation(LinearLayout.HORIZONTAL)
let footerWrapLayparams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
footerWrapLayparams.height = px2dp(100).toInt();
footerWrap.setLayoutParams(footerWrapLayparams)
// 创建选择相册
let photoView = new LinearLayout(this.context);
photoView.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
))
let xiangcheText = new TextView(this.context);
xiangcheText.setTextColor(Color.WHITE)
xiangcheText.setPadding(32, 52, 0, 0)
xiangcheText.setTextSize((16).toFloat())
xiangcheText.setGravity(Gravity.LEFT)
xiangcheText.setText(!this.onlyCamera?"相册选择":" ")
xiangcheText.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT))
class XiangcheClickListsner extends View.OnTouchListener {
constructor() {
super();
}
override onTouch(view : View, event : MotionEvent) : Boolean {
UTSAndroid.offAppActivityResult(t.activityresult)
if(event.getAction()==MotionEvent.ACTION_UP){
t.closeCamera()
t.isSelectedImging = true;
let intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
UTSAndroid.onAppActivityResult(t.activityresult);
UTSAndroid.getUniActivity()!.startActivityForResult(intent, 1)
}
return true;
}
}
if(!this.onlyCamera){
xiangcheText.setOnTouchListener(new XiangcheClickListsner())
}
photoView.addView(xiangcheText)
// 中间占位
let cneterView = new LinearLayout(this.context);
let cneterViewParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
cneterViewParams.weight = (1).toFloat();
cneterView.setLayoutParams(cneterViewParams)
// 扫一扫view
let scanView = new LinearLayout(this.context);
scanView.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
))
scanView.setGravity(Gravity.RIGHT)
let shaoyishaoText = new TextView(this.context);
shaoyishaoText.setTextColor(Color.WHITE)
shaoyishaoText.setPadding(0, 52, 32, 0)
shaoyishaoText.setTextSize((16).toFloat())
shaoyishaoText.setGravity(Gravity.RIGHT)
shaoyishaoText.setText("扫一扫")
shaoyishaoText.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT))
class ShaoyishaoTextClickListsner extends View.OnTouchListener {
constructor() {
super();
}
override onTouch(view : View, event : MotionEvent) : Boolean {
if(event.getAction()==MotionEvent.ACTION_UP){
t._openCamera()
}
return true;
}
}
shaoyishaoText.setOnTouchListener(new ShaoyishaoTextClickListsner())
scanView.addView(shaoyishaoText)
footerWrap.addView(photoView)
footerWrap.addView(cneterView)
footerWrap.addView(scanView)
// 创建一个顶部
let titleView = new RelativeLayout(this.context);
// titleView!.setOrientation(LinearLayout.HORIZONTAL)
// titleView!.setBackgroundColor(Color.RED)
let tplays = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
tplays.topMargin = px2dp(60).toInt()
tplays.leftMargin = px2dp(16).toInt()
titleView!.setLayoutParams(tplays)
// 创建一个返回按钮
let backBtn = new TextView(this.context);
backBtn.setTextColor(Color.WHITE)
backBtn.setTextSize((16).toFloat())
backBtn.setGravity(Gravity.BOTTOM)
backBtn.setText("返回")
backBtn.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT))
class BackDomClickListsner extends View.OnTouchListener {
constructor() {
super();
}
override onTouch(view : View, event : MotionEvent) : Boolean {
t.close();
return true;
}
}
backBtn.setOnTouchListener(new BackDomClickListsner())
titleView!.addView(backBtn)
continaer.addView(boxWrapParent)
continaer.addView(footerWrap)
parentView!.addView(continaer)
parentView!.addView(titleView)
decorView.addView(parentView!)
// 增加动画
try {
let s0 = (0).toFloat()
let s1 = (1).toFloat()
parentView!.setAlpha(s0)
parentView!.setScaleX(s0)
parentView!.setScaleY(s0)
let ani = parentView!.animate() as ViewPropertyAnimator;
ani.alpha(s1)
.scaleX(s1)
.scaleY(s1)
.setDuration(250)
.start()
} catch (e) {
//TODO handle the exception
}
}
// 设置图片上的小圆的识别位置。
setImageViewRusletBox(jieguo : SCANNING_PHOTO_RESULT) {
let t = this;
let btnwidth = 100;
let btnwidth2 = 50;
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 = item.centerX
let y = item.centerY
layoutParams.topMargin = (y - btnwidth2).toInt() // 设置顶部边距
layoutParams.leftMargin = (x - 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 {
// t.callFunEvent([rtxt], 'click')
t.callFunEvent(rtxt)
t.close()
} 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)
}
}
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
}
getCorrectionMatrixByimgUri(imgView : ImageView, previewView : PreviewView,rotationDegrees:number) : Matrix {
let matrix = new Matrix()
let imgWidt = imgView.drawable!.intrinsicWidth as number
let imgHeight = imgView.drawable!.intrinsicHeight as number
let d0 = (0).toFloat()
let source = floatArrayOf(
d0,
d0,
imgWidt.toFloat(),
d0,
imgWidt.toFloat(),
imgHeight.toFloat(),
d0,
imgHeight.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()
)
let vertexSize = 2
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
}
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.
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()
)
let vertexSize = 2
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
}
calcPoint(imgView : ImageView, previewView : PreviewView, image_point_x : number, image_point_y : number,degration:number) : ConverXy {
const image_w = imgView.drawable!.intrinsicWidth as number
const image_h = imgView.drawable!.intrinsicHeight as number
const c_w = previewView.width;
const c_h = previewView.height;
function scaleAndCenterContent(
container : contaiaSize,
content : contaiaSize
) : contaiaCalseSize {
// 计算缩放比例
const scaleWidth = container.width / content.width;
const scaleHeight = container.height / content.height;
const scale = Math.min(scaleWidth, scaleHeight);
// 计算居中偏移量
const offsetX = (container.width - content.width * scale) / 2;
const offsetY = (container.height - content.height * scale) / 2;
return { scale, offsetX, offsetY } as contaiaCalseSize;
}
function mapContentCoordinatesToContainer(
contentX : number,
contentY : number,
calcsize : contaiaCalseSize
) : contaiPointXy {
// 映射内容层坐标到容器层坐标
const containerX = contentX * calcsize.scale + calcsize.offsetX;
const containerY = contentY * calcsize.scale + calcsize.offsetY;
return { x: containerX, y: containerY } as contaiPointXy;
}
let rc = scaleAndCenterContent(
{ width: c_w, height: c_h } as contaiaSize,
{ width: image_h, height: image_w } as contaiaSize
)
if(degration==0){
rc = scaleAndCenterContent(
{ width: c_w, height: c_h } as contaiaSize,
{ width: image_w, height: image_h } as contaiaSize
)
}
let result = mapContentCoordinatesToContainer(image_point_y, image_point_x, rc)
if(degration==0){
result = mapContentCoordinatesToContainer(image_point_x,image_point_y,rc)
}
let nexyxcov = {
x: c_w - result.x, y: result.y, minx: 0, maxx: 0, miny: 0, maxy: 0
} as ConverXy
if(degration==0){
nexyxcov = {
x: result.x, y: result.y, minx: 0, maxx: 0, miny: 0, maxy: 0
} as ConverXy
}
return nexyxcov
}
qrDecoder(img : ImageProxy) {
let t = this;
let image = img.getImage();
if (!this.isDecoderQring&&image!=null&&t.isOpeningCameraing) {
this.isDecoderQring = 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)
let rotationDegrees = img.imageInfo.rotationDegrees
decoderBuffToQr(av, width, height, 0)
.then((res) => {
if (res != null ) {
let matrix = t.getCorrectionMatrix(img, t.cameraView!)
if (res.text.length > 0) {
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())
// let xy = t.covertXy(img, t.cameraView!,item.centerX,item.centerY);
matrix.mapPoints(ponit)
item.centerX = ponit[0]
item.centerY = ponit[1]
// item.centerX = xy[0]
// item.centerY = xy[1]
// console.log(item.centerX ,item.centerY)
}
if(ruslt.text.length>0){
t.setVibatar()
}
t.setImageViewRusletBox(ruslt)
t.closeCamera()
if (ruslt.text.length == 1) {
t.callFunEvent(ruslt.text[0])
t.close()
}
}else{
t.isDecoderQring = false
}
}
// image.close();
})
.catch(() => {
t.isDecoderQring = false
})
}
img.close();
}
}
@@ -0,0 +1,951 @@
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 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"
import FrameLayout from 'android.widget.FrameLayout';
import ViewPropertyAnimator from 'android.view.ViewPropertyAnimator';
import { SCANNING_OPTIONS, CAMERA_PHOTO_SIZE, CALL_RESULT_FUN, Call_BACK, Call_BACK_TYPE, SCANNING_PHOTO_RESULT, SCANNING_PHOTO_RESULT_BOUND } from "../interface.uts"
type buffoptsPlanesType = {
data : ByteBuffer,
getRowStride : number,
getPixelStride : number,
}
type buffoptsType = {
width : number,
height : number,
format : number,
rect : Rect,
planes : buffoptsPlanesType[],
}
type ConverXy = {
x : number,
y : number,
minx : number,
maxx : number,
miny : number,
maxy : number,
}
type contaiaSize = {
width : number,
height : number
}
type contaiaCalseSize = {
scale : number,
offsetX : number,
offsetY : number
}
type contaiPointXy = {
x : number,
y : number
}
let parentView : RelativeLayout | null = null;
let maskDomId = 8586
function px2dp(n : number) : number {
const mets = UTSAndroid.getAppContext()!.resources!.getDisplayMetrics()
return mets.density * n
}
export class xCamera {
/** 手动震动服务 */
vibrator = null as null | Vibrator
context : Context;
// 识别点的区域
resultView : RelativeLayout | null = null;
// 存放小绿点的数组
imgPointsBycanmarea = [] as RelativeLayout[]
//相机容器
camreaLayouView:RelativeLayout|null = null;
// 图片容器层.
imageLayouView:RelativeLayout|null = null;
viewBox_width = 0
viewBox_height = 0
imageView = null as null | ImageView
imageUrl = null as null | Uri
isSelectedImging = false;
// 相机区域视图
cameraView : PreviewView | null = null;
cameraProvider : ProcessCameraProvider | null = null
camera : Camera | null = null;
cameraPreview : Preview | null = null;
// 当前是否在解析码制中。
isDecoderQring = false
// 当前相机是否正在预览中。
isOpeningCameraing = false;
callFunEvent : CALL_RESULT_FUN = (str : string) => { }
onlyCamera:boolean = false;
// 临时存放的识别成功的结果,防止重复扫码。
tempresultList = [] as string[];
activityresult = (requestCode : Int, resultCode : Int, data ?: Intent)=>{
let t = this;
if (resultCode == Activity.RESULT_OK && data != null && data?.data != null) {
let selectedImageUri = data.data!
this.camreaLayouView!.setVisibility(View.GONE)
this.imageUrl = selectedImageUri
this.isSelectedImging = false
decoderUriPathToQrByUri(this.imageUrl!,(res:SCANNING_PHOTO_RESULT)=>{
t.isOpeningCameraing = false
t.clearImageView();
t.imageView!.setImageURI(t.imageUrl!);
let matrix = t.getCorrectionMatrixByimgUri(t.imageView!, t.cameraView!,90)
let ruslt = res as SCANNING_PHOTO_RESULT;
if(ruslt.text.length>0){
t.setVibatar()
}
for (let i = 0; i < ruslt.bounds.length; i++) {
let item = ruslt.bounds[i]
let result = t.calcPoint(t.imageView!, t.cameraView!,item.centerX,item.centerY,0)
// let ponit = floatArrayOf(item.centerX.toFloat(), item.centerY.toFloat())
// matrix.mapPoints(ponit)
// item.centerX = ponit[0]
// item.centerY = ponit[1]
item.centerX = result.x
item.centerY = result.y
}
t.setImageViewRusletBox(ruslt)
if (ruslt.text.length == 1) {
t.callFunEvent(ruslt.text[0])
t.close()
}
})
} else {
this.isSelectedImging = false
}
}
constructor() {
this.context = UTSAndroid.getAppContext()! as Context
this.vibrator = this.context!.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator;
}
clearImageView() {
this.clearImagePonit();
if (!this.isOpeningCameraing) {
this.imageView!.setImageDrawable(null)
}
}
setVibatar() {
if (this.vibrator == null) return
// 检查设备是否支持震动
if (this.vibrator!.hasVibrator()) {
// 触发短暂的震动,持续时间为200毫秒
this.vibrator!.vibrate(80);
}
}
openCamera(call:CALL_RESULT_FUN,onlyCameras:boolean) {
this.onlyCamera = onlyCameras;
let t = this;
this.callFunEvent = call
if (parentView == null) {
this.createView();
t._openCamera()
} else {
this._openCamera()
}
}
close() {
UTSAndroid.offAppActivityResult(this.activityresult)
let decorView = UTSAndroid.getUniActivity()!.window.decorView as ViewGroup
this.closeCamera();
this.clearImagePonit();
if (parentView != null) {
decorView.removeView(parentView)
}
parentView = null;
}
private _openCamera() {
let _this = this;
this.closeCamera();
this.clearImagePonit();
let cameraProviderFuture = ProcessCameraProvider.getInstance(this.context)
this.camreaLayouView!.setVisibility(View.VISIBLE)
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(800, 800))
.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.viewBox_width.toInt(), _this.viewBox_height.toInt());
}
}
let cameraExecutor = Executors.newSingleThreadExecutor();
imageAnalysis.setAnalyzer(cameraExecutor, new MyImageAnalyzer());
let camera : Camera = cameraProvider.bindToLifecycle(
UTSAndroid.getUniActivity()! as LifecycleOwner, cameraSelector, preview, imageAnalysis);
_this.camera = camera
_this.cameraPreview = preview
_this.cameraProvider = cameraProvider
_this.isDecoderQring = false
_this.isOpeningCameraing = true
}
}
cameraProviderFuture.addListener(new IntentRunable(), ContextCompat.getMainExecutor(this.context))
}
closeCamera() {
this.cameraProvider?.unbindAll()
this.cameraPreview?.setSurfaceProvider(null)
this.isOpeningCameraing = false;
}
clearImagePonit(){
for (let i = 0; i < this.imgPointsBycanmarea.length; i++) {
this.resultView?.removeView(this.imgPointsBycanmarea[i])
}
}
private createView() {
let t = this;
let decorView = UTSAndroid.getUniActivity()!.window.decorView as ViewGroup
// 创建父容器。
parentView = new RelativeLayout(this.context);
parentView!.setId((maskDomId).toInt())
// parentView!.setOrientation(LinearLayout.VERTICAL)
parentView!.setBackgroundColor(Color.BLACK)
parentView!.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
))
class MaskerDomClickListsner extends View.OnTouchListener {
constructor() {
super();
}
//阻止事件向下穿透。
override onTouch(view : View, event : MotionEvent) : Boolean {
return true;
}
}
parentView!.setOnTouchListener(new MaskerDomClickListsner())
let continaer = new LinearLayout(this.context);
continaer.setOrientation(LinearLayout.VERTICAL)
continaer.setBackgroundColor(Color.TRANSPARENT)
let continaerLayparams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
continaer.setLayoutParams(continaerLayparams)
// 创建一个中间容器
let boxWrapParent = new LinearLayout(this.context);
boxWrapParent.setOrientation(LinearLayout.VERTICAL)
let boxLayparams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
boxLayparams.weight = (1).toFloat();
boxLayparams.height = 0;
boxWrapParent.setLayoutParams(boxLayparams)
// 创建一个定位容器
let boxWrap = new RelativeLayout(this.context);
boxWrap.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
))
boxWrapParent.addView(boxWrap)
//监听尺寸变化。
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;
}
}
boxWrap.addOnLayoutChangeListener(new previewChangeRectadd())
let rongqitop = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
rongqitop.topMargin = 0
rongqitop.leftMargin = 0
// 创建容器内小绿点容器。
this.resultView = new RelativeLayout(this.context);
this.resultView!.setLayoutParams(rongqitop)
// 图片容器
let imgPview = new RelativeLayout(this.context);
let rongqitop2 = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
rongqitop2.topMargin = 0
rongqitop2.leftMargin = 0
imgPview.setLayoutParams(rongqitop2)
// 创建一张图片
this.imageView = new ImageView(this.context);
this.imageView!.setScaleType(ImageView.ScaleType.CENTER_INSIDE)
this.imageView!.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT))
imgPview.addView(this.imageView!)
this.imageView!.setImageDrawable(null)
// 相机容器。
let cview = new RelativeLayout(this.context);
let rongqitop3 = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
// rongqitop3.width = 1000
// rongqitop3.height = 1000
cview.setLayoutParams(rongqitop3)
// 相机预览层。
let sv = new PreviewView(this.context)
// https://developer.android.google.cn/reference/androidx/camera/view/PreviewView.ScaleType?hl=en
sv.setScaleType(PreviewView.ScaleType.FILL_CENTER)
// 让相机的高和宽与组件对齐。
let layaout = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
sv.setLayoutParams(layaout)
this.cameraView = sv;
cview.addView(this.cameraView!)
// this.imageView!.setVisibility(View.INVISIBLE)
//相机容器
this.camreaLayouView = cview;
// 图片容器层.
this.imageLayouView= imgPview;
boxWrap.addView(imgPview)
boxWrap.addView(cview)
boxWrap.addView(this.resultView!)
// 创建底
let footerWrap = new LinearLayout(this.context);
footerWrap.setOrientation(LinearLayout.HORIZONTAL)
let footerWrapLayparams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
footerWrapLayparams.height = px2dp(100).toInt();
footerWrap.setLayoutParams(footerWrapLayparams)
// 创建选择相册
let photoView = new LinearLayout(this.context);
photoView.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
))
let xiangcheText = new TextView(this.context);
xiangcheText.setTextColor(Color.WHITE)
xiangcheText.setPadding(32, 52, 0, 0)
xiangcheText.setTextSize((16).toFloat())
xiangcheText.setGravity(Gravity.LEFT)
xiangcheText.setText(!this.onlyCamera?"相册选择":" ")
xiangcheText.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT))
class XiangcheClickListsner extends View.OnTouchListener {
constructor() {
super();
}
override onTouch(view : View, event : MotionEvent) : Boolean {
UTSAndroid.offAppActivityResult(t.activityresult)
if(event.getAction()==MotionEvent.ACTION_UP){
t.closeCamera()
t.isSelectedImging = true;
let intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
UTSAndroid.onAppActivityResult(t.activityresult);
UTSAndroid.getUniActivity()!.startActivityForResult(intent, 1)
}
return true;
}
}
if(!this.onlyCamera){
xiangcheText.setOnTouchListener(new XiangcheClickListsner())
}
photoView.addView(xiangcheText)
// 中间占位
let cneterView = new LinearLayout(this.context);
let cneterViewParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
cneterViewParams.weight = (1).toFloat();
cneterView.setLayoutParams(cneterViewParams)
// 扫一扫view
let scanView = new LinearLayout(this.context);
scanView.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
))
scanView.setGravity(Gravity.RIGHT)
let shaoyishaoText = new TextView(this.context);
shaoyishaoText.setTextColor(Color.WHITE)
shaoyishaoText.setPadding(0, 52, 32, 0)
shaoyishaoText.setTextSize((16).toFloat())
shaoyishaoText.setGravity(Gravity.RIGHT)
shaoyishaoText.setText("扫一扫")
shaoyishaoText.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT))
class ShaoyishaoTextClickListsner extends View.OnTouchListener {
constructor() {
super();
}
override onTouch(view : View, event : MotionEvent) : Boolean {
if(event.getAction()==MotionEvent.ACTION_UP){
t._openCamera()
}
return true;
}
}
shaoyishaoText.setOnTouchListener(new ShaoyishaoTextClickListsner())
scanView.addView(shaoyishaoText)
footerWrap.addView(photoView)
footerWrap.addView(cneterView)
footerWrap.addView(scanView)
// 创建一个顶部
let titleView = new RelativeLayout(this.context);
// titleView!.setOrientation(LinearLayout.HORIZONTAL)
// titleView!.setBackgroundColor(Color.RED)
let tplays = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
tplays.topMargin = px2dp(60).toInt()
tplays.leftMargin = px2dp(16).toInt()
titleView!.setLayoutParams(tplays)
// 创建一个返回按钮
let backBtn = new TextView(this.context);
backBtn.setTextColor(Color.WHITE)
backBtn.setTextSize((16).toFloat())
backBtn.setGravity(Gravity.BOTTOM)
backBtn.setText("返回")
backBtn.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT))
class BackDomClickListsner extends View.OnTouchListener {
constructor() {
super();
}
override onTouch(view : View, event : MotionEvent) : Boolean {
t.close();
return true;
}
}
backBtn.setOnTouchListener(new BackDomClickListsner())
titleView!.addView(backBtn)
continaer.addView(boxWrapParent)
continaer.addView(footerWrap)
parentView!.addView(continaer)
parentView!.addView(titleView)
decorView.addView(parentView!)
// 增加动画
try {
let s0 = (0).toFloat()
let s1 = (1).toFloat()
parentView!.setAlpha(s0)
parentView!.setScaleX(s0)
parentView!.setScaleY(s0)
let ani = parentView!.animate() as ViewPropertyAnimator;
ani.alpha(s1)
.scaleX(s1)
.scaleY(s1)
.setDuration(250)
.start()
} catch (e) {
//TODO handle the exception
}
}
// 设置图片上的小圆的识别位置。
setImageViewRusletBox(jieguo : SCANNING_PHOTO_RESULT) {
let t = this;
let btnwidth = 100;
let btnwidth2 = 50;
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 = item.centerX
let y = item.centerY
layoutParams.topMargin = (y - btnwidth2).toInt() // 设置顶部边距
layoutParams.leftMargin = (x - 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 {
// t.callFunEvent([rtxt], 'click')
t.callFunEvent(rtxt)
t.close()
} 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)
}
}
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
}
getCorrectionMatrixByimgUri(imgView : ImageView, previewView : PreviewView,rotationDegrees:number) : Matrix {
let matrix = new Matrix()
let imgWidt = imgView.drawable!.intrinsicWidth as number
let imgHeight = imgView.drawable!.intrinsicHeight as number
let d0 = (0).toFloat()
let source = floatArrayOf(
d0,
d0,
imgWidt.toFloat(),
d0,
imgWidt.toFloat(),
imgHeight.toFloat(),
d0,
imgHeight.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()
)
let vertexSize = 2
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
}
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.
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()
)
let vertexSize = 2
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
}
calcPoint(imgView : ImageView, previewView : PreviewView, image_point_x : number, image_point_y : number,degration:number) : ConverXy {
const image_w = imgView.drawable!.intrinsicWidth as number
const image_h = imgView.drawable!.intrinsicHeight as number
const c_w = previewView.width;
const c_h = previewView.height;
function scaleAndCenterContent(
container : contaiaSize,
content : contaiaSize
) : contaiaCalseSize {
// 计算缩放比例
const scaleWidth = container.width / content.width;
const scaleHeight = container.height / content.height;
const scale = Math.min(scaleWidth, scaleHeight);
// 计算居中偏移量
const offsetX = (container.width - content.width * scale) / 2;
const offsetY = (container.height - content.height * scale) / 2;
return { scale, offsetX, offsetY } as contaiaCalseSize;
}
function mapContentCoordinatesToContainer(
contentX : number,
contentY : number,
calcsize : contaiaCalseSize
) : contaiPointXy {
// 映射内容层坐标到容器层坐标
const containerX = contentX * calcsize.scale + calcsize.offsetX;
const containerY = contentY * calcsize.scale + calcsize.offsetY;
return { x: containerX, y: containerY } as contaiPointXy;
}
let rc = scaleAndCenterContent(
{ width: c_w, height: c_h } as contaiaSize,
{ width: image_h, height: image_w } as contaiaSize
)
if(degration==0){
rc = scaleAndCenterContent(
{ width: c_w, height: c_h } as contaiaSize,
{ width: image_w, height: image_h } as contaiaSize
)
}
let result = mapContentCoordinatesToContainer(image_point_y, image_point_x, rc)
if(degration==0){
result = mapContentCoordinatesToContainer(image_point_x,image_point_y,rc)
}
let nexyxcov = {
x: c_w - result.x, y: result.y, minx: 0, maxx: 0, miny: 0, maxy: 0
} as ConverXy
if(degration==0){
nexyxcov = {
x: result.x, y: result.y, minx: 0, maxx: 0, miny: 0, maxy: 0
} as ConverXy
}
return nexyxcov
}
qrDecoder(img : ImageProxy) {
let t = this;
let image = img.getImage();
if (!this.isDecoderQring&&image!=null&&t.isOpeningCameraing) {
this.isDecoderQring = 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)
let rotationDegrees = img.imageInfo.rotationDegrees
decoderBuffToQr(av, width, height, 0)
.then((res) => {
if (res != null ) {
let matrix = t.getCorrectionMatrix(img, t.cameraView!)
if (res.text.length > 0) {
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())
// let xy = t.covertXy(img, t.cameraView!,item.centerX,item.centerY);
matrix.mapPoints(ponit)
item.centerX = ponit[0]
item.centerY = ponit[1]
// item.centerX = xy[0]
// item.centerY = xy[1]
// console.log(item.centerX ,item.centerY)
}
if(ruslt.text.length>0){
t.setVibatar()
}
t.setImageViewRusletBox(ruslt)
t.closeCamera()
if (ruslt.text.length == 1) {
t.callFunEvent(ruslt.text[0])
t.close()
}
}else{
t.isDecoderQring = false
}
}
// image.close();
})
.catch(() => {
t.isDecoderQring = false
})
}
img.close();
}
}
@@ -0,0 +1,936 @@
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;
}
@@ -0,0 +1,979 @@
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;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,13 @@
{
"minSdkVersion": "21",
"dependencies":[
"com.google.mlkit:barcode-scanning:17.2.0",
"androidx.camera:camera-core:1.3.4",
"androidx.camera:camera-camera2:1.3.4",
"androidx.camera:camera-lifecycle:1.3.4",
"androidx.camera:camera-video:1.3.4",
"androidx.camera:camera-view:1.3.4",
"androidx.camera:camera-extensions:1.3.4"
]
}
@@ -0,0 +1,382 @@
/**
* 引用 Android 系统库,示例如下:
* import { Context } from "android.content.Context";
* [可选实现,按需引入]
*/
/* 引入 interface.uts 文件中定义的变量 */
import { SCANNING_PHOTO_RESULT, SCANNING_PHOTO_RESULT_BOUND, SCANNING_PHOTO_RESULT_POS } from '../interface.uts';
/* 引入 unierror.uts 文件中定义的变量 */
import Intent from 'android.content.Intent';
import Context from 'android.content.Context'
import IOException from 'java.io.IOException'
import InputStream from 'java.io.InputStream'
import Uri from 'android.net.Uri';
import File from 'java.io.File'
import Bitmap from "android.graphics.Bitmap"
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 Task from "com.google.android.gms.tasks.Task"
import List from "java.util.List"
import ByteBuffer from 'java.nio.ByteBuffer'
import ImageFormat from 'android.graphics.ImageFormat'
import {xCamera} from "./camera.uts"
import {CALL_RESULT_FUN} from "../interface.uts"
import {gotoDemoActivity} from "./cameraByx.uts"
const camera = new xCamera()
/**
* 从相册中选取图片进行解码
* @public chooseImageDecodeQr
*/
export function chooseImage() : Promise<SCANNING_PHOTO_RESULT | null> {
return new Promise((res) => {
uni.chooseImage({
count: 1,
sourceType:['album'],
albumMode:"system",
success(evt : ChooseImageSuccess) {
console.log('**')
if (evt.tempFilePaths.length > 0) {
console.log(evt, "----")
const imgs = evt.tempFilePaths[0] as string;
try {
const decodedPath = Uri.decode(imgs).substring(7)
const file = File(decodedPath)
let image = InputImage.fromFilePath(UTSAndroid.getAppContext() as Context, Uri.fromFile(file));
let scanner = BarcodeScanning.getClient();
scanner.process(image)
.addOnSuccessListener((barcodes) => {
let listpos = [] as number[][][]
let bounedlist = [] as SCANNING_PHOTO_RESULT_BOUND[]
let rulstText = [] as string[]
for (barcode in barcodes) {
// 位置信息
let bounds = barcode.boundingBox
// 坐标
let corners = barcode.cornerPoints
// 解析的内容
let rawValue = barcode.rawValue
// 码的格式
let valueType = barcode.valueType
if (rawValue != null && bounds != null && corners != null) {
let iminfo = { width: bounds!.width(), height: bounds!.height(), centerX: bounds!.centerX(), centerY: bounds!.centerY() } as SCANNING_PHOTO_RESULT_BOUND
let posinfo = [] as number[][]
for (pos in corners) {
posinfo.push([pos!.x as number, pos!.y as number] as number[])
}
listpos.push(posinfo)
bounedlist.push(iminfo)
rulstText.push(rawValue)
}
}
res({
url: imgs,
position: listpos,
bounds: bounedlist,
text: rulstText,
imgWidth:image.getWidth(),
imgHeight:image.getHeight()
} as SCANNING_PHOTO_RESULT)
})
} catch (e : IOException) {
res(null)
}
}
},
fail() {
res(null)
}
})
})
}
/**
* 给定图片路径解析图片。
* @public decoderUriPathToQr
*/
export function decoderUriPathToQr(filePath : string,callback:(rs:SCANNING_PHOTO_RESULT)=>void) {
try{
let uri = UTSAndroid.getFileProviderUri(new File(filePath))!
let image = InputImage.fromFilePath(UTSAndroid.getAppContext() as Context, uri);
let scanner = BarcodeScanning.getClient();
scanner.process(image)
.addOnSuccessListener((barcodes) => {
let listpos = [] as number[][][]
let bounedlist = [] as SCANNING_PHOTO_RESULT_BOUND[]
let rulstText = [] as string[]
for (barcode in barcodes) {
// 位置信息
let bounds = barcode.boundingBox
// 坐标
let corners = barcode.cornerPoints
// 解析的内容
let rawValue = barcode.rawValue
// 码的格式
let valueType = barcode.valueType
if (rawValue != null && bounds != null && corners != null) {
let iminfo = { width: bounds!.width(), height: bounds!.height(), centerX: bounds!.centerX(), centerY: bounds!.centerY() } as SCANNING_PHOTO_RESULT_BOUND
let posinfo = [] as number[][]
for (pos in corners) {
posinfo.push([pos!.x as number, pos!.y as number] as number[])
}
listpos.push(posinfo)
bounedlist.push(iminfo)
rulstText.push(rawValue)
}
}
callback({
url: "",
position: listpos,
bounds: bounedlist,
text: rulstText,
imgWidth:image.getWidth(),
imgHeight:image.getHeight()
} as SCANNING_PHOTO_RESULT)
})
}catch(e){
console.error(e)
}
}
/**
* 给定图片路径Uri解析图片。
* @public decoderUriPathToQr
*/
export function decoderUriPathToQrByUri(filePath : Uri,callback:(rs:SCANNING_PHOTO_RESULT)=>void) {
try{
let image = InputImage.fromFilePath(UTSAndroid.getAppContext() as Context, filePath);
let options = BarcodeScannerOptions.Builder()
.setBarcodeFormats(
Barcode.FORMAT_QR_CODE,
Barcode.FORMAT_EAN_13,
Barcode.FORMAT_CODE_128,
Barcode.FORMAT_CODABAR
)
.build()
let scanner = BarcodeScanning.getClient(options);
scanner.process(image)
.addOnSuccessListener((barcodes) => {
let listpos = [] as number[][][]
let bounedlist = [] as SCANNING_PHOTO_RESULT_BOUND[]
let rulstText = [] as string[]
for (barcode in barcodes) {
// 位置信息
let bounds = barcode.boundingBox
// 坐标
let corners = barcode.cornerPoints
// 解析的内容
let rawValue = barcode.rawValue
// 码的格式
let valueType = barcode.valueType
if (rawValue != null && bounds != null && corners != null) {
let iminfo = { width: bounds!.width(), height: bounds!.height(), centerX: bounds!.centerX(), centerY: bounds!.centerY() } as SCANNING_PHOTO_RESULT_BOUND
let posinfo = [] as number[][]
for (pos in corners) {
posinfo.push([pos!.x as number, pos!.y as number] as number[])
}
listpos.push(posinfo)
bounedlist.push(iminfo)
rulstText.push(rawValue)
}
}
callback({
url: "",
position: listpos,
bounds: bounedlist,
text: rulstText,
imgWidth:image.getWidth(),
imgHeight:image.getHeight()
} as SCANNING_PHOTO_RESULT)
})
}catch(e){
console.error(e)
}
}
/**
* 给定ImageBuffer
* @public decoderBuffToQr
*/
export function decoderBuffToQr(bitmap : ByteArray, width : Number, height : Number, ration : Number) : Promise<SCANNING_PHOTO_RESULT | null> {
return new Promise((res) => {
let options = BarcodeScannerOptions.Builder()
.setBarcodeFormats(
Barcode.FORMAT_QR_CODE,
Barcode.FORMAT_EAN_13,
Barcode.FORMAT_CODE_128,
Barcode.FORMAT_CODABAR
)
.build()
let image = InputImage.fromByteArray(bitmap, width.toInt(), height.toInt(), ration.toInt(), InputImage.IMAGE_FORMAT_NV21)
// 如果只扫qr开启这行,速度快一点。
let scanner = BarcodeScanning.getClient(options);
// let scanner = BarcodeScanning.getClient();
scanner.process(image)
.addOnSuccessListener((barcodes) => {
let listpos = [] as number[][][]
let bounedlist = [] as SCANNING_PHOTO_RESULT_BOUND[]
let rulstText = [] as string[]
for (barcode in barcodes) {
// 位置信息
let bounds = barcode.boundingBox
// 坐标
let corners = barcode.cornerPoints
// 解析的内容
let rawValue = barcode.rawValue
// 码的格式
let valueType = barcode.valueType
if (rawValue != null && bounds != null && corners != null) {
let iminfo = { width: bounds!.width(), height: bounds!.height(), centerX: bounds!.centerX(), centerY: bounds!.centerY() } as SCANNING_PHOTO_RESULT_BOUND
let posinfo = [] as number[][]
for (pos in corners) {
posinfo.push([pos!.x as number, pos!.y as number] as number[])
}
listpos.push(posinfo)
bounedlist.push(iminfo)
rulstText.push(rawValue)
}
}
res({
url: "",
position: listpos,
bounds: bounedlist,
text: rulstText,
imgWidth:image.getWidth(),
imgHeight:image.getHeight()
} as SCANNING_PHOTO_RESULT)
})
.addOnFailureListener((e) => {
res(null)
})
})
}
/**
* 提供一个网络图片地址进行解码
* @public urlPathDecoderQr
*/
export function urlPathDecoderQr(url : String,callback:(rs:SCANNING_PHOTO_RESULT)=>void) {
uni.downloadFile({
url,
success(res) {
const contentResolver = UTSAndroid.getAppContext()!.contentResolver
let decodedPath = res.tempFilePath
if (res.tempFilePath.indexOf('file://') > -1) {
decodedPath = Uri.decode(res.tempFilePath).substring(7)
}
decoderUriPathToQr(decodedPath,callback)
},
fail() {
console.error('下载图片错误')
}
})
}
/**
* 检查相机权限
*/
export function 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[]) {
//用户拒绝了部分权限
res(false)
})
}
})
}
let callCustomFun = null as null|CALL_RESULT_FUN
const xMlkitScanningActivityResult = (requestCode : Int, resultCode : Int, data ?: Intent)=>{
let _this = this;
let jgCode = 1;
let datastr = uni.getStorageSync('xMlkitScannigUResult');
if(requestCode==jgCode.toInt()&&datastr!=null&&callCustomFun!=null){
let str = ""
let call = callCustomFun!;
if(typeof datastr == 'bigint'||typeof datastr == 'number' || typeof datastr == 'int'){
call(datastr.toString())
}else{
str = datastr as string;
call(str)
}
uni.removeStorageSync('xMlkitScannigUResult')
}
}
export function openCameraApi(call:CALL_RESULT_FUN,onlyCamera:boolean){
// UTSAndroid.offAppActivityResult(xMlkitScanningActivityResult)
// callCustomFun = call
let isPrivacyAgreeCamrea = uni.getStorageSync('isPrivacyAgreeCamreaByTmui')
if(isPrivacyAgreeCamrea!='on'){
uni.showModal({
title:"提醒",
content:"将授权你的设备相机使用权限进行拍摄识别功能,是否允许?",
success(isPermisseions){
if(isPermisseions.confirm){
checkPermissions()
.then((res:boolean)=>{
gotoDemoActivity(call,onlyCamera)
uni.setStorageSync('isPrivacyAgreeCamreaByTmui','on')
})
.catch(()=>{
uni.setStorageSync('isPrivacyAgreeCamreaByTmui','off')
})
}
},
fail(){
uni.showToast({title:"未授权相机使用权",icon:"none"})
}
})
}else{
// camera.openCamera(call,onlyCamera)
gotoDemoActivity(call,onlyCamera)
}
}
export function closeCamera(){
}
@@ -0,0 +1,468 @@
import Context from 'android.content.Context'
import LinearLayout from 'android.widget.LinearLayout';
import ViewGroup from 'android.view.ViewGroup';
import View from 'android.view.View';
import TextView from 'android.widget.TextView';
import ImageView from 'android.widget.ImageView';
import Gravity from 'android.view.Gravity';
import Color from 'android.graphics.Color';
import Activity from 'android.app.Activity';
// import CardView from 'androidx.cardview.widget.CardView';
import { ref, } from "vue"
import TextUtils from 'android.text.TextUtils';
import GradientDrawable from 'android.graphics.drawable.GradientDrawable'
import MotionEvent from 'android.view.MotionEvent';
import ColorStateList from 'android.content.res.ColorStateList';
import attr from 'android.R.attr'
import {hexToRgb,getDefaultColor} from "./xCoreColorUtil.uts"
/**
* 随机一个uid
* @param rdix 随机因子
* @param length 取的长度
* @param isAddStr 是否限制随机结果中的长度,不允许输出长度
* @returns String
*/
function getUid(rdix = 1, length = 12) : string {
let ix = "";
// #ifndef APP
ix = Math.floor(Math.random() * rdix * Math.floor(Math.random() * Date.now())).toString().substring(0, length);
// #endif
// #ifdef APP
ix = Math.floor(Math.random() * rdix * Math.floor(Math.random() * Date.now())).toString().substring(0, length as Int);
// #endif
return ix;
}
class MyClickListener implements View.OnClickListener {
callbackFun:()=>void;
constructor(cal?:()=>void){
this.callbackFun = cal as ()=>void;
}
override onClick(v?:View):void {
if(typeof this.callbackFun !='underfinde'){
this.callbackFun();
}
}
}
class MyTouchListener implements View.OnTouchListener {
//down,up,move,cancel,longClick
callbackFun:(types:string,event : MotionEvent,detail:UTSJSONObject)=>void;
dubleTime = 0;
tid = 56;
_x = 0;
_y = 0;
// 判断方向的差值
swipe_mindiff = 40
// 滑动时,一定时间内只能触发一次事件。不能连续触发。
tid_siper = 100;
swipeDirection = ""
constructor(call:(types:string,event : MotionEvent,detail?:UTSJSONObject)=>void) {
this.callbackFun = call;
}
override onTouch(view : View, event : MotionEvent) : Boolean {
if(event.action == MotionEvent.ACTION_DOWN ){
this._x = event.getX();
this._y = event.getY();
this.swipeDirection = ""
this.callbackFun('down',event,{});
let difftime = new Date().getTime() - this.dubleTime;
if(difftime >0 && difftime<=300){
this.callbackFun('doubleClick',event ,{});
}
this.dubleTime = new Date().getTime();
clearTimeout(this.tid)
tid = setTimeout(function() {
this.callbackFun('longPress',event,{});
}, 800);
}
if(event.action == MotionEvent.ACTION_UP ){
clearTimeout(this.tid)
this.callbackFun('up',event,{});
if(new Date().getTime() - this.dubleTime > 50){
this.callbackFun('click',event,{});
}
}
if(event.action == MotionEvent.ACTION_CANCEL || event.action== MotionEvent.ACTION_OUTSIDE ){
this.callbackFun('cancel',event,{});
clearTimeout(this.tid)
}
if(event.action == MotionEvent.ACTION_MOVE ){
let x = event.getX();
let y = event.getY();
let deltaX = Math.abs(x - this._x);
let deltaY = Math.abs(y - this._y);
if(deltaX > deltaY && deltaX > this.swipe_mindiff){
this.swipeDirection = (this._x > x) ? "left" : "right";
}else if(deltaY > deltaX && deltaY > this.swipe_mindiff){
this.swipeDirection = (this._y < y) ? "down" : "up";
}
if(this.swipeDirection !=""){
this.callbackFun('swiper',event,{x,y,diffX:deltaX,diffY:deltaY,direction:this.swipeDirection});
}
// this._x = x;
// this._y = y;
this.callbackFun('move',event,{});
clearTimeout(this.tid)
}
return true;
}
}
class xView {
public view : any;
public tagId:string = 'x_view_id_'+getUid();
public bgView:GradientDrawable
private _onclickFun= (_event : MotionEvent):void=>{};
private _onTouchStart= (_event : MotionEvent):void=>{};
private _onTouchEnd= (_event : MotionEvent):void=>{};
private _onTouchCancel= (_event : MotionEvent):void=>{};
private _onTouchMove= (_event : MotionEvent):void=>{};
private _onTouchLongPress= (_event : MotionEvent):void=>{};
private _onTouchDubleClick= (_event : MotionEvent):void=>{};
private _onTouchSwiper= (_event : MotionEvent,detail?:UTSJSONObject):void=>{};
// 设置禁用,不会触发上面的事件。
private _disabled = false;
constructor(context : Context) {
let view = new View(context);
view.setTag(this.tagId)
let layoutParams_crd = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
view.setLayoutParams(layoutParams_crd);
this.bgView = new GradientDrawable();
// let statuesInts= [[attr.state_enabled].toKotlinList().toIntArray(),[attr.state_pressed].toKotlinList().toIntArray(),[-attr.state_enabled].toKotlinList().toIntArray()]
// let statuesColors= [Color.WHITE,Color.RED,Color.RED]
// let states = new ColorStateList(statuesInts.toKotlinList().toTypedArray(),statuesColors.toKotlinList().toIntArray())
this.bgView.setShape(GradientDrawable.RECTANGLE)
this.bgView.setColor(Color.WHITE)
this.bgView.mutate()
view.setBackground(this.bgView)
this.view = view;
// view.setOnClickListener(new MyClickListener(():void=>{
// this._onclickFun()
// }));
view.setOnTouchListener(new MyTouchListener((types:string,event : MotionEvent,detail?:UTSJSONObject):void=>{
if(this._disabled == false){
if(types=='down'){
this._onTouchStart(event)
}
if(types=='up'){
this._onTouchEnd(event);
}
if(types=='click'){
this._onclickFun(event);
}
if(types=='move'){
this._onTouchMove(event)
}
if(types=='cancel'){
this._onTouchCancel(event)
}
if(types=='longPress'){
this._onTouchLongPress(event)
}
if(types=='doubleClick'){
this._onTouchDubleClick(event)
}
if(types=='swiper'){
this._onTouchSwiper(event,detail)
}
}
}));
}
getView():View{
return this.view as View;
}
setDisabled(dis:boolean) : xView{
this._disabled = dis;
return this;
}
/**
* 触发距离是大于50时触发并判断方向
* x,y当前坐标,diffX滑动时距离开始时按下的横向x距离,diffY表示竖向。Direction为方向:left,right,up,dowon
*detail:
*{x,y,diffX,diffY,direction}
*/
setSwiper(fun?:(event: MotionEvent,detail?:UTSJSONObject)=>void) : xView{
if(typeof fun !== 'undefined'){
this._onTouchSwiper = fun;
}
return this;
}
setDoubleClick(fun?:(event: MotionEvent)=>void) : xView{
if(typeof fun !== 'undefined'){
this._onTouchDubleClick = fun;
}
return this;
}
setClick(fun?:(event: MotionEvent)=>void) : xView{
if(typeof fun !== 'undefined'){
this._onclickFun = fun;
}
return this;
}
setTouchStart(fun?:(event: MotionEvent)=>void) : xView{
if(typeof fun !== 'undefined'){
this._onTouchStart = fun;
}
return this;
}
setTouchMove(fun?:(event: MotionEvent)=>void) : xView{
if(typeof fun !== 'undefined'){
this._onTouchMove = fun;
}
return this;
}
setTouchEnd(fun?:(event: MotionEvent)=>void) : xView{
if(typeof fun !== 'undefined'){
this._onTouchEnd = fun;
}
return this;
}
setTouchCancel(fun?:(event: MotionEvent)=>void) : xView{
if(typeof fun !== 'undefined'){
this._onTouchCancel = fun;
}
return this;
}
setTouchLongPress(fun?:(event: MotionEvent)=>void) : xView{
if(typeof fun !== 'undefined'){
this._onTouchLongPress = fun;
}
return this;
}
setBackgroundColor(colorStr : string) : xView {
this.bgView.mutate()
if(colorStr == 'transparent'){
this.bgView.setColor(Color.TRANSPARENT)
}else{
this.bgView.setColor(Color.parseColor(getDefaultColor(colorStr)))
}
return this;
}
setBorder(w?:number,colorStr?:string,dashed?:boolean) : xView {
this.bgView.mutate()
let tw:number = 1;
let tcolor = 'transparent';
let tdashed = false;
if(typeof tw !== 'undefined'){
tw = w as number;
}
if(typeof tw !== 'undefined'){
tcolor = colorStr as string;
}
if(typeof dashed !== 'undefined'){
tdashed = dashed as boolean;
}
let colorNum = Color.TRANSPARENT;
if(tcolor != 'transparent'){
colorNum = Color.parseColor(getDefaultColor(tcolor));
}
if(dashed == true){
let dashedWidth = 8
let dashedGap = 5
this.bgView.setStroke(tw.toInt(),colorNum,dashedWidth.toFloat(),dashedGap.toFloat())
}else{
this.bgView.setStroke(tw.toInt(),colorNum)
}
return this;
}
setMargin(n:any):xView{
let view = this.view as View;
let selfPrams = view.getLayoutParams()
let params = new LinearLayout.LayoutParams(
selfPrams.width,
selfPrams.height
)
if(Array.isArray(n)){
let rd = n as number[];
if(rd.length==0){
let rds = 0;
let rdf = (rds).toInt();
params.setMargins(rdf,rdf,rdf,rdf)
}else if(rd.length==1){
let rdf = (rd[0]).toInt();
params.setMargins(rdf,rdf,rdf,rdf)
}else if(rd.length==2){
let rds = 0;
params.setMargins((rd[0]).toInt(),(rd[1]).toInt(),(rd[0]).toInt(),(rd[1]).toInt())
}else if(rd.length==3){
let rds = 0;
let rdf = rds.toInt();
params.setMargins((rd[0]).toInt(),(rd[1]).toInt(),(rd[2]).toInt(),rdf)
}else if(rd.length==4){
params.setMargins((rd[0]).toInt(),(rd[1]).toInt(),(rd[2]).toInt(),(rd[3]).toInt())
}
}else{
let rd = n as number;
let rdf = (rd).toInt();
params.setMargins(rdf,rdf,rdf,rdf)
}
view.setLayoutParams(params);
return this;
}
setPadding(n:any):xView{
let view = this.view as View;
if(Array.isArray(n)){
let rd = n as number[];
if(rd.length==0){
let rds = 0;
let rdf = (rds).toInt();
view.setPadding(rdf,rdf,rdf,rdf)
}else if(rd.length==1){
let rdf = (rd[0]).toInt();
view.setPadding(rdf,rdf,rdf,rdf)
}else if(rd.length==2){
view.setPadding((rd[0]).toInt(),(rd[1]).toInt(),(rd[0]).toInt(),(rd[1]).toInt())
}else if(rd.length==3){
let rds = 0;
let rdf = rds.toInt();
view.setPadding((rd[0]).toInt(),(rd[1]).toInt(),(rd[2]).toInt(),rdf)
}else if(rd.length==4){
view.setPadding((rd[0]).toInt(),(rd[1]).toInt(),(rd[2]).toInt(),(rd[3]).toInt())
}
}else{
let rd = n as number;
let rdf = (rd).toInt();
view.setPadding(rdf,rdf,rdf,rdf)
}
return this;
}
setRadius(radius:any) : xView{
let x =0
let y =0
this.bgView.mutate()
if(Array.isArray(radius)){
let rd = radius as number[];
if(rd.length==0){
let rdfs = 0;
this.bgView.setCornerRadius((rdfs).toFloat());
// view.setBackground(this.bgView)
}else if(rd.length==1){
let rdfs = rd[0];
this.bgView.setCornerRadius((rdfs).toFloat());
// view.setBackground(this.bgView)
}else if(rd.length==2){
let tl = [
(rd[0]).toFloat(),(rd[0]).toFloat(),
(rd[1]).toFloat(),(rd[1]).toFloat(),
x.toFloat(),x.toFloat(),
x.toFloat(),x.toFloat()
]
let tlint = tl.toKotlinList().toFloatArray()
this.bgView.setCornerRadii(tlint);
}else if(rd.length==3){
let tl = [
(rd[0]).toFloat(),(rd[0]).toFloat(),
(rd[1]).toFloat(),(rd[1]).toFloat(),
(rd[2]).toFloat(),(rd[2]).toFloat(),
x.toFloat(),x.toFloat()
]
let tlint = tl.toKotlinList().toFloatArray()
this.bgView.setCornerRadii(tlint);
}else if(rd.length==4){
let tl = [
(rd[0]).toFloat(),(rd[0]).toFloat(),
(rd[1]).toFloat(),(rd[1]).toFloat(),
(rd[2]).toFloat(),(rd[2]).toFloat(),
(rd[3]).toFloat(),(rd[3]).toFloat(),
]
let tlint = tl.toKotlinList().toFloatArray()
this.bgView.setCornerRadii(tlint);
}
}else{
let rd = radius as number;
this.bgView.setCornerRadius((rd).toFloat());
}
return this;
}
/** 设置当前宽和高为父级的相对宽和高
*w,h有效值为:auto|100%
*/
setSizeBy(w:string="100%",h:string = "auto"){
let view = this.view as View;
let ww = ViewGroup.LayoutParams.MATCH_PARENT;
let hh = ViewGroup.LayoutParams.MATCH_PARENT;
if(w=="auto"){
ww = ViewGroup.LayoutParams.WRAP_CONTENT
}
if(h=="auto"){
hh = ViewGroup.LayoutParams.WRAP_CONTENT
}
let layoutParams_crd = new LinearLayout.LayoutParams(ww,hh)
view.setLayoutParams(layoutParams_crd);
}
setSize(width : number, height : number) : xView {
let view = this.view as View;
let layoutParams_crd = new LinearLayout.LayoutParams(
(width).toInt(),
(height).toInt()
)
view.setLayoutParams(layoutParams_crd);
return this;
}
setWidth(width : number) : xView {
let view = this.view as View;
let lpar = view.getLayoutParams();
let layoutParams_crd = new LinearLayout.LayoutParams(
(width).toInt(),
(lpar.height).toInt(),
)
view.setLayoutParams(layoutParams_crd);
return this;
}
setHeight(height : number) : xView {
let view = this.view as View;
let lpar = view.getLayoutParams();
let layoutParams_crd = new LinearLayout.LayoutParams(
(lpar.width).toInt(),
(height).toInt(),
)
view.setLayoutParams(layoutParams_crd);
return this;
}
}
export default xView;
@@ -0,0 +1,676 @@
/**
* 数字转16进制
*/
function toHex(numbers : number) : string {
let i = 0 as number
if (numbers === i) {
return '00';
}
let hex = '';
const hexChars = '0123456789abcdef';
while (numbers > 0) {
const remainder = numbers % 16;
hex = hexChars[remainder] + hex;
numbers = Math.floor(numbers / 16);
}
return hex;
}
/**
* 颜色值列表
*/
export const colors = new Map<string, string>([
['primary', '#0579FF'],
['success', '#09CC54'],
['danger', '#FE6D1B'],
['warn', '#F0A22C'],
['error', '#E2001B'],
['info', '#F2F2F2'],
['kleinblue', '#002FA7'], // 克莱因蓝
['chinesered', '#FF0000'], // 中国红
['internationalorange', '#FF4F00'], // 国际橙
['egyptianvlue', '#1034A6'], // 埃及艳蓝
['parisviolet', '#6C3082'], // 巴黎紫
['moroccanblue', '#1256A7'], // 摩洛哥蓝
['brazilgreen', '#009B3A'], // 巴西绿
['britishracinggreen', '#004225'], // 英国赛车绿
['indianyellow', '#E3A857'], // 印度黄
['australiangold', '#FFDF00'], // 澳大利亚金
['venetianred', '#C80815'], // 威尼斯红
['majorelleblue', '#6050DC'], // 马若雷蓝
['tuscanred', '#7C3030'], // 托斯卡红
['naplesyellow', '#FADA5E'], // 那不勒斯黄
['capumortuum', '#592720'], // 死者之首
['mayablue', '#73C2FB'], // 玛雅蓝
['persianrose', '#FE28A2'], // 波斯玫瑰
['tyrianpurple', '#66023C'], // 泰尔紫
['saharasand', '#F1E788'], // 撒哈拉沙
['burmeseruby', '#B00A0A'], // 缅甸红宝石
['transparent', 'rgba(0,0,0,0)'],
['aliceblue', '#F0F8FF'],
['antiquewhite', '#FAEBD7'],
['aqua', '#00FFFF'],
['aquamarine', '#7FFFD4'],
['azure', '#F0FFFF'],
['beige', '#F5F5DC'],
['bisque', '#FFE4C4'],
['black', '#000000'],
['blanchedalmond', '#FFEBCD'],
['blue', '#0000FF'],
['blueviolet', '#8A2BE2'],
['brown', '#A52A2A'],
['burlywood', '#DEB887'],
['cadetblue', '#5F9EA0'],
['chartreuse', '#7FFF00'],
['chocolate', '#D2691E'],
['coral', '#FF7F50'],
['cornflowerblue', '#6495ED'],
['cornsilk', '#FFF8DC'],
['crimson', '#DC143C'],
['cyan', '#00FFFF'],
['darkblue', '#00008B'],
['darkcyan', '#008B8B'],
['darkgoldenrod', '#B8860B'],
['darkgray', '#A9A9A9'],
['darkgreen', '#006400'],
['darkkhaki', '#BDB76B'],
['darkmagenta', '#8B008B'],
['darkolivegreen', '#556B2F'],
['darkorange', '#FF8C00'],
['darkorchid', '#9932CC'],
['darkred', '#8B0000'],
['darksalmon', '#E9967A'],
['darkseagreen', '#8FBC8F'],
['darkslateblue', '#483D8B'],
['darkslategray', '#2F4F4F'],
['darkturquoise', '#00CED1'],
['darkviolet', '#9400D3'],
['deeppink', '#FF1493'],
['deepskyblue', '#00BFFF'],
['dimgray', '#696969'],
['dodgerblue', '#1E90FF'],
['firebrick', '#B22222'],
['floralwhite', '#FFFAF0'],
['forestgreen', '#228B22'],
['fuchsia', '#FF00FF'],
['gainsboro', '#DCDCDC'],
['ghostwhite', '#F8F8FF'],
['gold', '#FFD700'],
['goldenrod', '#DAA520'],
['gray', '#808080'],
['green', '#008000'],
['greenyellow', '#ADFF2F'],
['honeydew', '#F0FFF0'],
['hotpink', '#FF69B4'],
['indianred', '#CD5C5C'],
['indigo', '#4B0082'],
['ivory', '#FFFFF0'],
['khaki', '#F0E68C'],
['lavender', '#E6E6FA'],
['lavenderblush', '#FFF0F5'],
['lawngreen', '#7CFC00'],
['lemonchiffon', '#FFFACD'],
['lightblue', '#ADD8E6'],
['lightcoral', '#F08080'],
['lightcyan', '#E0FFFF'],
['lightgoldenrodyellow', '#FAFAD2'],
['lightgray', '#D3D3D3'],
['lightgreen', '#90EE90'],
['lightpink', '#FFB6C1'],
['lightsalmon', '#FFA07A'],
['lightseagreen', '#20B2AA'],
['lightskyblue', '#87CEFA'],
['lightslategray', '#778899'],
['lightsteelblue', '#B0C4DE'],
['lightyellow', '#FFFFE0'],
['lime', '#00FF00'],
['limegreen', '#32CD32'],
['linen', '#FAF0E6'],
['magenta', '#FF00FF'],
['maroon', '#800000'],
['mediumaquamarine', '#66CDAA'],
['mediumblue', '#0000CD'],
['mediumorchid', '#BA55D3'],
['mediumpurple', '#9370DB'],
['mediumseagreen', '#3CB371'],
['mediumslateblue', '#7B68EE'],
['mediumspringgreen', '#00FA9A'],
['mediumturquoise', '#48D1CC'],
['mediumvioletred', '#C71585'],
['midnightblue', '#191970'],
['mintcream', '#F5FFFA'],
['mistyrose', '#FFE4E1'],
['moccasin', '#FFE4B5'],
['navajowhite', '#FFDEAD'],
['navy', '#000080'],
['oldlace', '#FDF5E6'],
['olive', '#808000'],
['olivedrab', '#6B8E23'],
['orange', '#FFA500'],
['orangered', '#FF4500'],
['orchid', '#DA70D6'],
['palegoldenrod', '#EEE8AA'],
['palegreen', '#98FB98'],
['paleturquoise', '#AFEEEE'],
['palevioletred', '#DB7093'],
['papayawhip', '#FFEFD5'],
['peachpuff', '#FFDAB9'],
['peru', '#CD853F'],
['pink', '#FFC0CB'],
['plum', '#DDA0DD'],
['powderblue', '#B0E0E6'],
['purple', '#800080'],
['rebeccapurple', '#663399'],
['red', '#FF0000'],
['rosybrown', '#BC8F8F'],
['royalblue', '#4169E1'],
['saddlebrown', '#8B4513'],
['salmon', '#FA8072'],
['sandybrown', '#F4A460'],
['seagreen', '#2E8B57'],
['seashell', '#FFF5EE'],
['sienna', '#A0522D'],
['silver', '#C0C0C0'],
['skyblue', '#87CEEB'],
['slateblue', '#6A5ACD'],
['slategray', '#708090'],
['snow', '#FFFAFA'],
['springgreen', '#00FF7F'],
['steelblue', '#4682B4'],
['tan', '#D2B48C'],
['teal', '#008080'],
['thistle', '#D8BFD8'],
['tomato', '#FF6347'],
['turquoise', '#40E0D0'],
['violet', '#EE82EE'],
['wheat', '#F5DEB3'],
['white', '#FFFFFF'],
['whitesmoke', '#F5F5F5'],
['yellow', '#FFFF00'],
['yellowgreen', '#9ACD32']
])
export function isValidColor(color : string) : boolean {
// return chroma.valid(color);
const hexRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}|[A-Fa-f0-9]{8})$/;
const rgbRegex = /^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/;
const rgbaRegex = /^rgba\((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\s*,\s*(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\s*,\s*(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\s*,\s*((1(\.0{1,2})?)|(0(\.\d{1,2})?))\)$/;
const hslRegex = /^hsl\((\d{1,3}), (\d{1,3})%, (\d{1,3})%\)$/;
const hslaRegex = /^hsla\(\s*((\d{1,2}|[1-2]\d{2}|3[0-5]\d)(\.\d+)?|\d+(\.\d+)?)\s*,\s*((\d{1,2}|\d{0,1}\d{1}\d{1}|[1-2]\d{2}|3[0-5]\d)(\.\d+)?|\d+(\.\d+)?)%\s*,\s*((\d{1,2}|\d{0,1}\d{1}\d{1}|[1-2]\d{2}|3[0-5]\d)(\.\d+)?|\d+(\.\d+)?)%\s*,\s*((1|0(\.\d{1,2})?|(\.\d{1,2})))\)$/i;
if (color == '') {
return false; // 颜色值为空
} else if (color === 'inherit' || color === 'transparent') {
return false; // 特殊颜色值
} else if (color === 'currentColor') {
return false; // currentColor 不是有效的颜色值
} else if (hexRegex.test(color) || rgbRegex.test(color) || rgbaRegex.test(color) || hslRegex.test(color) || hslaRegex.test(color)) {
return true; // 符合颜色值的格式
}
return false;
}
/**
* 获取默认的颜色值
* @param [string] 颜色名称或者16进制颜色值
*/
export function getDefaultColor(sColor : string) : string {
if(sColor=="") return ""
let sc = sColor.toLocaleLowerCase().trim().replace(" ","");
if (isValidColor(sc)) {
// 如果符合所有颜色值,进行转换为16进制。css方便统一解析。
return sc;
}
// 检测是否是颜色名称。
let sco = colors.get(sc);
if (typeof sco == 'string') return sco as string;
return colors.get("primary")!;
}
/**
* 16进制转rgb
*/
export function hexToRgb(sColors : string) : RGBA {
if (sColors == "") {
return {
r: 0,
g: 0,
b: 0,
a: 1
} as RGBA
}
let reg = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}|[A-Fa-f0-9]{8})$/;
let sColor : string = sColors.toLowerCase();
if (sColor != '' && reg.test(sColor)) {
if (sColor.length == 4) {
let sColorNew = "#";
for (let i = 1; i < 4; i += 1) {
sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1));
}
sColor = sColorNew;
}
//处理六位的颜色值
let sColorChange : number[] = [];
sColorChange.push(parseInt(sColor.substring(1, 3), 16));
sColorChange.push(parseInt(sColor.substring(3, 5), 16));
sColorChange.push(parseInt(sColor.substring(5, 7), 16));
if (sColor.length == 9) {
sColorChange.push(parseInt(sColor.substring(7, 9), 16) / 255);
}
return {
r: sColorChange[0],
g: sColorChange[1],
b: sColorChange[2],
a: sColorChange.length == 4 ? sColorChange[3] : 1
} as RGBA
} else if (/^(rgb|RGB|rgba|RGBA)/.test(sColor)) {
let arr : string[] = sColor.replace(/(?:\(|\)|rgba|rgb|RGB|RGBA)*/g, "").split(",")
let p : number[] = arr.map((val : string) : number => parseInt(val));
if (p.length < 3) {
return {
r: 0,
g: 0,
b: 0,
a: 1
} as RGBA
}
if (p.length == 3) {
p.add(1)
}
return {
r: p[0],
g: p[1],
b: p[2],
a: arr.length==3?p[3]:parseFloat(arr[3])
} as RGBA
}
return {
r: 0,
g: 0,
b: 0,
a: 1
} as RGBA;
}
/**
* rgb转hsl
*/
export function rgbToHsl(rgb : UTSJSONObject) : UTSJSONObject {
let r = rgb.getNumber("r")
r = r as number / 255
let g = rgb.getNumber("g")
g = g as number / 255
let b = rgb.getNumber("b")
b = b as number / 255
let a = rgb.getNumber("a")
var max = Math.max(r, g, b);
var min = Math.min(r, g, b);
let h=60 * (4 + (r - g) / (max - min));
let s=(max - min) / (2 - max - min);
let l= (max + min) / 2;
if (max === r) {
h = (60 * (g - b)) / (max - min);
} else if (max === g) {
h = 60 * (2 + (b - r) / (max - min));
}
if (h < 0) {
h += 360;
}
if (max === min) {
s = 0;
} else if (l < 0.5) {
s = (max - min) / (max + min);
}
return { h: h, s: s * 100, l: l * 100, a: a };
}
/**
* hsl转rgb
*/
export function hslToRgb(hsl : UTSJSONObject) : UTSJSONObject {
let h = hsl.getNumber("h")!
let s = hsl.getNumber("s")!
let l = hsl.getNumber("l")!
let a = hsl.getNumber("a")!
h = h / 360;
s = s / 100;
l = l / 100;
let r = l;
let g = l;
let b = l;
function hue2rgb(pxx : number, q : number, txs : number) : number {
let t = txs;
let p = pxx;
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
}
if (s > 0) {
let q = l * (1 + s);
if (l >= 0.5) {
q = l + s - l * s;
}
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
r = Math.round(r * 255);
g = Math.round(g * 255);
b = Math.round(b * 255);
return { r, g, b, a };
}
/**
* rgb转16进制
*/
export function rgbToHex(rgb : UTSJSONObject) : string {
let r = rgb.getNumber("r")!
let g = rgb.getNumber("g")!
let b = rgb.getNumber("b")!
let a = rgb.getNumber("a")!
return "#" + toHex(r) + toHex(g) + toHex(b) + toHex(a*255)
}
export function hslaToCss(hsl : UTSJSONObject) : string {
let rgb = hslToRgb(hsl)
return rgbToHex(rgb)
}
export function hslaToRgbCss(hsl : UTSJSONObject) : string {
let rgb = hslToRgb(hsl)
let r = rgb.getNumber("r")!
let g = rgb.getNumber("g")!
let b = rgb.getNumber("b")!
let a = rgb.getNumber("a")!
return `rgba(${r},${g},${b},${a})`
}
// 根据 HSLA 颜色计算亮度
export function getLuminance(color : UTSJSONObject, type = "rgba") : number {
let colordefault : UTSJSONObject = type == 'rgba' ? color : hslToRgb(color);
const r= colordefault.getNumber("r")!
const g= colordefault.getNumber("g")!
const b= colordefault.getNumber("b")!
const rs = r / 255
const gs = g / 255
const bs = b / 255
const rl = Math.pow(rs <= 0.03928 ? rs / 12.92 : ((rs + 0.055) / 1.055), 2.4);
const gl = Math.pow(gs <= 0.03928 ? gs / 12.92 : ((gs + 0.055) / 1.055), 2.4);
const bl = Math.pow(bs <= 0.03928 ? bs / 12.92 : ((bs + 0.055) / 1.055), 2.4);
return 0.2126 * rl + 0.7152 * gl + 0.0722 * bl;
}
/**
* 对颜色加深
*/
export function colorAddDeepen(sColor : string) : string {
let rgb = hexToRgb(getDefaultColor(sColor));
let hsl = rgbToHsl(rgb);
let l = hsl.getNumber("l")! - 5;
l = Math.max(0, Math.min(l, 100))
return hslaToRgbCss({ h: hsl.getNumber("h"), s: hsl.getNumber("s"), l, a: hsl.getNumber("a")! })
}
/**
* 对颜色提亮
*/
export function colorAddBrighten(sColor : string) : string {
let rgb = hexToRgb(getDefaultColor(sColor));
let hsl = rgbToHsl(rgb);
let l = hsl.getNumber("l")! + 10;
l = Math.max(0, Math.min(l, 100))
return rgbToHex(hslToRgb({ h: hsl.getNumber("h"), s:hsl.getNumber("s"), l, a: hsl.getNumber("a")! }))
}
/**
* 获取浅色系主题,根据颜色值计算。返回正常值和hover值。
*/
export function colorGetThint(sColor : string) {
let rgb = hexToRgb(getDefaultColor(sColor));
let hsl = rgbToHsl(rgb);
let s = hsl.getNumber("s")!;
// let l = hsl.getNumber("l");
if (Math.ceil(s) > 0) {
s = 45
}
return {
default: rgbToHex(hslToRgb({ h: hsl.getNumber("h"), s, l: 93, a: hsl.getNumber("a")! })),
hover: rgbToHex(hslToRgb({ h: hsl.getNumber("h"), s, l: 85, a: hsl.getNumber("a")! }))
}
}
/**
* 获取正常主题,根据颜色值计算。返回正常值和hover值。
*/
export function colorGetHover(sColor : string) {
return {
default: sColor,
hover: colorAddDeepen(sColor)
}
}
/**
* 处理相主题色。
* @param color 需要处理的颜色值
* @param hoverColor 对应按下去的颜色默认与color相等。
* @returns UTSJSONObject
*/
export function getDefaultColorObj(color : string, hoverColor : string) : UTSJSONObject {
let hsla : UTSJSONObject = rgbToHsl(hexToRgb(getDefaultColor(color)));
let hoverHsla : UTSJSONObject = rgbToHsl(hexToRgb(getDefaultColor(hoverColor)));
let shadow = hslaToRgbCss({ h: hsla.getNumber("h")!, s: (hsla.getNumber("s")!)== 0 ? 0 : 60, l: 40, a: 0.4 });
let shadowHOver = hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: (hoverHsla.getNumber("s")!)== 0 ? 0 : 60, l: 40, a: 0.4 });
if((hsla.getNumber("h")!)==0&&(hsla.getNumber("s")!)==0){
shadow = "transparent"
}
if((hoverHsla.getNumber("h")!)==0&&(hoverHsla.getNumber("s")!)==0){
shadowHOver = "transparent"
}
let o = {
default: {
background: hslaToRgbCss(hsla),
borderColor: hslaToRgbCss({ h: hsla.getNumber("h")!, s: hsla.getNumber("s")!, a: hsla.getNumber("a")!, l: Math.min(Math.max(0, hsla.getNumber("l")! - 5), hsla.getNumber("l")!) }),
fontColor: getLuminance(hsla, 'hsla') < 0.46 && hsla.getNumber("a")! > 0.1 ? '#ffffff' : '#000000',
shadow: shadow
},
active: {
background: hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: hoverHsla.getNumber("a")!, l: Math.min(Math.max(0, hoverHsla.getNumber("l")! - 10), hoverHsla.getNumber("l")!) }),
borderColor: hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: hoverHsla.getNumber("a")!, l: Math.max(Math.max(0, hoverHsla.getNumber("l")! - 10), hoverHsla.getNumber("l")!) }),
fontColor: getLuminance(hoverHsla, 'hsla') < 0.46 && hoverHsla.getNumber("a")! > 0.1 ? '#ffffff' : '#000000',
shadow: shadowHOver
}
} as UTSJSONObject
return o;
}
/**
* 处理相主题色。
* @param color 需要处理的颜色值
* @param hoverColor 对应按下去的颜色默认与color相等。
* @returns UTSJSONObject
*/
export function getTextColorObj(color : string, hoverColor : string) : UTSJSONObject {
let hsla : UTSJSONObject = rgbToHsl(hexToRgb(getDefaultColor(color)));
let hoverHsla : UTSJSONObject = rgbToHsl(hexToRgb(getDefaultColor(hoverColor)));
// tmxColor.hslaToCss({ ...hsla}) : tmxColor.hslaToCss({ ...hsla,l:30})
let o = {
default: {
background: "transparent",
borderColor: "transparent",
fontColor: getLuminance(hsla, 'hsla') < 0.3 && hsla.getNumber("a")! > 0.1 ? hslaToRgbCss(hsla) : hslaToRgbCss({ h: hsla.getNumber("h")!, s: hsla.getNumber("s")!, a: hsla.getNumber("a")!, l: 30 }),
shadow: "transparent",
},
active: {
background: hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: hoverHsla.getNumber("a")!, l: 95 }),
borderColor:"transparent",
fontColor: getLuminance(hoverHsla, 'hsla') < 0.3 && hoverHsla.getNumber("a")! > 0.1 ? hslaToRgbCss(hoverHsla) : hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: hoverHsla.getNumber("a")!, l: 30 }),
shadow: "transparent",
}
} as UTSJSONObject
return o;
}
/**
* 处理相主题色。
* @param color 需要处理的颜色值
* @param hoverColor 对应按下去的颜色默认与color相等。
* @returns UTSJSONObject
*/
export function getThinColorObj(color : string, hoverColor : string,isCoverDark?:boolean) : UTSJSONObject {
let dark = isCoverDark==null?false:isCoverDark
// #ifdef WEB
dark = isCoverDark==undefined?false:dark
// #endif
let hsla : UTSJSONObject = rgbToHsl(hexToRgb(getDefaultColor(color)));
let hoverHsla : UTSJSONObject = rgbToHsl(hexToRgb(getDefaultColor(hoverColor)));
// tmxColor.hslaToCss({ ...hsla}) : tmxColor.hslaToCss({ ...hsla,l:30})
let fontcolor = getLuminance(hsla, 'hsla') < 0.3 && hsla.getNumber("a")! > 0.1 ? hslaToRgbCss(hsla) : hslaToRgbCss({ h: hsla.getNumber("h")!, s: hsla.getNumber("s")!, a: hsla.getNumber("a")!, l:30 })
let bordercolor = hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: hoverHsla.getNumber("a")!, l: dark==true?21:92 })
let bgcolor = hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: hoverHsla.getNumber("a")!, l: dark==true?20:95 })
if(dark){
let p = hsla;
p.set('l',90)
fontcolor = hslaToRgbCss(p)
p.set('l',20)
p.set('s',p.getNumber('s')!==0?0:20)
bgcolor = hslaToRgbCss(p)
p.set('l',22)
bordercolor = hslaToRgbCss(p)
}
let o = {
default: {
background: bgcolor,
borderColor:bordercolor,
fontColor: fontcolor,
shadow: "transparent",
},
active: {
background: hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: hoverHsla.getNumber("a")!, l: dark==true?18:88 }),
borderColor: hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a:0, l: dark==true?18:80 }),
fontColor: getLuminance(hoverHsla, 'hsla') < 0.3 && hoverHsla.getNumber("a")! > 0.1 ? hslaToRgbCss(hoverHsla) : hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: hoverHsla.getNumber("a")!, l: 30 }),
shadow: "transparent",
}
} as UTSJSONObject
return o;
}
/**
* 处理相主题色。
* @param color 需要处理的颜色值
* @param hoverColor 对应按下去的颜色默认与color相等。
* @returns UTSJSONObject
*/
export function getOutlineColorObj(color : string, hoverColor : string) : UTSJSONObject {
let hsla : UTSJSONObject = rgbToHsl(hexToRgb(getDefaultColor(color)));
let hoverHsla : UTSJSONObject = rgbToHsl(hexToRgb(getDefaultColor(hoverColor)));
// tmxColor.hslaToCss({ ...hsla}) : tmxColor.hslaToCss({ ...hsla,l:30})
let o = {
default: {
background: 'transparent',
borderColor:hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: 1, l: 75 }),
fontColor: getLuminance(hsla, 'hsla') < 0.3 && hsla.getNumber("a")! > 0.1 ? hslaToRgbCss(hsla) : hslaToRgbCss({ h: hsla.getNumber("h")!, s: hsla.getNumber("s")!, a: hsla.getNumber("a")!, l: 30 }),
shadow: "transparent",
},
active: {
background: 'transparent',
borderColor: hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a:1, l: 70 }),
fontColor: getLuminance(hoverHsla, 'hsla') < 0.3 && hoverHsla.getNumber("a")! > 0.1 ? hslaToRgbCss(hoverHsla) : hslaToRgbCss({ h: hoverHsla.getNumber("h")!, s: hoverHsla.getNumber("s")!, a: hoverHsla.getNumber("a")!, l: 30 }),
shadow: "transparent",
}
} as UTSJSONObject
return o;
}
/**
* 设置颜色的亮度值。29
* isCoverDark:是根据亮度值,是否要设置n的值,如果n超过了
* 指定的30就不操作。
*/
export function setTextColorLightByDark(color:string):string{
let realColor = getDefaultColor(color);
let hsla = rgbToHsl(hexToRgb(realColor))
let l = hsla.getNumber('l')!
let s = hsla.getNumber('s')!
if(l<30){
if(s>0){
hsla.set('l',50)
}else{
hsla.set('l',100)
}
}
return hslaToRgbCss(hsla)
}
/**
* 变动的颜色是否需要变深。如果不需要返回原始颜色
* 如果果需要自动加深。
*/
export function setBgColorLightByDark(color:string):string{
let realColor = getDefaultColor(color);
let hsla = rgbToHsl(hexToRgb(realColor))
let l = hsla.getNumber('l')!
let s = hsla.getNumber('s')!
if(s==0&&l>50){
hsla.set('l',100-l)
}
if(s>0){
if(l>50){
hsla.set('l',100-l)
}
}
return hslaToRgbCss(hsla)
}
export function isBlackAndWhite(color:string):boolean{
let realColor = getDefaultColor(color);
let hsla = rgbToHsl(hexToRgb(realColor))
let s = hsla.getNumber('s')!
return s==0
}
@@ -0,0 +1,3 @@
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromYDelta="100%" android:toYDelta="0%"
android:duration="300"/>
@@ -0,0 +1,3 @@
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromYDelta="0%" android:toYDelta="100%"
android:duration="300"/>
@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical"
android:layout_width="match_parent" android:background="#000000" android:layout_height="match_parent">
<LinearLayout android:layout_width="match_parent" android:id="@+id/surface_camera_box" android:layout_height="match_parent" android:layout_gravity="center_vertical">
<SurfaceView android:id="@+id/surface_camera" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_gravity="center_vertical">
</SurfaceView>
</LinearLayout>
<RelativeLayout android:orientation="vertical" android:layout_marginBottom="100dp" android:id="@+id/scan_box" android:layout_width="match_parent"
android:layout_height="match_parent" >
</RelativeLayout>
<RelativeLayout android:orientation="vertical"
android:layout_gravity="center_vertical"
android:id="@+id/camre_center_preview"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</RelativeLayout>
<RelativeLayout android:orientation="vertical" android:id="@+id/relative_layout" android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent"
android:layout_gravity="top|bottom" android:orientation="vertical">
<LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"
android:gravity="top" android:orientation="horizontal">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="100dp" android:paddingLeft="32dp"
android:paddingTop="88px" android:orientation="vertical" android:gravity="center_horizontal|left"
android:id="@+id/header_top">
<!-- 这里的内容将会水平居中并底部对齐 -->
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
android:textColor="#ffffff" android:textSize="16dp" android:id="@+id/header_back"
android:text="返回" />
</LinearLayout>
</LinearLayout>
<LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"
android:gravity="bottom" android:orientation="horizontal">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="100dp"
android:orientation="horizontal" android:background="#000000" android:gravity="center_horizontal|top"
android:id="@+id/header_bottom">
<TextView android:paddingTop="30px" android:id="@+id/footer_asml" android:height="50dp" android:layout_width="0dp" android:textAlignment="center" android:layout_weight="1" android:layout_height="wrap_content"
android:textColor="#fff" android:text="相册选取" android:textSize="16dp" />
<!-- 这里的内容将会水平居中并底部对齐 -->
<TextView android:paddingTop="30px" android:height="50dp" android:id="@+id/footer_camre" android:textAlignment="center" android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content"
android:textColor="#fff" android:text="" android:textSize="16dp" />
<TextView android:paddingTop="30px" android:id="@+id/footer_scanning" android:height="50dp" android:textAlignment="center" android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content"
android:textColor="#fff" android:text="扫一扫" android:textSize="16dp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</RelativeLayout>
</FrameLayout>