package com.test.myidentify;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.media.Image;
import android.media.ImageReader;
import android.os.Handler;
import android.os.HandlerThread;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.ScriptIntrinsicYuvToRGB;
import android.renderscript.Type;
import android.util.Log;
import android.view.Surface;
import android.view.TextureView;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import io.dcloud.feature.uniapp.UniSDKInstance;
import io.dcloud.feature.uniapp.ui.action.AbsComponentData;
import io.dcloud.feature.uniapp.ui.component.AbsVContainer;
import io.dcloud.feature.uniapp.ui.component.UniComponent;
public class identifyModule extends UniComponent<FrameLayout> {
private TextureView textureView;
//相机相关
private CameraManager cameraManager;
private CameraDevice cameraDevice;
private CameraCaptureSession cameraCaptureSession;
private HandlerThread backgroundThread;
private Handler backgroundHandler;
private ImageReader imageReader;
private Bitmap mCameraImage;
public identifyModule(UniSDKInstance uniSDKInstance, AbsVContainer absVContainer, AbsComponentData absComponentData) {
super(uniSDKInstance, absVContainer, absComponentData);
}
@Override
protected FrameLayout initComponentHostView(@NonNull Context context) {
textureView = new TextureView(context);
FrameLayout frameLayout = new FrameLayout(context);
frameLayout.setBackgroundColor(Color.BLACK);
frameLayout.addView(textureView);
boolean res = checkPermissions(); //检查权限
if(res) {
initializeCamera();
}
return frameLayout;
}
private static int PERMISSION_REQUEST_CODE = 1000;
//加入要使用的权限
protected static final String[] permissions = {
Manifest.permission.CAMERA, //相机摄像头权限
Manifest.permission.READ_PHONE_STATE, //读取电话状态
Manifest.permission.WRITE_EXTERNAL_STORAGE, //读写设备外部存储的文件,例如读取照片、音乐、视频等文件
Manifest.permission.READ_EXTERNAL_STORAGE, //读取外部存储中的内容
};
//检查权限
private boolean checkPermissions() {
Context context = getContext();// 获取上下文
boolean check = true;
if (context instanceof Activity) {
Activity activity = (Activity) context;
// 相机权限尚未被授予
List<String> needRequestPermissionList = new ArrayList<String>();
for (String perm : permissions) {
if (ContextCompat.checkSelfPermission(activity, perm) != PackageManager.PERMISSION_GRANTED) {
needRequestPermissionList.add(perm);
check = false;
} else {
if (ActivityCompat.shouldShowRequestPermissionRationale(activity, perm)) {
needRequestPermissionList.add(perm);
}
}
}
if (needRequestPermissionList.size() > 0) {
ActivityCompat.requestPermissions(
activity,
needRequestPermissionList.toArray(new String[needRequestPermissionList.size()]),
PERMISSION_REQUEST_CODE);
}
}
return check;
}
private static final int REQUEST_CAMERA_PERMISSION = 1000;
// 处理相机权限请求的回调
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode == REQUEST_CAMERA_PERMISSION) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
initializeCamera();
}
}
}
// 初始化CameraManager和打开相机
public void initializeCamera() {
cameraManager = (CameraManager) getContext().getSystemService(Context.CAMERA_SERVICE);
try {
// 选择相机ID,这里假设使用第一个后置摄像头
String cameraId = cameraManager.getCameraIdList()[0];
if (ActivityCompat.checkSelfPermission(MyApplication.context, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
return;
}
cameraManager.openCamera(cameraId, stateCallback, backgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
cameraDevice = camera;
createCameraCaptureSession();
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
camera.close();
cameraDevice = null;
}
@Override
public void onError(@NonNull CameraDevice camera, int i) {
camera.close();
cameraDevice = null;
}
};
//创建相机捕获会话
private void createCameraCaptureSession() {
try {
SurfaceTexture surfaceTexture = textureView.getSurfaceTexture();// 获取TextureView的SurfaceTexture
surfaceTexture.setDefaultBufferSize(640, 480);// 设置SurfaceTexture的一些属性
imageReader = ImageReader.newInstance(640, 480, ImageFormat.YUV_420_888, 2);
// 处理捕获的图像数据的回调
imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
Image image = null;
try {
image = reader.acquireLatestImage();
if (image != null) {
mCameraImage = yuv420ToBitmap(image);
}
} catch (Exception e) {
Log.e("====", "Error converting YUV to Bitmap", e);
} finally {
if (image != null) {
image.close();
}
}
}
}, backgroundHandler);
// 将ImageReader的Surface设置给预览请求构建器
final CaptureRequest.Builder captureRequest = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
captureRequest.addTarget(new Surface(surfaceTexture));
captureRequest.addTarget(imageReader.getSurface());
cameraDevice.createCaptureSession(Arrays.asList(new Surface(surfaceTexture), imageReader.getSurface()),
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
try {
cameraCaptureSession = session;
captureRequest.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
cameraCaptureSession.setRepeatingRequest(captureRequest.build(), null, backgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
// 处理配置失败的情况
}
},
backgroundHandler
);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
//将YUV格式的Image转换为Bitmap
private Bitmap yuv420ToBitmap(Image image) {
RenderScript rs = RenderScript.create(MyApplication.context);
ScriptIntrinsicYuvToRGB script = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs));
byte[] yuvByteArray = image2byteArray(image);
//创建了两个Allocation对象:一个用于输入YUV数据,另一个用于输出RGB数据
Type.Builder yuvType = new Type.Builder(rs, Element.U8(rs)).setX(yuvByteArray.length);
Allocation in = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT);
Type.Builder rgbaType = new Type.Builder(rs, Element.RGBA_8888(rs))
.setX(image.getWidth())
.setY(image.getHeight());
Allocation out = Allocation.createTyped(rs, rgbaType.create(), Allocation.USAGE_SCRIPT);
//将YUV数据复制到输入Allocation,RenderScript可以访问
in.copyFrom(yuvByteArray);
script.setInput(in);
script.forEach(out);
Bitmap bitmap = Bitmap.createBitmap(image.getWidth(), image.getHeight(), Bitmap.Config.ARGB_8888);
out.copyTo(bitmap);
return bitmap;
}
//Image对象转换为一个包含YUV_420_888数据的字节数组
public static byte[] image2byteArray(Image image) {
Image.Plane[] planes = image.getPlanes();// 获取Image的平面
ByteBuffer yBuffer = planes[0].getBuffer();
ByteBuffer uBuffer = planes[1].getBuffer();
ByteBuffer vBuffer = planes[2].getBuffer();
// 获取每个平面的数据长度
int ySize = yBuffer.remaining();
int uSize = uBuffer.remaining();
int vSize = vBuffer.remaining();
int yuvSize = ySize + uSize + vSize;// 计算YUV_420_888格式的总字节大小
byte[] yuvBytes = new byte[yuvSize];// 创建一个足够大的字节数组来存储YUV_420_888数据
yBuffer.get(yuvBytes, 0, ySize);// 将Y平面的数据复制到字节数组中
int uvIndex = ySize;// 计算U和V平面在YUV_420_888格式中的起始位置
uBuffer.get(yuvBytes, uvIndex, uSize);// 将U平面的数据复制到字节数组中
int vIndex = uvIndex + uSize;// 计算V平面在YUV_420_888格式中的起始位置
vBuffer.get(yuvBytes, vIndex, vSize);// 将V平面的数据复制到字节数组中
return yuvBytes;// 返回包含YUV_420_888数据的字节数组
}
}