You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2587 lines
112 KiB
2587 lines
112 KiB
package com.ouxuan.oxface;
|
|
|
|
import android.app.ActivityManager;
|
|
import android.content.BroadcastReceiver;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.IntentFilter;
|
|
import android.content.pm.PackageManager;
|
|
import android.graphics.Bitmap;
|
|
import android.graphics.Canvas;
|
|
import android.graphics.Color;
|
|
import android.graphics.Paint;
|
|
import android.graphics.PorterDuff;
|
|
import android.graphics.RectF;
|
|
import android.hardware.Camera;
|
|
import android.os.Handler;
|
|
import android.os.Looper;
|
|
import android.os.Bundle;
|
|
import android.os.Handler;
|
|
import android.util.Log;
|
|
import android.view.TextureView;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.widget.Button;
|
|
import android.widget.ImageView;
|
|
import android.widget.RelativeLayout;
|
|
import android.widget.TextView;
|
|
import android.widget.Toast;
|
|
|
|
import androidx.core.app.ActivityCompat;
|
|
import androidx.core.content.ContextCompat;
|
|
|
|
import com.baidu.idl.face.main.finance.activity.BaseActivity;
|
|
import com.baidu.idl.face.main.finance.callback.CameraDataCallback;
|
|
import com.baidu.idl.face.main.finance.callback.FaceDetectCallBack;
|
|
import com.baidu.idl.face.main.finance.camera.AutoTexturePreviewView;
|
|
import com.baidu.idl.face.main.finance.camera.CameraPreviewManager;
|
|
import com.baidu.idl.face.main.finance.manager.FaceSDKManager;
|
|
import com.baidu.idl.face.main.finance.model.LivenessModel;
|
|
import com.baidu.idl.face.main.finance.model.SingleBaseConfig;
|
|
import com.baidu.idl.face.main.finance.utils.BitmapUtils;
|
|
import com.baidu.idl.face.main.finance.utils.FaceOnDrawTexturViewUtil;
|
|
import com.baidu.idl.face.main.finance.utils.TestPopWindow;
|
|
import com.baidu.idl.main.facesdk.FaceInfo;
|
|
import com.baidu.idl.main.facesdk.model.BDFaceImageInstance;
|
|
import com.ouxuan.oxface.data.DeviceSelectDataManager;
|
|
import com.ouxuan.oxface.orderOX.OrderVerificationResultActivity;
|
|
import com.ouxuan.oxface.orderOX.OrderSelectionActivity;
|
|
import com.ouxuan.oxface.orderOX.VerificationCodeActivity;
|
|
import com.ouxuan.oxface.utils.LogManager;
|
|
import com.ouxuan.oxface.network.OrderVerificationManager;
|
|
import com.ouxuan.oxface.network.OrderVerificationResultHandler;
|
|
import com.ouxuan.oxface.network.LeaveVerificationManager;
|
|
import com.ouxuan.oxface.utils.VenueSceneUtils;
|
|
import com.ouxuan.oxface.network.NetworkStatusIndicator;
|
|
import com.ouxuan.oxface.abgate.GateUnavailableDialog;
|
|
import com.ouxuan.oxface.abgate.ABGateManager;
|
|
import com.ouxuan.oxface.device.GateABController;
|
|
|
|
import java.util.List;
|
|
|
|
/**
|
|
* 简化版人脸识别界面 - 只显示视频流
|
|
*/
|
|
public class OXFaceOnlineActivity extends BaseActivity implements View.OnClickListener {
|
|
|
|
private static final float MIN_FACE_AREA = 1500; //人脸靠近阈值: 人脸面积在屏幕中的像素乘积
|
|
private static final String TAG = "OXFaceOnlineActivity";
|
|
private static final int CAMERA_PERMISSION_REQUEST_CODE = 1001;
|
|
|
|
// 图片越大,性能消耗越大,也可以选择640*480, 1280*720
|
|
private static final int PREFER_WIDTH = SingleBaseConfig.getBaseConfig().getRgbAndNirWidth();
|
|
private static final int PERFER_HEIGH = SingleBaseConfig.getBaseConfig().getRgbAndNirHeight();
|
|
|
|
// 新增控制变量
|
|
private static int PROCESS_FRAME_INTERVAL = 10; // 降低处理频率,每隔10帧处理一次
|
|
private int frameCounter = 0;
|
|
private boolean needSendFaceImage = false; // 是否需要将人脸图片转为base64发送
|
|
private long lastProcessTime = 0;
|
|
private long lastBackgroundProcessTime = 0; // 应用在后台时的最后处理时间
|
|
private static long MIN_PROCESS_INTERVAL = 1000; // 增加最小处理间隔至1秒
|
|
|
|
// 智能调节处理频率的控制变量
|
|
private static final int FAST_PROCESS_FRAME_INTERVAL = 3; // 快速处理频率:每3帧处理一次
|
|
private static final long FAST_MIN_PROCESS_INTERVAL = 300; // 快速处理间隔:300ms
|
|
private static final int SLOW_PROCESS_FRAME_INTERVAL = 30; // 慢速处理频率:每30帧处理一次
|
|
private static final long SLOW_MIN_PROCESS_INTERVAL = 3000; // 慢速处理间隔:3000ms
|
|
private static final long FACE_DETECTION_TIMEOUT = 5000; // 人脸检测超时时间:5秒
|
|
|
|
private long lastFaceDetectedTime = 0; // 上次检测到人脸的时间
|
|
private boolean isFaceDetected = false; // 当前是否检测到人脸
|
|
private int consecutiveNoFaceCount = 0; // 连续未检测到人脸的次数
|
|
private static final int CONSECUTIVE_NO_FACE_THRESHOLD = 20; // 连续未检测到人脸的阈值
|
|
|
|
private Context mContext;
|
|
private AutoTexturePreviewView mAutoCameraPreviewView;
|
|
private TextureView mDrawDetectFaceView;
|
|
private RelativeLayout relativeLayout;
|
|
private boolean isNeedCamera = true;
|
|
private RectF rectF;
|
|
private Paint paint;
|
|
private Paint paintBg;
|
|
private boolean liveStatus;
|
|
private LivenessModel currentLivenessModel;
|
|
|
|
// 新增的视图元素
|
|
private TextView preText;
|
|
private ImageView previewView;
|
|
private RelativeLayout preViewRelativeLayout;
|
|
|
|
private TextView deveLop;
|
|
private RelativeLayout deveLopRelativeLayout;
|
|
private ImageView developView;
|
|
private TextView preToastText;
|
|
private TextView detectSurfaceText;
|
|
private ImageView isCheckImage;
|
|
private ImageView mFaceDetectImageView;
|
|
private TextView mTvDetect;
|
|
private TextView mTvLive;
|
|
private TextView mTvLiveScore;
|
|
private TextView mTvAllTime;
|
|
private RelativeLayout progressLayout;
|
|
private RelativeLayout layoutCompareStatus;
|
|
private TextView textCompareStatus;
|
|
private ImageView progressBarView;
|
|
private RelativeLayout payHintRl;
|
|
private boolean mIsPayHint = true;
|
|
private boolean count = true;
|
|
private RelativeLayout financeQualityTestFailed;
|
|
private TextView qualityTestTimeTv;
|
|
private TextView qualityDetectedTv;
|
|
private TextView qualityShelteredPart;
|
|
private Button qualityRetestDetectBtn;
|
|
private RelativeLayout financeByLivingDetection;
|
|
private RelativeLayout financeFailedInVivoTest;
|
|
private TextView failedInVivoTestRgb;
|
|
private TextView failedInVivoTestNir;
|
|
private TextView failedInVivoTestDepth;
|
|
private TextView failedInVivoTestTime;
|
|
private TextView failedInVivoTestFrames;
|
|
private TextView byLivingDetectionRgb;
|
|
private TextView byLivingDetectionNir;
|
|
private TextView byLivingDetectionDepth;
|
|
private TextView byLivingTetectionTime;
|
|
private TextView byLivingDetectionFrames;
|
|
private ImageView qualityDetectRegImageItem;
|
|
private ImageView noDetectRegImageItem;
|
|
private ImageView detectRegImageItem;
|
|
private TestPopWindow testPopWindow;
|
|
private View saveCamera;
|
|
private boolean isSaveImage;
|
|
private View spot;
|
|
private boolean isCheck = false;
|
|
private boolean isTime = true;
|
|
private long searshTime;
|
|
private boolean isCompareCheck = false;
|
|
|
|
// 底部按钮
|
|
private Button btnOpenDoor;
|
|
private Button btnMemoryInfo; // 新增的内存信息按钮
|
|
// private Button btnScanQR;
|
|
// private Button btnSettings;
|
|
|
|
// 新增的二维码区域控件
|
|
private ImageView imgMiniProgramCode;
|
|
private ImageView imgScanDoorQRCode;
|
|
private Button btnVerificationCode;
|
|
private Button btnScannerDoor;
|
|
|
|
// 新增店铺名称相关变量
|
|
private TextView tvStoreName;
|
|
private int storeNameClickCount = 0;
|
|
private long lastStoreNameClickTime = 0;
|
|
private static final int MAX_CLICK_COUNT = 5;
|
|
private static final long CLICK_INTERVAL = 1000; // 1秒内点击有效
|
|
|
|
// 新增订单查验相关变量
|
|
private int modeType = 0; // 1验证码验证 2人脸验证 3扫码验证 4扫码器验证
|
|
private String verifyCode = ""; // 验证码
|
|
private com.ouxuan.oxface.data.LoginDataManager loginDataManager; // 新增LoginDataManager实例
|
|
private CameraControlReceiver cameraControlReceiver; // 新增摄像头控制广播接收器实例
|
|
|
|
// 网络请求管理器
|
|
private OrderVerificationManager orderVerificationManager;
|
|
private OrderVerificationResultHandler orderVerificationResultHandler;
|
|
private LeaveVerificationManager leaveVerificationManager;
|
|
|
|
// 摄像头暂停管理相关
|
|
private Handler cameraTimeoutHandler;
|
|
private static final int CAMERA_RESUME_TIMEOUT = 60000; // 30秒超时
|
|
private boolean isCameraPausedByDialog = false; // 标记是否由对话框暂停
|
|
private int pauseRequestCount = 0; // 暂停请求计数
|
|
|
|
// 网络状态指示器
|
|
private NetworkStatusIndicator networkStatusIndicator;
|
|
|
|
// AB门禁不可用弹窗
|
|
private GateUnavailableDialog gateUnavailableDialog;
|
|
private GateABController gateABController;
|
|
private ABGateManager abGateManager;
|
|
private boolean isGateCheckEnabled = false; // AB门检测是否开启
|
|
|
|
@Override
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
setContentView(R.layout.activity_oxface_online);
|
|
|
|
android.util.Log.d(TAG, "onCreate方法开始执行");
|
|
LogManager.logInfo(TAG, "OXFaceOnlineActivity onCreate开始");
|
|
|
|
mContext = this;
|
|
// 初始化LoginDataManager
|
|
loginDataManager = com.ouxuan.oxface.data.LoginDataManager.getInstance(this);
|
|
|
|
// 初始化网络请求管理器
|
|
initNetworkManagers();
|
|
|
|
// 初始化离场校验管理器
|
|
initLeaveVerificationManager();
|
|
|
|
initView();
|
|
|
|
// 初始化网络状态指示器
|
|
initNetworkStatusIndicator();
|
|
|
|
// 初始化AB门禁管理和不可用弹窗
|
|
initGateUnavailableDialog();
|
|
|
|
// 初始化人脸检测状态
|
|
lastFaceDetectedTime = System.currentTimeMillis();
|
|
isFaceDetected = false;
|
|
consecutiveNoFaceCount = 0;
|
|
|
|
// 检查并请求相机权限
|
|
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA)
|
|
!= PackageManager.PERMISSION_GRANTED) {
|
|
ActivityCompat.requestPermissions(this,
|
|
new String[]{android.Manifest.permission.CAMERA},
|
|
CAMERA_PERMISSION_REQUEST_CODE);
|
|
} else {
|
|
LogManager.logInfo(TAG, "相机权限已授予");
|
|
}
|
|
|
|
// 启动内存优化周期性任务
|
|
startMemoryOptimizationTask();
|
|
|
|
// 更新小程序码
|
|
android.util.Log.d(TAG, "准备调用updateMiniQrcode方法");
|
|
updateMiniQrcode();
|
|
android.util.Log.d(TAG, "updateMiniQrcode方法调用完成");
|
|
|
|
LogManager.logInfo(TAG, "OXFaceOnlineActivity onCreate");
|
|
android.util.Log.d(TAG, "onCreate方法执行结束");
|
|
}
|
|
|
|
/**
|
|
* 初始化离场校验管理器
|
|
*/
|
|
private void initLeaveVerificationManager() {
|
|
// 初始化离场校验管理器
|
|
leaveVerificationManager = new LeaveVerificationManager(this, new LeaveVerificationManager.LeaveVerificationListener() {
|
|
@Override
|
|
public void onLeaveVerificationStart() {
|
|
showLoadingStatus("正在进行离场校验...");
|
|
}
|
|
|
|
@Override
|
|
public void onLeaveVerificationSuccess(com.ouxuan.oxface.network.api.PadApiService.CheckLeaveResult result) {
|
|
runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (layoutCompareStatus != null) {
|
|
layoutCompareStatus.setVisibility(View.VISIBLE);
|
|
textCompareStatus.setTextColor(Color.parseColor("#009874"));
|
|
textCompareStatus.setText(result != null && result.getMessage() != null ?
|
|
result.getMessage() : "离场成功");
|
|
|
|
// 3秒后隐藏状态提示
|
|
new Handler().postDelayed(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (layoutCompareStatus != null) {
|
|
layoutCompareStatus.setVisibility(View.GONE);
|
|
}
|
|
}
|
|
}, 3000);
|
|
}
|
|
|
|
// 离场校验成功后开启B门
|
|
if (gateABController != null) {
|
|
LogManager.logInfo(TAG, "离场校验成功,开启B门");
|
|
gateABController.handleFaceRecognitionSuccess(false); // 参数保留兼容性,实际都开B门
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void onLeaveVerificationError(int errorCode, String errorMessage) {
|
|
runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (layoutCompareStatus != null) {
|
|
layoutCompareStatus.setVisibility(View.VISIBLE);
|
|
textCompareStatus.setTextColor(Color.parseColor("#fec133"));
|
|
textCompareStatus.setText(errorMessage != null ? errorMessage : "离场失败");
|
|
|
|
// 3秒后隐藏状态提示
|
|
new Handler().postDelayed(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (layoutCompareStatus != null) {
|
|
layoutCompareStatus.setVisibility(View.GONE);
|
|
}
|
|
}
|
|
}, 3000);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void onLeaveVerificationComplete() {
|
|
// 离场校验完成后恢复摄像头
|
|
resumeCamera();
|
|
}
|
|
|
|
@Override
|
|
public void showToast(String message) {
|
|
OXFaceOnlineActivity.this.showToast(message);
|
|
}
|
|
});
|
|
|
|
LogManager.logInfo(TAG, "离场校验管理器初始化完成");
|
|
}
|
|
|
|
/**
|
|
* 初始化网络请求管理器
|
|
*/
|
|
private void initNetworkManagers() {
|
|
// 初始化订单验证管理器
|
|
orderVerificationManager = new OrderVerificationManager(this, new OrderVerificationManager.OrderVerificationListener() {
|
|
@Override
|
|
public void showLoadingStatus(String message) {
|
|
OXFaceOnlineActivity.this.showLoadingStatus(message);
|
|
}
|
|
|
|
@Override
|
|
public void hideLoadingStatus() {
|
|
OXFaceOnlineActivity.this.hideLoadingStatus();
|
|
}
|
|
|
|
@Override
|
|
public void showToast(String message) {
|
|
OXFaceOnlineActivity.this.showToast(message);
|
|
}
|
|
|
|
@Override
|
|
public void onVerificationSuccess(com.ouxuan.oxface.network.api.PadApiService.CheckOrderResult data, int verificationType) {
|
|
// 订单核销成功后开启B门
|
|
if (gateABController != null) {
|
|
LogManager.logInfo(TAG, "订单核销成功,开启B门");
|
|
gateABController.handleFaceRecognitionSuccess(true); // 参数保留兼容性,实际都开B门
|
|
}
|
|
|
|
// 然后处理原有的页面跳转逻辑
|
|
orderVerificationResultHandler.handleVerificationSuccess(data, verificationType);
|
|
}
|
|
|
|
@Override
|
|
public void onVerificationSuccessWithFullResponse(com.ouxuan.oxface.network.api.PadApiService.CheckOrderResult data, int verificationType, String fullResponseJson) {
|
|
// 当前不使用这个方法,保持兼容性
|
|
orderVerificationResultHandler.handleVerificationSuccess(data, verificationType);
|
|
}
|
|
|
|
@Override
|
|
public void onVerificationError(int errorCode, String errorMessage, int verificationType) {
|
|
orderVerificationResultHandler.handleVerificationError(errorCode, errorMessage, verificationType);
|
|
}
|
|
|
|
@Override
|
|
public void onVerificationException(Throwable throwable, int verificationType) {
|
|
orderVerificationResultHandler.handleVerificationException(throwable, verificationType);
|
|
}
|
|
|
|
@Override
|
|
public void onVerificationComplete() {
|
|
// 验证完成后恢复摄像头(无论成功或失败)
|
|
// 但只在非人脸验证模式下恢复,人脸验证模式下不需要恢复
|
|
// 因为人脸验证模式下没有暂停摄像头
|
|
LogManager.logInfo(TAG, "验证完成,检查是否需要恢复摄像头");
|
|
}
|
|
});
|
|
|
|
// 初始化订单验证结果处理器
|
|
orderVerificationResultHandler = new OrderVerificationResultHandler(this, new OrderVerificationResultHandler.OrderVerificationResultListener() {
|
|
@Override
|
|
public void showToast(String message) {
|
|
OXFaceOnlineActivity.this.showToast(message);
|
|
}
|
|
|
|
@Override
|
|
public void navigateToResultPage(Intent intent) {
|
|
startActivity(intent);
|
|
}
|
|
|
|
@Override
|
|
public void navigateToOrderSelectionPage(String orderData, int verificationType, String faceBase64) {
|
|
// 在跳转到订单选择页面时暂停摄像头
|
|
pauseCameraWithTimeout();
|
|
LogManager.logInfo(TAG, "跳转订单选择页面时暂停摄像头");
|
|
|
|
// 跳转到订单选择页面
|
|
Intent intent = new Intent(OXFaceOnlineActivity.this, OrderSelectionActivity.class);
|
|
intent.putExtra(OrderSelectionActivity.EXTRA_ORDER_DATA, orderData);
|
|
intent.putExtra(OrderSelectionActivity.EXTRA_VERIFICATION_TYPE, verificationType);
|
|
if (faceBase64 != null) {
|
|
intent.putExtra(OrderSelectionActivity.EXTRA_FACE_BASE64, faceBase64);
|
|
}
|
|
startActivityForResult(intent, 1002); // 使用新的requestCode
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* View
|
|
*/
|
|
private void initView() {
|
|
LogManager.logInfo(TAG, "初始化界面视图");
|
|
android.util.Log.d(TAG, "初始化界面视图开始");
|
|
// 获取整个布局
|
|
relativeLayout = findViewById(R.id.all_relative);
|
|
|
|
// 单目摄像头RGB 图像预览
|
|
mAutoCameraPreviewView = findViewById(R.id.auto_camera_preview_view);
|
|
// 画人脸框
|
|
rectF = new RectF();
|
|
paint = new Paint();
|
|
paintBg = new Paint();
|
|
mDrawDetectFaceView = findViewById(R.id.draw_detect_face_view);
|
|
mDrawDetectFaceView.setOpaque(false);
|
|
mDrawDetectFaceView.setKeepScreenOn(true);
|
|
|
|
LogManager.logInfo(TAG, "镜像设置getRgbRevert"+SingleBaseConfig.getBaseConfig().getRgbRevert());
|
|
|
|
//TODO 还需要根据实际情况继续调整摄像头镜像参数
|
|
android.util.Log.d( "OXFaceOnlineActivity", ": 666666" + SingleBaseConfig.getBaseConfig().getRgbRevert());
|
|
if (SingleBaseConfig.getBaseConfig().getRgbRevert()){
|
|
mDrawDetectFaceView.setRotationY(180);
|
|
}else{
|
|
// mDrawDetectFaceView.setRotationY(0);
|
|
}
|
|
|
|
// 店铺名称显示
|
|
tvStoreName = findViewById(R.id.tv_store_name);
|
|
if (tvStoreName != null) {
|
|
// 设置点击事件监听器
|
|
tvStoreName.setOnClickListener(new View.OnClickListener() {
|
|
@Override
|
|
public void onClick(View v) {
|
|
handleStoreNameClick();
|
|
}
|
|
});
|
|
|
|
// 获取并显示店铺名称
|
|
loadAndDisplayStoreName();
|
|
}
|
|
|
|
// 返回按钮(隐藏但仍保留引用)
|
|
ImageView mButReturn = findViewById(R.id.btn_back);
|
|
if (mButReturn != null) {
|
|
mButReturn.setOnClickListener(this);
|
|
}
|
|
|
|
// 初始化二维码区域控件
|
|
imgMiniProgramCode = findViewById(R.id.img_mini_program_code);
|
|
imgScanDoorQRCode = findViewById(R.id.img_scan_door_qrcode);
|
|
btnVerificationCode = findViewById(R.id.btn_verification_code);
|
|
btnScannerDoor = findViewById(R.id.btn_scanner_door);
|
|
|
|
// 添加调试日志,检查控件是否正确初始化
|
|
android.util.Log.d(TAG, "imgMiniProgramCode初始化: " + (imgMiniProgramCode != null));
|
|
android.util.Log.d(TAG, "imgScanDoorQRCode初始化: " + (imgScanDoorQRCode != null));
|
|
android.util.Log.d(TAG, "btnVerificationCode初始化: " + (btnVerificationCode != null));
|
|
android.util.Log.d(TAG, "btnScannerDoor初始化: " + (btnScannerDoor != null));
|
|
|
|
// 设置二维码区域控件的点击事件
|
|
if (imgMiniProgramCode != null) {
|
|
imgMiniProgramCode.setOnClickListener(this);
|
|
}
|
|
if (imgScanDoorQRCode != null) {
|
|
imgScanDoorQRCode.setOnClickListener(this);
|
|
}
|
|
if (btnVerificationCode != null) {
|
|
btnVerificationCode.setOnClickListener(this);
|
|
}
|
|
if (btnScannerDoor != null) {
|
|
btnScannerDoor.setOnClickListener(this);
|
|
}
|
|
|
|
// 底部覆盖层,包含按钮和二维码
|
|
RelativeLayout bottomOverlay = findViewById(R.id.bottom_overlay);
|
|
if (bottomOverlay != null) {
|
|
// 确保底部覆盖层显示
|
|
bottomOverlay.setVisibility(View.VISIBLE);
|
|
}
|
|
|
|
// 初始化底部按钮
|
|
btnOpenDoor = findViewById(R.id.button_open_door);
|
|
btnMemoryInfo = findViewById(R.id.button_memory_info); // 初始化内存信息按钮
|
|
// btnScanQR = findViewById(R.id.button_scan_qr);
|
|
// btnSettings = findViewById(R.id.button_settings);
|
|
|
|
if (btnOpenDoor != null) {
|
|
btnOpenDoor.setOnClickListener(this);
|
|
}
|
|
if (btnMemoryInfo != null) { // 设置内存信息按钮点击事件
|
|
btnMemoryInfo.setOnClickListener(this);
|
|
}
|
|
// if (btnScanQR != null) {
|
|
// btnScanQR.setOnClickListener(this);
|
|
// }
|
|
// if (btnSettings != null) {
|
|
// btnSettings.setOnClickListener(this);
|
|
// }
|
|
|
|
// 隐藏原有的UI元素
|
|
hideOriginalUI();
|
|
|
|
// 初始化状态信息显示
|
|
layoutCompareStatus = findViewById(R.id.layout_compare_status);
|
|
if (layoutCompareStatus != null) {
|
|
layoutCompareStatus.setVisibility(View.GONE);
|
|
}
|
|
textCompareStatus = findViewById(R.id.text_compare_status);
|
|
|
|
// 初始化测试弹窗
|
|
initTestPopWindow();
|
|
|
|
android.util.Log.d(TAG, "初始化界面视图结束");
|
|
}
|
|
|
|
/**
|
|
* 隐藏原有的UI元素,保持界面简洁
|
|
*/
|
|
private void hideOriginalUI() {
|
|
// 隐藏进度条
|
|
if (progressLayout != null) {
|
|
progressLayout.setVisibility(View.GONE);
|
|
}
|
|
|
|
progressBarView = findViewById(R.id.progress_bar_view);
|
|
if (progressBarView != null) {
|
|
progressBarView.setVisibility(View.GONE);
|
|
}
|
|
|
|
// 隐藏提示区域
|
|
if (preViewRelativeLayout != null) {
|
|
preViewRelativeLayout.setVisibility(View.GONE);
|
|
}
|
|
|
|
// 隐藏其他不需要的UI元素
|
|
if (financeQualityTestFailed != null) {
|
|
financeQualityTestFailed.setVisibility(View.GONE);
|
|
}
|
|
if (financeByLivingDetection != null) {
|
|
financeByLivingDetection.setVisibility(View.GONE);
|
|
}
|
|
if (financeFailedInVivoTest != null) {
|
|
financeFailedInVivoTest.setVisibility(View.GONE);
|
|
}
|
|
if (saveCamera != null) {
|
|
saveCamera.setVisibility(View.GONE);
|
|
}
|
|
if (spot != null) {
|
|
spot.setVisibility(View.GONE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 初始化测试弹窗
|
|
*/
|
|
private void initTestPopWindow() {
|
|
testPopWindow = new TestPopWindow(this,
|
|
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
|
|
testPopWindow.setmOnClickFinance(new TestPopWindow.OnClickFinance() {
|
|
@Override
|
|
public void rester(boolean isReTest) {
|
|
// 重新检测
|
|
if (isReTest) {
|
|
if (testPopWindow != null) {
|
|
testPopWindow.closePopupWindow();
|
|
}
|
|
if (payHintRl != null) {
|
|
payHintRl.setVisibility(View.GONE);
|
|
}
|
|
count = true;
|
|
new Handler().postDelayed(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (count) {
|
|
isNeedCamera = true;
|
|
}
|
|
}
|
|
}, 500); // 延迟0.5秒执行
|
|
} else {
|
|
if (testPopWindow != null) {
|
|
testPopWindow.closePopupWindow();
|
|
}
|
|
finish();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 初始化网络状态指示器
|
|
*/
|
|
private void initNetworkStatusIndicator() {
|
|
try {
|
|
networkStatusIndicator = new NetworkStatusIndicator(this, relativeLayout);
|
|
|
|
// 设置网络恢复监听器
|
|
networkStatusIndicator.setNetworkRecoveryListener(new NetworkStatusIndicator.NetworkRecoveryListener() {
|
|
@Override
|
|
public void onNetworkRecovered() {
|
|
LogManager.logInfo(TAG, "接收到网络恢复通知,恢复摄像头预览");
|
|
// 网络恢复时恢复摄像头预览
|
|
resumeCamera();
|
|
}
|
|
|
|
@Override
|
|
public void onNetworkLost() {
|
|
LogManager.logWarning(TAG, "接收到网络断开通知");
|
|
// 网络断开时可以执行一些操作(可选)
|
|
// 例如:清理缓存、停止不必要的网络请求等
|
|
}
|
|
});
|
|
|
|
networkStatusIndicator.startMonitoring();
|
|
LogManager.logInfo(TAG, "网络状态指示器初始化完成");
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "网络状态指示器初始化失败", e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 初始化AB门禁管理和不可用弹窗
|
|
*/
|
|
private void initGateUnavailableDialog() {
|
|
try {
|
|
LogManager.logInfo(TAG, "开始初始化AB门禁管理和不可用弹窗");
|
|
|
|
// 初始化AB门禁管理器
|
|
abGateManager = ABGateManager.getInstance();
|
|
abGateManager.initialize(this);
|
|
|
|
// 初始化门状态控制器
|
|
gateABController = GateABController.getInstance();
|
|
gateABController.initialize(this);
|
|
|
|
// 设置门状态监听器(触发时机1)
|
|
gateABController.setGateStateListener(new GateABController.GateUnavailableListener() {
|
|
@Override
|
|
public void onGateStateChanged(GateABController.GateABState state) {
|
|
LogManager.logInfo(TAG, "门状态发生变化: " + state.toString());
|
|
}
|
|
|
|
@Override
|
|
public void onGateUnavailable(String reason) {
|
|
LogManager.logWarning(TAG, "触发门禁不可用弹窗(触发时机1): " + reason);
|
|
|
|
// 检查是否应该显示弹窗
|
|
boolean shouldShow = gateUnavailableDialog.checkShow(OXFaceOnlineActivity.this, isNetworkAvailable());
|
|
if (shouldShow) {
|
|
// 获取当前门状态信息
|
|
GateABController.GateABState currentState = gateABController.getCurrentGateState();
|
|
// 如果已有弹窗存在,更新内容(带门状态信息);如果没有,显示新弹窗
|
|
gateUnavailableDialog.updateGateStateError(reason,
|
|
currentState.gateAOpen,
|
|
currentState.gateBOpen,
|
|
currentState.udpConnected);
|
|
LogManager.logInfo(TAG, "已更新门禁不可用弹窗内容: " + reason);
|
|
} else {
|
|
LogManager.logInfo(TAG, "根据业务规则,不显示门禁不可用弹窗");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onGateAvailable() {
|
|
LogManager.logInfo(TAG, "门禁恢复可用,隐藏弹窗并恢复摄像头预览");
|
|
|
|
// 隐藏门禁不可用弹窗
|
|
if (gateUnavailableDialog != null && gateUnavailableDialog.isShowing()) {
|
|
gateUnavailableDialog.hide();
|
|
}
|
|
|
|
// 恢复摄像头预览(与弹窗的onDialogHide回调保持一致)
|
|
resumeCamera();
|
|
}
|
|
|
|
@Override
|
|
public void onGateStatusUpdate(boolean gateAOpen, boolean gateBOpen, boolean udpConnected) {
|
|
// 实时更新弹窗中的门状态显示
|
|
if (gateUnavailableDialog != null && gateUnavailableDialog.isShowing()) {
|
|
LogManager.logInfo(TAG, "更新弹窗中的门状态显示: A门-" +
|
|
(gateAOpen ? "开启" : "关闭") + ", B门-" +
|
|
(gateBOpen ? "开启" : "关闭") + ", UDP-" +
|
|
(udpConnected ? "正常" : "异常"));
|
|
|
|
// 更新弹窗中的门状态显示(保持原有消息不变)
|
|
String currentReason = getCurrentDialogReason();
|
|
gateUnavailableDialog.updateGateStateError(currentReason, gateAOpen, gateBOpen, udpConnected);
|
|
}
|
|
|
|
// 特别处理:如果A门和B门都关闭且UDP正常,且有弹窗正在显示,应该关闭弹窗
|
|
if (!gateAOpen && !gateBOpen && udpConnected &&
|
|
gateUnavailableDialog != null && gateUnavailableDialog.isShowing()) {
|
|
LogManager.logInfo(TAG, "A门和B门都关闭且UDP正常,关闭门禁不可用弹窗");
|
|
gateUnavailableDialog.hide();
|
|
resumeCamera();
|
|
}
|
|
}
|
|
});
|
|
|
|
// 初始化门禁不可用弹窗
|
|
gateUnavailableDialog = new GateUnavailableDialog(this);
|
|
|
|
// 设置弹窗操作监听器
|
|
gateUnavailableDialog.setDialogListener(new GateUnavailableDialog.GateUnavailableDialogListener() {
|
|
@Override
|
|
public void onDialogShow() {
|
|
LogManager.logInfo(TAG, "门禁不可用弹窗显示,暂停摄像头和中断操作");
|
|
// 暂停摄像头和中断其他操作
|
|
pauseCameraWithTimeout();
|
|
}
|
|
|
|
@Override
|
|
public void onDialogHide() {
|
|
LogManager.logInfo(TAG, "门禁不可用弹窗隐藏,恢复摄像头和操作");
|
|
// 恢复摄像头和操作
|
|
resumeCamera();
|
|
}
|
|
|
|
@Override
|
|
public void onDialogClosed() {
|
|
LogManager.logInfo(TAG, "门禁不可用弹窗关闭,恢复正常门禁监控");
|
|
// 人数异常弹窗关闭后,恢复正常的门禁监控状态
|
|
try {
|
|
// 检查UDP是否正常初始化,如果没有则重新初始化
|
|
if (gateABController != null && !gateABController.isUDPInitialized()) {
|
|
LogManager.logInfo(TAG, "UDP未初始化,重新初始化UDP门禁控制");
|
|
gateABController.reinitializeUDP();
|
|
// 稍等片刻后启动轮询,确保初始化完成
|
|
new Handler(Looper.getMainLooper()).postDelayed(() -> {
|
|
if (gateABController != null) {
|
|
gateABController.startUDPPolling();
|
|
LogManager.logInfo(TAG, "UDP门禁状态轮询已重新启动");
|
|
}
|
|
}, 500);
|
|
}
|
|
|
|
if (gateABController != null) {
|
|
LogManager.logInfo(TAG, "门禁监控已恢复,当前门状态监听器正常运行");
|
|
}
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "恢复UDP门禁监控失败", e);
|
|
}
|
|
}
|
|
});
|
|
|
|
// 检查是否开启AB门检测
|
|
checkGateCheckEnabled();
|
|
|
|
LogManager.logInfo(TAG, "AB门禁管理和不可用弹窗初始化完成");
|
|
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "AB门禁管理和不可用弹窗初始化失败", e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 显示新的门禁不可用弹窗
|
|
* @param reason 不可用原因
|
|
*/
|
|
private void showNewGateUnavailableDialog(String reason) {
|
|
try {
|
|
// 执行checkShow()判断是否显示弹窗
|
|
boolean shouldShow = gateUnavailableDialog.checkShow(OXFaceOnlineActivity.this, isNetworkAvailable());
|
|
if (shouldShow) {
|
|
// 如果已有弹窗存在,更新内容;如果没有,显示新弹窗
|
|
gateUnavailableDialog.updateGateStateError(reason);
|
|
LogManager.logInfo(TAG, "已更新门禁不可用弹窗内容: " + reason);
|
|
} else {
|
|
LogManager.logInfo(TAG, "根据业务规则,不显示门禁不可用弹窗");
|
|
}
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "显示门禁不可用弹窗失败", e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取当前弹窗的原因消息(用于状态更新时保持原有消息)
|
|
* @return 当前弹窗的原因消息
|
|
*/
|
|
private String getCurrentDialogReason() {
|
|
if (gateABController != null) {
|
|
GateABController.GateABState currentState = gateABController.getCurrentGateState();
|
|
return currentState.getUnavailableReason();
|
|
}
|
|
return "门禁状态异常"; // 默认消息
|
|
}
|
|
|
|
/**
|
|
* 显示新的人数异常弹窗
|
|
* @param isLeaveScene 是否为离场场景
|
|
* @param peopleNum 门内人数
|
|
*/
|
|
private void showNewPeopleCountDialog(boolean isLeaveScene, int peopleNum) {
|
|
try {
|
|
// 执行checkShow()判断是否显示弹窗
|
|
boolean shouldShow = gateUnavailableDialog.checkShow(OXFaceOnlineActivity.this, isNetworkAvailable());
|
|
if (shouldShow) {
|
|
// 如果已有弹窗存在,更新内容;如果没有,显示新弹窗
|
|
gateUnavailableDialog.updatePeopleCountError(isLeaveScene, peopleNum);
|
|
LogManager.logWarning(TAG, "触发门禁不可用弹窗(触发时机2): 人数异常, 场景=" + (isLeaveScene ? "离场" : "进场") + ", 人数=" + peopleNum);
|
|
} else {
|
|
LogManager.logInfo(TAG, "根据业务规则,不显示门禁不可用弹窗");
|
|
}
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "显示人数异常弹窗失败", e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 检查是否开启AB门检测
|
|
*/
|
|
private void checkGateCheckEnabled() {
|
|
try {
|
|
// 从配置中获取AB门检测是否开启
|
|
// 这里可以根据实际业务需要添加配置检查逻辑
|
|
// 例如:从 DeviceSelectDataManager 获取相关配置
|
|
|
|
// 暂时默认开启,后续可以根据实际需要调整
|
|
isGateCheckEnabled = true;
|
|
|
|
LogManager.logInfo(TAG, "AB门检测状态: " + (isGateCheckEnabled ? "已开启" : "已关闭"));
|
|
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "检查AB门检测状态失败", e);
|
|
isGateCheckEnabled = false; // 异常时默认关闭
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onResume() {
|
|
super.onResume();
|
|
|
|
// 启动网络状态监控
|
|
if (networkStatusIndicator != null) {
|
|
networkStatusIndicator.startMonitoring();
|
|
}
|
|
|
|
// 启动UDP门禁状态轮询
|
|
if (gateABController != null && gateABController.isUDPInitialized()) {
|
|
gateABController.startUDPPolling();
|
|
LogManager.logInfo(TAG, "UDP门禁状态轮询已启动");
|
|
}
|
|
|
|
// 只有在拥有相机权限时才启动预览
|
|
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA)
|
|
== PackageManager.PERMISSION_GRANTED) {
|
|
startTestOpenDebugRegisterFunction();
|
|
}else{
|
|
LogManager.logInfo(TAG, "OXFaceOnlineActivity onResume has not CAMERA permission");
|
|
}
|
|
LogManager.logInfo(TAG, "OXFaceOnlineActivity onResume");
|
|
}
|
|
|
|
private void startTestOpenDebugRegisterFunction() {
|
|
LogManager.logInfo(TAG, "启动摄像头预览");
|
|
try {
|
|
// 设置摄像头方向
|
|
if (SingleBaseConfig.getBaseConfig().getRBGCameraId() != -1){
|
|
CameraPreviewManager.getInstance().setCameraFacing(SingleBaseConfig.getBaseConfig().getRBGCameraId());
|
|
} else {
|
|
CameraPreviewManager.getInstance().setCameraFacing(CameraPreviewManager.CAMERA_FACING_FRONT);
|
|
}
|
|
|
|
CameraPreviewManager.getInstance().startPreview(mContext, mAutoCameraPreviewView,
|
|
PREFER_WIDTH, PERFER_HEIGH, new CameraDataCallback() {
|
|
@Override
|
|
public void onGetCameraData(byte[] data, Camera camera, int width, int height) {
|
|
try {
|
|
// 摄像头预览数据进行人脸检测
|
|
if (isNeedCamera) {
|
|
// 增加帧计数器和时间间隔控制
|
|
frameCounter++;
|
|
long currentTime = System.currentTimeMillis();
|
|
|
|
// 智能调节处理频率
|
|
adjustProcessFrequency(currentTime);
|
|
|
|
// 只有当满足处理条件时才进行人脸检测
|
|
if (frameCounter % PROCESS_FRAME_INTERVAL == 0 &&
|
|
(currentTime - lastProcessTime) > MIN_PROCESS_INTERVAL) {
|
|
lastProcessTime = currentTime;
|
|
|
|
// 检查应用是否处于前台
|
|
if (!isApplicationInForeground()) {
|
|
// 如果应用在后台,减少处理频率,每隔10秒处理一次
|
|
if ((currentTime - lastBackgroundProcessTime) < 10000) {
|
|
return;
|
|
}
|
|
lastBackgroundProcessTime = currentTime;
|
|
}
|
|
|
|
FaceSDKManager.getInstance().onDetectCheck(data, null, null,
|
|
height, width, 1, new FaceDetectCallBack() {
|
|
@Override
|
|
public void onFaceDetectCallback(LivenessModel livenessModel) {
|
|
try {
|
|
// 更新人脸检测状态
|
|
updateFaceDetectionStatus(livenessModel);
|
|
|
|
// 开发模式结果输出
|
|
checkOpenDebugResult(livenessModel);
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "人脸检测回调处理异常", e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onTip(int code, String msg) {
|
|
try {
|
|
if(!msg.equals("未检测到人脸")){
|
|
LogManager.logInfo(TAG, "人脸检测提示onTip - 代码: " + code + ", 消息: " + msg);
|
|
}
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "人脸检测提示处理异常", e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onFaceDetectDarwCallback(LivenessModel livenessModel) {
|
|
try {
|
|
// 绘制人脸框
|
|
showFrame(livenessModel);
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "人脸框绘制回调处理异常", e);
|
|
}
|
|
}
|
|
});
|
|
} else if (frameCounter % 30 == 0) {
|
|
// 对于跳过处理的帧,每30帧清空一次画布,确保无人脸时画面干净
|
|
try {
|
|
LivenessModel emptyModel = new LivenessModel();
|
|
emptyModel.setTrackFaceInfo(null);
|
|
showFrame(emptyModel);
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "清空画布时发生异常", e);
|
|
}
|
|
}
|
|
}else{
|
|
android.util.Log.d("OXFace", "isNeedCamera: false");
|
|
}
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "摄像头数据处理异常", e);
|
|
}
|
|
}
|
|
});
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "启动摄像头预览失败", e);
|
|
// 尝试重新初始化摄像头
|
|
new Handler().postDelayed(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (!isFinishing()) {
|
|
LogManager.logInfo(TAG, "尝试重新启动摄像头预览");
|
|
startTestOpenDebugRegisterFunction();
|
|
}
|
|
}
|
|
}, 5000); // 5秒后重试
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 智能调节处理频率
|
|
* @param currentTime 当前时间
|
|
*/
|
|
private void adjustProcessFrequency(long currentTime) {
|
|
// 如果检测到人脸,加速处理
|
|
if (isFaceDetected) {
|
|
if (PROCESS_FRAME_INTERVAL != FAST_PROCESS_FRAME_INTERVAL ||
|
|
MIN_PROCESS_INTERVAL != FAST_MIN_PROCESS_INTERVAL) {
|
|
PROCESS_FRAME_INTERVAL = FAST_PROCESS_FRAME_INTERVAL;
|
|
MIN_PROCESS_INTERVAL = FAST_MIN_PROCESS_INTERVAL;
|
|
LogManager.logInfo(TAG, "检测到人脸,加速处理频率:每" + PROCESS_FRAME_INTERVAL + "帧,最小间隔" + MIN_PROCESS_INTERVAL + "ms");
|
|
}
|
|
}
|
|
// 如果长时间未检测到人脸,降低处理频率以节省资源
|
|
else if (currentTime - lastFaceDetectedTime > FACE_DETECTION_TIMEOUT) {
|
|
// 检查是否已经处于慢速处理模式
|
|
if (PROCESS_FRAME_INTERVAL != SLOW_PROCESS_FRAME_INTERVAL ||
|
|
MIN_PROCESS_INTERVAL != SLOW_MIN_PROCESS_INTERVAL) {
|
|
PROCESS_FRAME_INTERVAL = SLOW_PROCESS_FRAME_INTERVAL;
|
|
MIN_PROCESS_INTERVAL = SLOW_MIN_PROCESS_INTERVAL;
|
|
LogManager.logInfo(TAG, "长时间未检测到人脸,降低处理频率:每" + PROCESS_FRAME_INTERVAL + "帧,最小间隔" + MIN_PROCESS_INTERVAL + "ms");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 更新人脸检测状态
|
|
* @param livenessModel 活体检测模型
|
|
*/
|
|
private void updateFaceDetectionStatus(LivenessModel livenessModel) {
|
|
long currentTime = System.currentTimeMillis();
|
|
|
|
// 检查是否检测到人脸
|
|
boolean faceDetected = (livenessModel != null &&
|
|
livenessModel.getTrackFaceInfo() != null &&
|
|
livenessModel.getTrackFaceInfo().length > 0);
|
|
|
|
if (faceDetected) {
|
|
// 检测到人脸
|
|
isFaceDetected = true;
|
|
lastFaceDetectedTime = currentTime;
|
|
consecutiveNoFaceCount = 0; // 重置连续未检测到人脸计数器
|
|
|
|
LogManager.logDebug(TAG, "检测到人脸");
|
|
} else {
|
|
// 未检测到人脸
|
|
consecutiveNoFaceCount++;
|
|
|
|
// 如果连续多次未检测到人脸,标记为未检测到人脸状态
|
|
if (consecutiveNoFaceCount >= CONSECUTIVE_NO_FACE_THRESHOLD) {
|
|
isFaceDetected = false;
|
|
LogManager.logDebug(TAG, "连续未检测到人脸,当前计数: " + consecutiveNoFaceCount);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 绘制人脸框
|
|
*/
|
|
private void showFrame(final LivenessModel model) {
|
|
runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
Canvas canvas = mDrawDetectFaceView.lockCanvas();
|
|
if (canvas == null) {
|
|
return;
|
|
}
|
|
|
|
// 始终清空canvas
|
|
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
|
|
|
|
// 如果没有模型数据,直接返回清空的画布
|
|
if (model == null) {
|
|
mDrawDetectFaceView.unlockCanvasAndPost(canvas);
|
|
return;
|
|
}
|
|
|
|
// 获取人脸信息
|
|
FaceInfo[] faceInfos = model.getTrackFaceInfo();
|
|
|
|
// 如果没有检测到人脸,直接返回清空的画布
|
|
if (faceInfos == null || faceInfos.length == 0) {
|
|
mDrawDetectFaceView.unlockCanvasAndPost(canvas);
|
|
return;
|
|
}
|
|
|
|
// 只在检测到人脸时绘制人脸框
|
|
for (int i = 0; i < faceInfos.length; i++) {
|
|
FaceInfo faceInfo = faceInfos[i];
|
|
|
|
// 如果人脸置信度太低,不绘制
|
|
if (faceInfo.score < 0.6) {
|
|
continue;
|
|
}
|
|
|
|
rectF.set(FaceOnDrawTexturViewUtil.getFaceRectTwo(faceInfo));
|
|
// 检测图片的坐标和显示的坐标不一样,需要转换。
|
|
FaceOnDrawTexturViewUtil.mapFromOriginalRect(rectF,
|
|
mAutoCameraPreviewView, model.getBdFaceImageInstance());
|
|
// 人脸框颜色
|
|
FaceOnDrawTexturViewUtil.drawFaceColor(paint, paintBg, liveStatus, model);
|
|
// 绘制人脸框
|
|
FaceOnDrawTexturViewUtil.drawRect(canvas,
|
|
rectF, paint, 5f, 50f, 25f);
|
|
}
|
|
|
|
// 提交canvas
|
|
mDrawDetectFaceView.unlockCanvasAndPost(canvas);
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void onClick(View view) {
|
|
int id = view.getId();
|
|
if (id == R.id.btn_back) {
|
|
finish();
|
|
} else if (id == R.id.button_open_door) {
|
|
// 开门按钮点击事件
|
|
handleOpenDoorClick();
|
|
} else if (id == R.id.button_memory_info) {
|
|
// 内存信息按钮点击事件
|
|
displayMemoryInfo();
|
|
}
|
|
// else if (id == R.id.button_scan_qr) {
|
|
// // 扫码按钮点击事件
|
|
// handleScanQRClick();
|
|
// } else if (id == R.id.button_settings) {
|
|
// // 设置按钮点击事件
|
|
// handleSettingsClick();
|
|
// }
|
|
else if (id == R.id.img_mini_program_code) {
|
|
// 新用户扫码上传人脸点击事件
|
|
handleMiniProgramCodeClick();
|
|
} else if (id == R.id.img_scan_door_qrcode) {
|
|
// 扫码开门点击事件
|
|
handleScanDoorQRCodeClick();
|
|
} else if (id == R.id.btn_verification_code) {
|
|
// 验证码开门点击事件
|
|
handleVerificationCodeClick();
|
|
} else if (id == R.id.btn_scanner_door) {
|
|
// 扫码器开门点击事件
|
|
handleScannerDoorClick();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 处理开门按钮点击事件
|
|
*/
|
|
private void handleOpenDoorClick() {
|
|
LogManager.logInfo(TAG, "用户点击开门按钮");
|
|
Toast.makeText(this, "开门功能已触发", Toast.LENGTH_SHORT).show();
|
|
|
|
// 这里可以添加实际的开门逻辑
|
|
// 例如:发送网络请求到服务器执行开门操作
|
|
// 或者通过蓝牙/WiFi连接门锁设备
|
|
|
|
// 显示开门状态
|
|
if (layoutCompareStatus != null) {
|
|
layoutCompareStatus.setVisibility(View.VISIBLE);
|
|
textCompareStatus.setTextColor(Color.parseColor("#009874"));
|
|
textCompareStatus.setText("正在开门...");
|
|
|
|
// 3秒后隐藏状态提示
|
|
new Handler().postDelayed(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (layoutCompareStatus != null) {
|
|
layoutCompareStatus.setVisibility(View.GONE);
|
|
}
|
|
}
|
|
}, 3000);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 处理扫码按钮点击事件
|
|
*/
|
|
// private void handleScanQRClick() {
|
|
// LogManager.logInfo(TAG, "用户点击扫码按钮");
|
|
// Toast.makeText(this, "扫码功能已触发", Toast.LENGTH_SHORT).show();
|
|
|
|
// // 这里可以添加实际的扫码逻辑
|
|
// // 例如:启动扫码Activity或显示扫码界面
|
|
|
|
// // 显示扫码状态
|
|
// if (layoutCompareStatus != null) {
|
|
// layoutCompareStatus.setVisibility(View.VISIBLE);
|
|
// textCompareStatus.setTextColor(Color.parseColor("#009874"));
|
|
// textCompareStatus.setText("请将二维码对准扫描区域");
|
|
|
|
// // 3秒后隐藏状态提示
|
|
// new Handler().postDelayed(new Runnable() {
|
|
// @Override
|
|
// public void run() {
|
|
// if (layoutCompareStatus != null) {
|
|
// layoutCompareStatus.setVisibility(View.GONE);
|
|
// }
|
|
// }
|
|
// }, 3000);
|
|
// }
|
|
// }
|
|
|
|
/**
|
|
* 处理设置按钮点击事件
|
|
*/
|
|
private void handleSettingsClick() {
|
|
LogManager.logInfo(TAG, "用户点击设置按钮");
|
|
Toast.makeText(this, "设置功能已触发", Toast.LENGTH_SHORT).show();
|
|
|
|
// 这里可以添加实际的设置逻辑
|
|
// 例如:启动设置Activity或显示设置对话框
|
|
|
|
// 显示设置状态
|
|
if (layoutCompareStatus != null) {
|
|
layoutCompareStatus.setVisibility(View.VISIBLE);
|
|
textCompareStatus.setTextColor(Color.parseColor("#009874"));
|
|
textCompareStatus.setText("正在加载设置...");
|
|
|
|
// 3秒后隐藏状态提示
|
|
new Handler().postDelayed(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (layoutCompareStatus != null) {
|
|
layoutCompareStatus.setVisibility(View.GONE);
|
|
}
|
|
}
|
|
}, 3000);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 处理新用户扫码上传人脸点击事件
|
|
*/
|
|
private void handleMiniProgramCodeClick() {
|
|
LogManager.logInfo(TAG, "用户点击新用户扫码上传人脸");
|
|
Toast.makeText(this, "新用户扫码上传人脸功能已触发", Toast.LENGTH_SHORT).show();
|
|
|
|
// 这里可以添加实际的扫码逻辑
|
|
// 例如:启动扫码Activity或显示扫码界面
|
|
|
|
// 显示扫码状态
|
|
if (layoutCompareStatus != null) {
|
|
layoutCompareStatus.setVisibility(View.VISIBLE);
|
|
textCompareStatus.setTextColor(Color.parseColor("#009874"));
|
|
textCompareStatus.setText("请使用小程序扫码上传人脸");
|
|
|
|
// 3秒后隐藏状态提示
|
|
new Handler().postDelayed(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (layoutCompareStatus != null) {
|
|
layoutCompareStatus.setVisibility(View.GONE);
|
|
}
|
|
}
|
|
}, 3000);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 处理扫码开门点击事件
|
|
*/
|
|
private void handleScanDoorQRCodeClick() {
|
|
LogManager.logInfo(TAG, "用户点击扫码开门");
|
|
Toast.makeText(this, "扫码开门功能已触发", Toast.LENGTH_SHORT).show();
|
|
|
|
// 这里可以添加实际的扫码开门逻辑
|
|
// 例如:启动扫码Activity或显示扫码界面
|
|
|
|
// 显示扫码状态
|
|
if (layoutCompareStatus != null) {
|
|
layoutCompareStatus.setVisibility(View.VISIBLE);
|
|
textCompareStatus.setTextColor(Color.parseColor("#009874"));
|
|
textCompareStatus.setText("请扫描二维码开门");
|
|
|
|
// 3秒后隐藏状态提示
|
|
new Handler().postDelayed(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (layoutCompareStatus != null) {
|
|
layoutCompareStatus.setVisibility(View.GONE);
|
|
}
|
|
}
|
|
}, 3000);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 处理验证码开门点击事件
|
|
*/
|
|
private void handleVerificationCodeClick() {
|
|
LogManager.logInfo(TAG, "用户点击验证码开门");
|
|
|
|
// 使用新的暂停摄像头方法
|
|
pauseCameraWithTimeout();
|
|
LogManager.logInfo(TAG, "暂停摄像头预览 - 使用带超时的暂停方法");
|
|
|
|
// 启动验证码Activity
|
|
Intent intent = new Intent(this, VerificationCodeActivity.class);
|
|
startActivityForResult(intent, 1001);
|
|
}
|
|
|
|
/**
|
|
* 处理扫码器开门点击事件
|
|
*/
|
|
private void handleScannerDoorClick2() {
|
|
LogManager.logInfo(TAG, "用户点击扫码器开门");
|
|
|
|
// 检查是否有正在进行的人脸识别请求
|
|
if (orderVerificationManager != null && orderVerificationManager.isRequestInProgress()) {
|
|
LogManager.logInfo(TAG, "有正在进行的订单验证请求,取消扫码操作");
|
|
Toast.makeText(this, "正在处理其他请求,请稍后再试", Toast.LENGTH_SHORT).show();
|
|
return;
|
|
}
|
|
|
|
// 暂停摄像头预览
|
|
pauseCameraWithTimeout();
|
|
LogManager.logInfo(TAG, "已暂停摄像头预览");
|
|
|
|
// 调用华为统一扫码功能
|
|
com.ouxuan.oxface.device.HuaWeiScanManager.doScan(this, new com.ouxuan.oxface.device.HuaWeiScanManager.ScanResultListener() {
|
|
@Override
|
|
public void onScanSuccess(String scanResult) {
|
|
LogManager.logInfo(TAG, "华为扫码器扫码成功,结果: " + scanResult);
|
|
|
|
runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
Toast.makeText(OXFaceOnlineActivity.this, "扫码成功: " + scanResult, Toast.LENGTH_SHORT).show();
|
|
|
|
// 设置模式为4(扫码器验证)
|
|
modeType = OrderVerificationManager.TYPE_SCANNER_VERIFICATION;
|
|
verifyCode = scanResult;
|
|
|
|
// 执行扫码器核销
|
|
getCheckOrder();
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void onScanFailed(int errorCode, String errorMsg) {
|
|
LogManager.logError(TAG, "华为扫码器扫码失败,错误码: " + errorCode + ", 错误信息: " + errorMsg);
|
|
|
|
// 恢复摄像头预览
|
|
resumeCamera();
|
|
LogManager.logInfo(TAG, "扫码失败,已恢复摄像头预览");
|
|
|
|
runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
Toast.makeText(OXFaceOnlineActivity.this, "扫码失败: " + errorMsg, Toast.LENGTH_SHORT).show();
|
|
|
|
// 显示状态提示
|
|
if (layoutCompareStatus != null) {
|
|
layoutCompareStatus.setVisibility(View.VISIBLE);
|
|
textCompareStatus.setTextColor(Color.parseColor("#FF0000"));
|
|
textCompareStatus.setText("扫码失败: " + errorMsg);
|
|
|
|
// 3秒后隐藏状态提示
|
|
new Handler().postDelayed(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (layoutCompareStatus != null) {
|
|
layoutCompareStatus.setVisibility(View.GONE);
|
|
}
|
|
}
|
|
}, 3000);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
|
if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) {
|
|
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
|
LogManager.logInfo(TAG, "相机权限已授予,启动摄像头预览");
|
|
startTestOpenDebugRegisterFunction();
|
|
} else {
|
|
LogManager.logError(TAG, "相机权限被拒绝");
|
|
Toast.makeText(this, "需要相机权限才能使用人脸识别功能", Toast.LENGTH_LONG).show();
|
|
finish();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onDestroy() {
|
|
super.onDestroy();
|
|
|
|
// 销毁网络状态指示器
|
|
if (networkStatusIndicator != null) {
|
|
networkStatusIndicator.destroy();
|
|
networkStatusIndicator = null;
|
|
}
|
|
|
|
// 释放AB门禁管理和不可用弹窗资源
|
|
if (gateUnavailableDialog != null) {
|
|
gateUnavailableDialog.release();
|
|
gateUnavailableDialog = null;
|
|
LogManager.logInfo(TAG, "门禁不可用弹窗资源已释放");
|
|
}
|
|
|
|
if (gateABController != null) {
|
|
gateABController.release();
|
|
gateABController = null;
|
|
LogManager.logInfo(TAG, "AB门控制器资源已释放");
|
|
}
|
|
|
|
if (abGateManager != null) {
|
|
abGateManager.release();
|
|
abGateManager = null;
|
|
LogManager.logInfo(TAG, "AB门禁管理器资源已释放");
|
|
}
|
|
|
|
// 注销广播接收器
|
|
if (cameraControlReceiver != null) {
|
|
unregisterReceiver(cameraControlReceiver);
|
|
LogManager.logInfo(TAG, "摄像头控制广播接收器已注销");
|
|
}
|
|
|
|
// 停止摄像头预览
|
|
if (CameraPreviewManager.getInstance() != null) {
|
|
CameraPreviewManager.getInstance().stopPreview();
|
|
}
|
|
|
|
// 释放当前的LivenessModel资源
|
|
if (currentLivenessModel != null) {
|
|
BDFaceImageInstance image = currentLivenessModel.getBdFaceImageInstance();
|
|
if (image != null) {
|
|
image.destory();
|
|
}
|
|
// 清空引用
|
|
currentLivenessModel = null;
|
|
}
|
|
|
|
// 释放绘图资源
|
|
if (rectF != null) {
|
|
rectF = null;
|
|
}
|
|
if (paint != null) {
|
|
paint = null;
|
|
}
|
|
if (paintBg != null) {
|
|
paintBg = null;
|
|
}
|
|
|
|
// 清除其他相关资源
|
|
System.gc();
|
|
|
|
LogManager.logInfo(TAG, "OXFaceOnlineActivity onDestroy - 资源已释放");
|
|
}
|
|
|
|
@Override
|
|
protected void onStop() {
|
|
super.onStop();
|
|
|
|
// 停止网络状态监控
|
|
if (networkStatusIndicator != null) {
|
|
networkStatusIndicator.stopMonitoring();
|
|
}
|
|
|
|
// 停止UDP门禁状态轮询
|
|
if (gateABController != null) {
|
|
gateABController.stopUDPPolling();
|
|
LogManager.logInfo(TAG, "UDP门禁状态轮询已停止");
|
|
}
|
|
|
|
LogManager.logInfo(TAG, "OXFaceOnlineActivity onStop - 应用进入后台");
|
|
|
|
// 应用进入后台时,进一步降低处理频率以节省资源
|
|
PROCESS_FRAME_INTERVAL = 30; // 后台时每30帧处理一次
|
|
MIN_PROCESS_INTERVAL = 3000; // 后台时最小间隔3秒
|
|
|
|
LogManager.logInfo(TAG, "后台模式处理频率:每" + PROCESS_FRAME_INTERVAL + "帧,最小间隔" + MIN_PROCESS_INTERVAL + "ms");
|
|
}
|
|
|
|
@Override
|
|
protected void onStart() {
|
|
super.onStart();
|
|
LogManager.logInfo(TAG, "OXFaceOnlineActivity onStart - 应用回到前台");
|
|
|
|
// 应用回到前台时,恢复正常的处理频率
|
|
PROCESS_FRAME_INTERVAL = FAST_PROCESS_FRAME_INTERVAL; // 前台时使用快速处理频率
|
|
MIN_PROCESS_INTERVAL = FAST_MIN_PROCESS_INTERVAL; // 前台时使用快速处理间隔
|
|
|
|
LogManager.logInfo(TAG, "前台模式处理频率:每" + PROCESS_FRAME_INTERVAL + "帧,最小间隔" + MIN_PROCESS_INTERVAL + "ms");
|
|
}
|
|
|
|
// ***************开发模式结果输出*************
|
|
private void checkOpenDebugResult(final LivenessModel livenessModel) {
|
|
// 当未检测到人脸UI显示
|
|
runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
layoutCompareStatus.setBackgroundColor(Color.parseColor("#CC000000"));
|
|
if (livenessModel == null) {
|
|
liveStatus = false;
|
|
layoutCompareStatus.setVisibility(View.GONE);
|
|
// mFaceDetectImageView.setImageResource(R.mipmap.ic_image_video);
|
|
// mTvDetect.setText(String.format("检测耗时 :%s ms", 0));
|
|
// mTvLive.setText(String.format("RGB活体检测耗时 :%s ms", 0));
|
|
// mTvLiveScore.setText(String.format("RGB活体检测结果 :%s", false));
|
|
// mTvAllTime.setText(String.format("总耗时 :%s ms", 0));
|
|
return;
|
|
}
|
|
|
|
BDFaceImageInstance image = livenessModel.getBdFaceImageInstance();
|
|
if (image != null) {
|
|
// mFaceDetectImageView.setImageBitmap(BitmapUtils.getInstaceBmp(image));
|
|
float faceArea = livenessModel.getFaceInfo().width * livenessModel.getFaceInfo().height;
|
|
image.destory();
|
|
|
|
// 新增:人脸过小检测
|
|
if (faceArea < MIN_FACE_AREA) {
|
|
layoutCompareStatus.setVisibility(View.VISIBLE);
|
|
textCompareStatus.setTextColor(Color.parseColor("#fec133"));
|
|
textCompareStatus.setText("请靠近设备");
|
|
return;
|
|
}
|
|
}
|
|
|
|
// 模糊结果过滤
|
|
float blur = livenessModel.getFaceInfo().bluriness;
|
|
if (blur > SingleBaseConfig.getBaseConfig().getBlur()) {
|
|
//照片模糊
|
|
layoutCompareStatus.setVisibility(View.VISIBLE);
|
|
textCompareStatus.setTextColor(Color.parseColor("#fec133"));
|
|
textCompareStatus.setText("人脸模糊,请摘掉眼镜帽子调整识别角度");
|
|
return;
|
|
}
|
|
|
|
//质量检测
|
|
if (!livenessModel.isQualityCheck()) {
|
|
liveStatus = false;
|
|
} else {
|
|
// 活体检测逻辑
|
|
if (SingleBaseConfig.getBaseConfig().isLivingControl()) {
|
|
if (livenessModel.isRGBLiveStatus()) {
|
|
liveStatus = true;
|
|
layoutCompareStatus.setVisibility(View.VISIBLE);
|
|
textCompareStatus.setTextColor(Color.parseColor("#009874"));
|
|
textCompareStatus.setText("通过活体检测");
|
|
} else {
|
|
liveStatus = false;
|
|
layoutCompareStatus.setVisibility(View.VISIBLE);
|
|
textCompareStatus.setTextColor(Color.parseColor("#fec133"));
|
|
textCompareStatus.setText("请尝试移动人脸到屏幕正中");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
FaceInfo faceInfo = livenessModel.getFaceInfo();
|
|
if (faceInfo.bestImageScore < SingleBaseConfig.getBaseConfig().getBestImageScore()) {
|
|
layoutCompareStatus.setVisibility(View.VISIBLE);
|
|
textCompareStatus.setTextColor(Color.parseColor("#fec133"));
|
|
textCompareStatus.setText("阈值低于" + SingleBaseConfig.getBaseConfig().getBestImageScore());
|
|
return;
|
|
}
|
|
|
|
//通过检测
|
|
if (livenessModel != null) {
|
|
checkResultOnline(livenessModel);//在线人脸检测
|
|
} else {
|
|
currentLivenessModel = null;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
private void checkResultOnline(LivenessModel livenessModel) {
|
|
this.currentLivenessModel = livenessModel;
|
|
// 当未检测到人脸UI显示
|
|
runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (livenessModel == null) {
|
|
return;
|
|
}
|
|
|
|
// 减少base64转换频率,只有在特定条件下才进行转换
|
|
// 例如:每3秒检查一次是否需要发送人脸图像
|
|
long currentTime = System.currentTimeMillis();
|
|
if (currentTime - searshTime > 3000) {
|
|
searshTime = currentTime;
|
|
needSendFaceImage = true;
|
|
|
|
// 触发时机2:在线人脸检测开始前进行AB门人数检测
|
|
if (isGateCheckEnabled && abGateManager != null) {
|
|
LogManager.logInfo(TAG, "开始执行AB门人数检测(触发时机2)");
|
|
|
|
// 在后台线程中执行AB门人数检测
|
|
new Thread(() -> {
|
|
try {
|
|
// 执行人数检测
|
|
boolean peopleCheckPassed = abGateManager.ABPeopleCheck();
|
|
|
|
if (!peopleCheckPassed) {
|
|
// 人数检测未通过,显示弹窗
|
|
runOnUiThread(() -> {
|
|
try {
|
|
// 判断场景类型
|
|
boolean isLeaveScene = VenueSceneUtils.isLeaveScene(OXFaceOnlineActivity.this);
|
|
|
|
// 获取实际人数用于显示
|
|
gateABController.get485PeopleNum(new GateABController.PeopleNumCallback() {
|
|
@Override
|
|
public void onSuccess(int peopleNum) {
|
|
// 直接更新弹窗内容,不需要检查重复弹窗
|
|
showNewPeopleCountDialog(isLeaveScene, peopleNum);
|
|
}
|
|
|
|
@Override
|
|
public void onError(String errorMessage) {
|
|
LogManager.logError(TAG, "获取人数失败,无法显示具体人数: " + errorMessage);
|
|
|
|
// 直接更新弹窗内容,不需要检查重复弹窗
|
|
showNewGateUnavailableDialog("门禁内人数不符合要求,请等待");
|
|
}
|
|
});
|
|
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "处理人数检测结果异常", e);
|
|
}
|
|
});
|
|
|
|
// 人数检测未通过,中断后续的人脸识别流程
|
|
LogManager.logInfo(TAG, "AB门人数检测未通过,中断后续人脸识别流程");
|
|
return;
|
|
} else {
|
|
LogManager.logInfo(TAG, "AB门人数检测通过,继续人脸识别流程");
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "AB门人数检测异常", e);
|
|
// 异常情况下继续正常流程,不阻塞用户操作
|
|
LogManager.logInfo(TAG, "AB门检测异常,继续正常人脸识别流程");
|
|
}
|
|
|
|
// 继续正常的人脸识别流程(只有在检测通过或异常时才执行)
|
|
runOnUiThread(() -> processFaceRecognitionFlow());
|
|
|
|
}).start();
|
|
|
|
// AB门检测开启时,先返回,等待后台维续
|
|
return;
|
|
} else {
|
|
// AB门检测未开启,直接继续正常流程
|
|
processFaceRecognitionFlow();
|
|
}
|
|
} else {
|
|
// 时间间隔不够,跳过本次处理
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 处理人脸识别流程(从 checkResultOnline 中抽取出来)
|
|
*/
|
|
private void processFaceRecognitionFlow() {
|
|
String base64img = getFaceImageBase64(currentLivenessModel);
|
|
if (base64img != null) {
|
|
// 这里可以处理base64数据,如上传到服务器等
|
|
Log.i(TAG, "processFaceRecognitionFlow: 获取到人脸base64数据");
|
|
|
|
// 保存人脸base64数据到verifyCode变量
|
|
verifyCode = base64img;
|
|
|
|
// 设置模式为2(人脸验证)
|
|
modeType = OrderVerificationManager.TYPE_FACE_VERIFICATION;
|
|
|
|
// 判断是进场还是离场场景
|
|
if (VenueSceneUtils.isLeaveScene(OXFaceOnlineActivity.this)) {
|
|
// 离场场景:检查gate_enter_open_enable配置
|
|
VenueSceneUtils.GateConfig gateConfig = VenueSceneUtils.getGateConfig(OXFaceOnlineActivity.this);
|
|
if (gateConfig != null && gateConfig.gateEnterOpenEnable) {
|
|
// 如果 gate_enter_open_enable 为 true,直接开门不进行网络核销
|
|
LogManager.logInfo(TAG, "检测到离场场景,gate_enter_open_enable为true,直接开启B门");
|
|
showLoadingStatus("离场验证成功");
|
|
|
|
// 2秒后隐藏提示并开门
|
|
new Handler().postDelayed(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
hideLoadingStatus();
|
|
|
|
// 直接开启B门
|
|
if (gateABController != null) {
|
|
LogManager.logInfo(TAG, "gate_enter_open_enable为true,直接开启B门");
|
|
gateABController.handleFaceRecognitionSuccess(false); // 参数保留兼容性,实际都开B门
|
|
}
|
|
}
|
|
}, 2000);
|
|
} else {
|
|
// gate_enter_open_enable 为 false 或配置不存在,按正常流程进行网络校验
|
|
if (isNetworkAvailable()) {
|
|
LogManager.logInfo(TAG, "检测到离场场景,网络可用,执行离场校验");
|
|
performLeaveVerification(base64img);
|
|
} else {
|
|
LogManager.logWarning(TAG, "检测到离场场景,但网络不可用,直接开启B门");
|
|
showLoadingStatus("网络不可用,直接开门");
|
|
|
|
// 直接开启B门,防止用户卡在场内
|
|
if (gateABController != null) {
|
|
LogManager.logInfo(TAG, "离场场景网络不可用,直接开启B门");
|
|
gateABController.handleFaceRecognitionSuccess(false); // 开启B门
|
|
}
|
|
|
|
// 3秒后隐藏提示
|
|
new Handler().postDelayed(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
hideLoadingStatus();
|
|
}
|
|
}, 3000);
|
|
}
|
|
}
|
|
} else {
|
|
// 进场场景:先检查网络状态
|
|
if (isNetworkAvailable()) {
|
|
LogManager.logInfo(TAG, "检测到进场场景,网络可用,执行订单核销");
|
|
|
|
// 检查是否已有正在进行的人脸验证请求,避免重复请求
|
|
if (orderVerificationManager != null && orderVerificationManager.isRequestInProgress()) {
|
|
LogManager.logInfo(TAG, "已有正在进行的人脸验证请求,跳过本次请求");
|
|
return;
|
|
}
|
|
|
|
getCheckOrder();
|
|
} else {
|
|
LogManager.logWarning(TAG, "检测到进场场景,但网络不可用");
|
|
showLoadingStatus("无网络连接,请检查网络设置");
|
|
// 3秒后隐藏提示
|
|
new Handler().postDelayed(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
hideLoadingStatus();
|
|
}
|
|
}, 3000);
|
|
|
|
// 网络不可用时,不执行任何门禁操作,保持安全
|
|
LogManager.logInfo(TAG, "进场场景网络不可用,等待网络恢复");
|
|
}
|
|
}
|
|
|
|
// 处理完成后重置标志
|
|
needSendFaceImage = false;
|
|
} else {
|
|
Log.i(TAG, "processFaceRecognitionFlow: base64img score too low ");
|
|
layoutCompareStatus.setVisibility(View.VISIBLE);
|
|
textCompareStatus.setTextColor(Color.parseColor("#fec133"));
|
|
textCompareStatus.setText("请重新识别");
|
|
}
|
|
}
|
|
|
|
private String getFaceImageBase64(LivenessModel livenessModel) {
|
|
if (livenessModel == null) return null;
|
|
FaceInfo faceInfo = livenessModel.getFaceInfo();
|
|
|
|
// 添加判断条件,减少不必要的base64转换
|
|
Log.d(TAG, faceInfo.leftEyeclose+" "+faceInfo.score+" getFaceImageBase64: "+faceInfo.bestImageScore + " getBestImageScore-Config:"+SingleBaseConfig.getBaseConfig().getBestImageScore());
|
|
if (faceInfo != null && faceInfo.bestImageScore > SingleBaseConfig.getBaseConfig().getBestImageScore()) {
|
|
BDFaceImageInstance image = livenessModel.getBdFaceImageInstance();
|
|
if (image != null) {
|
|
Bitmap bitmap = BitmapUtils.getInstaceBmp(image);
|
|
|
|
// 仅在确实需要使用base64数据时才进行转换
|
|
if (needSendFaceImage) {
|
|
String bitmap_str = BitmapUtils.bitmapToBase64(bitmap);
|
|
image.destory();
|
|
return bitmap_str;
|
|
} else {
|
|
// 如果不需要发送,则不进行base64转换,直接释放资源
|
|
image.destory();
|
|
if (bitmap != null && !bitmap.isRecycled()) {
|
|
bitmap.recycle();
|
|
}
|
|
return "dummy_base64_placeholder";
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* 检查应用是否在前台
|
|
* @return 如果应用在前台,则返回true;否则返回false
|
|
*/
|
|
private boolean isApplicationInForeground() {
|
|
android.app.ActivityManager activityManager = (android.app.ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
|
|
List<android.app.ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
|
|
if (appProcesses == null) {
|
|
return false;
|
|
}
|
|
|
|
String packageName = getPackageName();
|
|
for (android.app.ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
|
|
if (appProcess.importance == android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
|
|
&& appProcess.processName.equals(packageName)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* 低内存时的处理
|
|
*/
|
|
@Override
|
|
public void onLowMemory() {
|
|
super.onLowMemory();
|
|
LogManager.logInfo(TAG, "系统内存不足,触发onLowMemory");
|
|
|
|
// 清理不必要的缓存
|
|
clearMemoryCache();
|
|
|
|
// 强制GC
|
|
System.gc();
|
|
}
|
|
|
|
/**
|
|
* 清理内存缓存
|
|
*/
|
|
private void clearMemoryCache() {
|
|
// 如果当前不在人脸检测状态,释放当前的LivenessModel
|
|
if (currentLivenessModel != null && !isNeedCamera) {
|
|
BDFaceImageInstance image = currentLivenessModel.getBdFaceImageInstance();
|
|
if (image != null) {
|
|
image.destory();
|
|
}
|
|
currentLivenessModel = null;
|
|
}
|
|
|
|
// 如果设备内存不足,考虑调整处理频率
|
|
PROCESS_FRAME_INTERVAL = Math.min(30, PROCESS_FRAME_INTERVAL + 5);
|
|
MIN_PROCESS_INTERVAL = Math.min(3000, MIN_PROCESS_INTERVAL + 500);
|
|
|
|
LogManager.logInfo(TAG, "内存不足,调整处理频率:每" + PROCESS_FRAME_INTERVAL + "帧,最小间隔" + MIN_PROCESS_INTERVAL + "ms");
|
|
}
|
|
|
|
/**
|
|
* 优化内存的周期性任务
|
|
*/
|
|
private void startMemoryOptimizationTask() {
|
|
new Handler().postDelayed(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
// 获取当前应用内存使用情况
|
|
Runtime runtime = Runtime.getRuntime();
|
|
long usedMemory = (runtime.totalMemory() - runtime.freeMemory()) / (1024 * 1024);
|
|
long totalMemory = runtime.maxMemory() / (1024 * 1024);
|
|
float memoryUsageRatio = (float) usedMemory / totalMemory;
|
|
|
|
LogManager.logInfo(TAG, "内存使用情况:" + usedMemory + "MB/" + totalMemory +
|
|
"MB,使用率:" + (int)(memoryUsageRatio * 100) + "%");
|
|
|
|
// 如果内存使用率超过70%,主动清理内存
|
|
if (memoryUsageRatio > 0.7f) {
|
|
LogManager.logInfo(TAG, "内存使用率超过70%,主动清理内存");
|
|
clearMemoryCache();
|
|
System.gc();
|
|
}
|
|
|
|
// 如果内存使用率超过85%,进行更积极的清理
|
|
if (memoryUsageRatio > 0.85f) {
|
|
LogManager.logWarning(TAG, "内存使用率超过85%,进行积极清理");
|
|
// 进一步降低处理频率
|
|
PROCESS_FRAME_INTERVAL = Math.min(50, PROCESS_FRAME_INTERVAL + 10);
|
|
MIN_PROCESS_INTERVAL = Math.min(5000, MIN_PROCESS_INTERVAL + 1000);
|
|
|
|
// 清理更多资源
|
|
if (currentLivenessModel != null) {
|
|
BDFaceImageInstance image = currentLivenessModel.getBdFaceImageInstance();
|
|
if (image != null) {
|
|
image.destory();
|
|
}
|
|
currentLivenessModel = null;
|
|
}
|
|
|
|
LogManager.logInfo(TAG, "积极清理后调整处理频率:每" + PROCESS_FRAME_INTERVAL + "帧,最小间隔" + MIN_PROCESS_INTERVAL + "ms");
|
|
System.gc();
|
|
}
|
|
|
|
// 每30秒执行一次
|
|
if (!isFinishing()) {
|
|
new Handler().postDelayed(this, 30000);
|
|
}
|
|
}
|
|
}, 30000);
|
|
}
|
|
|
|
/**
|
|
* 更新小程序码显示
|
|
*/
|
|
private void updateMiniQrcode() {
|
|
LogManager.logInfo(TAG, "开始更新小程序码");
|
|
try {
|
|
DeviceSelectDataManager deviceSelectDataManager = DeviceSelectDataManager.getInstance(this);
|
|
|
|
// 更新普通小程序码(扫码开门)
|
|
boolean hasQrcode = deviceSelectDataManager.hasMiniQrcodeUrl();
|
|
if (hasQrcode) {
|
|
String base64Qrcode = deviceSelectDataManager.getMiniQrcodeUrl();
|
|
if (base64Qrcode != null && !base64Qrcode.isEmpty()) {
|
|
// 在主线程中更新UI
|
|
runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
try {
|
|
// 处理base64数据并显示二维码
|
|
String decodedBase64 = base64Qrcode;
|
|
if (base64Qrcode.startsWith("data:image")) {
|
|
int commaIndex = base64Qrcode.indexOf(',');
|
|
if (commaIndex > 0) {
|
|
decodedBase64 = base64Qrcode.substring(commaIndex + 1);
|
|
}
|
|
}
|
|
|
|
Bitmap qrcodeBitmap = BitmapUtils.base64ToBitmap(decodedBase64);
|
|
if (qrcodeBitmap != null && imgScanDoorQRCode != null) {
|
|
imgScanDoorQRCode.setImageBitmap(qrcodeBitmap);
|
|
LogManager.logInfo(TAG, "扫码开门小程序码更新成功");
|
|
} else {
|
|
LogManager.logWarning(TAG, "扫码开门小程序码更新失败");
|
|
}
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "更新扫码开门小程序码异常: " + e.getMessage(), e);
|
|
}
|
|
}
|
|
});
|
|
} else {
|
|
LogManager.logWarning(TAG, "扫码开门小程序码数据为空");
|
|
}
|
|
} else {
|
|
LogManager.logDebug(TAG, "未找到扫码开门小程序码链接");
|
|
}
|
|
|
|
// 更新上传人脸小程序码
|
|
boolean hasUploadFaceQrcode = deviceSelectDataManager.hasUploadFaceMiniQrcodeUrl();
|
|
if (hasUploadFaceQrcode) {
|
|
String uploadFaceQrcodeUrl = deviceSelectDataManager.getUploadFaceMiniQrcodeUrl();
|
|
if (uploadFaceQrcodeUrl != null && !uploadFaceQrcodeUrl.isEmpty()) {
|
|
// 在主线程中更新UI
|
|
runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
try {
|
|
// 使用Glide加载网络图片
|
|
if (imgMiniProgramCode != null) {
|
|
com.bumptech.glide.Glide.with(OXFaceOnlineActivity.this)
|
|
.load(uploadFaceQrcodeUrl)
|
|
.into(imgMiniProgramCode);
|
|
LogManager.logInfo(TAG, "上传人脸小程序码更新成功: " + uploadFaceQrcodeUrl);
|
|
} else {
|
|
LogManager.logWarning(TAG, "上传人脸小程序码ImageView为空");
|
|
}
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "更新上传人脸小程序码异常: " + e.getMessage(), e);
|
|
}
|
|
}
|
|
});
|
|
} else {
|
|
LogManager.logWarning(TAG, "上传人脸小程序码URL为空");
|
|
}
|
|
} else {
|
|
LogManager.logDebug(TAG, "未找到上传人脸小程序码链接");
|
|
}
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "获取小程序码异常: " + e.getMessage(), e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取并显示店铺名称
|
|
*/
|
|
private void loadAndDisplayStoreName() {
|
|
try {
|
|
DeviceSelectDataManager deviceDataManager = DeviceSelectDataManager.getInstance(this);
|
|
String[] storeInfo = deviceDataManager.getStoreInfo();
|
|
String storeName = storeInfo[0]; // 第一个元素是店铺名称
|
|
|
|
if (storeName != null && !storeName.isEmpty()) {
|
|
// 根据VenueSceneUtils.isLeaveScene来区分进场或离场
|
|
if (VenueSceneUtils.isLeaveScene(this)) {
|
|
// 离场场景
|
|
tvStoreName.setText("离场 | " + storeName);
|
|
} else {
|
|
// 进场场景
|
|
tvStoreName.setText("进场 | " + storeName);
|
|
}
|
|
LogManager.logInfo(TAG, "成功显示店铺名称: " + storeName);
|
|
} else {
|
|
// 根据VenueSceneUtils.isLeaveScene来区分进场或离场
|
|
if (VenueSceneUtils.isLeaveScene(this)) {
|
|
// 离场场景
|
|
tvStoreName.setText("离场 | 未获取到店铺名称");
|
|
} else {
|
|
// 进场场景
|
|
tvStoreName.setText("进场 | 未获取到店铺名称");
|
|
}
|
|
LogManager.logWarning(TAG, "未获取到店铺名称");
|
|
}
|
|
} catch (Exception e) {
|
|
// 根据VenueSceneUtils.isLeaveScene来区分进场或离场
|
|
if (VenueSceneUtils.isLeaveScene(this)) {
|
|
// 离场场景
|
|
tvStoreName.setText("离场 | 店铺名称获取失败");
|
|
} else {
|
|
// 进场场景
|
|
tvStoreName.setText("进场 | 店铺名称获取失败");
|
|
}
|
|
LogManager.logError(TAG, "获取店铺名称失败", e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 处理店铺名称点击事件
|
|
*/
|
|
private void handleStoreNameClick() {
|
|
long currentTime = System.currentTimeMillis();
|
|
|
|
// 如果距离上次点击时间超过1秒,重置计数器
|
|
if (currentTime - lastStoreNameClickTime > CLICK_INTERVAL) {
|
|
storeNameClickCount = 0;
|
|
}
|
|
|
|
storeNameClickCount++;
|
|
lastStoreNameClickTime = currentTime;
|
|
|
|
LogManager.logDebug(TAG, "店铺名称被点击,当前计数: " + storeNameClickCount);
|
|
|
|
// 如果连续点击达到5次
|
|
if (storeNameClickCount >= MAX_CLICK_COUNT) {
|
|
LogManager.logInfo(TAG, "连续点击5次店铺名称,准备返回主界面");
|
|
Toast.makeText(this, "即将返回主界面", Toast.LENGTH_SHORT).show();
|
|
|
|
// 重置计数器
|
|
storeNameClickCount = 0;
|
|
|
|
// 延迟一段时间后返回主界面
|
|
new Handler().postDelayed(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
returnToMainActivity();
|
|
}
|
|
}, 500); // 延迟0.5秒后返回
|
|
} else if (storeNameClickCount >= 3) {
|
|
// 当点击次数达到3次时,提示用户还需几次点击
|
|
int remainingClicks = MAX_CLICK_COUNT - storeNameClickCount;
|
|
Toast.makeText(this, "再点击" + remainingClicks + "次返回主界面", Toast.LENGTH_SHORT).show();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 返回主界面
|
|
*/
|
|
private void returnToMainActivity() {
|
|
LogManager.logInfo(TAG, "返回主界面");
|
|
Intent intent = new Intent(this, MainActivity.class);
|
|
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
startActivity(intent);
|
|
finish();
|
|
}
|
|
|
|
/**
|
|
* 处理验证码Activity返回结果
|
|
*/
|
|
@Override
|
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
super.onActivityResult(requestCode, resultCode, data);
|
|
|
|
// 处理华为扫码结果
|
|
boolean handled = com.ouxuan.oxface.device.HuaWeiScanManager.handleScanResult(requestCode, resultCode, data, new com.ouxuan.oxface.device.HuaWeiScanManager.ScanResultListener() {
|
|
@Override
|
|
public void onScanSuccess(String scanResult) {
|
|
LogManager.logInfo(TAG, "华为扫码成功,结果: " + scanResult);
|
|
Log.d(TAG, "扫码结果: " + scanResult);
|
|
|
|
runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
Toast.makeText(OXFaceOnlineActivity.this, "扫码成功: " + scanResult, Toast.LENGTH_SHORT).show();
|
|
|
|
// 调用checkOrder函数进行扫码核销
|
|
performScanVerification();
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void onScanFailed(int errorCode, String errorMsg) {
|
|
LogManager.logError(TAG, "华为扫码失败,错误码: " + errorCode + ", 错误信息: " + errorMsg);
|
|
Log.e(TAG, "扫码失败,错误码: " + errorCode + ", 错误信息: " + errorMsg);
|
|
|
|
// 恢复摄像头预览
|
|
resumeCamera();
|
|
LogManager.logInfo(TAG, "扫码失败,已恢复摄像头预览");
|
|
|
|
runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
Toast.makeText(OXFaceOnlineActivity.this, "扫码失败: " + errorMsg, Toast.LENGTH_SHORT).show();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
if (handled) {
|
|
// 如果已经处理了扫码结果,也需要恢复摄像头预览
|
|
if (requestCode == com.ouxuan.oxface.device.HuaWeiScanManager.REQUEST_CODE_SCAN) {
|
|
resumeCamera();
|
|
LogManager.logInfo(TAG, "华为扫码完成,已恢复摄像头预览");
|
|
}
|
|
return; // 如果已经处理了扫码结果,则直接返回
|
|
}
|
|
|
|
if (requestCode == 1001) { // 验证码验证获取输入结果
|
|
// 恢复摄像头预览
|
|
resumeCamera();
|
|
LogManager.logInfo(TAG, "恢复摄像头预览 - 使用resumeCamera方法");
|
|
|
|
if (resultCode == RESULT_OK && data != null) {
|
|
String verificationCode = data.getStringExtra("verification_code");
|
|
if (verificationCode != null && verificationCode.length() == 12) {
|
|
LogManager.logInfo(TAG, "收到验证码: " + verificationCode);
|
|
|
|
android.util.Log.d("MainActivity", "收到验证码"+verificationCode);
|
|
|
|
handleVerificationCodeSubmit(verificationCode);
|
|
}
|
|
} else if (resultCode == RESULT_CANCELED) {
|
|
LogManager.logInfo(TAG, "用户点击关闭验证码弹窗");
|
|
} else {
|
|
LogManager.logInfo(TAG, "用户取消验证码输入");
|
|
}
|
|
} else if (requestCode == 1002) { // 订单选择页面返回结果
|
|
// 恢复摄像头预览
|
|
resumeCamera();
|
|
LogManager.logInfo(TAG, "恢复摄像头预览 - 使用resumeCamera方法");
|
|
|
|
if (resultCode == RESULT_OK && data != null) {
|
|
// 处理订单选择结果
|
|
String selectedOrderJson = data.getStringExtra("selected_order");
|
|
String orderNo = data.getStringExtra("order_no");
|
|
String verificationCode = data.getStringExtra("verification_code");
|
|
int orderType = data.getIntExtra("order_type", 0);
|
|
String cardNo = data.getStringExtra("card_no");
|
|
String project = data.getStringExtra("project");
|
|
int verificationType = data.getIntExtra("verification_type", 2);
|
|
|
|
LogManager.logInfo(TAG, "订单选择成功: " + orderNo);
|
|
|
|
// 显示成功状态
|
|
if (layoutCompareStatus != null) {
|
|
layoutCompareStatus.setVisibility(View.VISIBLE);
|
|
textCompareStatus.setTextColor(Color.parseColor("#009874"));
|
|
textCompareStatus.setText("订单核销成功");
|
|
|
|
// 3秒后隐藏状态提示
|
|
new Handler().postDelayed(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (layoutCompareStatus != null) {
|
|
layoutCompareStatus.setVisibility(View.GONE);
|
|
}
|
|
}
|
|
}, 3000);
|
|
}
|
|
|
|
} else if (resultCode == RESULT_CANCELED) {
|
|
LogManager.logInfo(TAG, "用户取消订单选择");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 处理验证码提交
|
|
*/
|
|
private void handleVerificationCodeSubmit(String verificationCode) {
|
|
// 设置验证码验证模式
|
|
modeType = OrderVerificationManager.TYPE_VERIFICATION_CODE;
|
|
verifyCode = verificationCode;
|
|
|
|
// 调用订单查询方法进行验证码验证
|
|
getCheckOrder();
|
|
}
|
|
|
|
/**
|
|
* 查询并显示当前应用的内存信息
|
|
*/
|
|
private void displayMemoryInfo() {
|
|
Runtime runtime = Runtime.getRuntime();
|
|
long maxMemory = runtime.maxMemory() / (1024 * 1024); // MB
|
|
long totalMemory = runtime.totalMemory() / (1024 * 1024); // MB
|
|
long freeMemory = runtime.freeMemory() / (1024 * 1024); // MB
|
|
long usedMemory = totalMemory - freeMemory; // MB
|
|
|
|
// 获取系统内存信息(需要API 16及以上)
|
|
ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
|
|
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
|
|
activityManager.getMemoryInfo(memoryInfo);
|
|
|
|
long totalMem = memoryInfo.totalMem / (1024 * 1024); // MB
|
|
long availMem = memoryInfo.availMem / (1024 * 1024); // MB
|
|
long threshold = memoryInfo.threshold / (1024 * 1024); // MB
|
|
boolean isLowMemory = memoryInfo.lowMemory;
|
|
|
|
// 构建内存信息字符串
|
|
StringBuilder memoryInfoStr = new StringBuilder();
|
|
memoryInfoStr.append("应用内存信息:\n");
|
|
memoryInfoStr.append("最大可用内存: ").append(maxMemory).append(" MB\n");
|
|
memoryInfoStr.append("已分配内存: ").append(totalMemory).append(" MB\n");
|
|
memoryInfoStr.append("已使用内存: ").append(usedMemory).append(" MB\n");
|
|
memoryInfoStr.append("空闲内存: ").append(freeMemory).append(" MB\n\n");
|
|
|
|
memoryInfoStr.append("系统内存信息:\n");
|
|
memoryInfoStr.append("总内存: ").append(totalMem).append(" MB\n");
|
|
memoryInfoStr.append("可用内存: ").append(availMem).append(" MB\n");
|
|
memoryInfoStr.append("内存阈值: ").append(threshold).append(" MB\n");
|
|
memoryInfoStr.append("低内存状态: ").append(isLowMemory ? "是" : "否");
|
|
|
|
// 显示内存信息(可以通过Toast或者对话框显示)
|
|
Toast.makeText(this, memoryInfoStr.toString(), Toast.LENGTH_LONG).show();
|
|
|
|
// 同时输出到日志
|
|
LogManager.logInfo(TAG, "内存信息:\n" + memoryInfoStr.toString());
|
|
}
|
|
|
|
/**
|
|
* 获取当前应用内存使用情况的简要信息
|
|
* @return 内存使用情况字符串
|
|
*/
|
|
private String getMemoryUsageSummary() {
|
|
Runtime runtime = Runtime.getRuntime();
|
|
long maxMemory = runtime.maxMemory() / (1024 * 1024); // MB
|
|
long totalMemory = runtime.totalMemory() / (1024 * 1024); // MB
|
|
long freeMemory = runtime.freeMemory() / (1024 * 1024); // MB
|
|
long usedMemory = totalMemory - freeMemory; // MB
|
|
float memoryUsageRatio = (float) usedMemory / maxMemory;
|
|
|
|
return String.format("内存: %d/%d MB (%.1f%%)",
|
|
usedMemory, maxMemory, memoryUsageRatio * 100);
|
|
}
|
|
|
|
/**
|
|
* 获取认证Token(统一认证方式)
|
|
* @return Token字符串
|
|
*/
|
|
private String getAuthToken() {
|
|
return loginDataManager.getAuthToken();
|
|
}
|
|
|
|
/**
|
|
* 查验订单列表
|
|
* 根据modeType和verifyCode等参数获取相应的数据查询参数并发送网络请求
|
|
*/
|
|
private void getCheckOrder() {
|
|
LogManager.logInfo(TAG, "开始查验订单列表,modeType: " + modeType);
|
|
|
|
// 人脸验证模式下不需要暂停摄像头,保持视频流继续运行
|
|
if (modeType != OrderVerificationManager.TYPE_FACE_VERIFICATION) {
|
|
// 非人脸验证模式(验证码、扫码等)才暂停摄像头
|
|
pauseCameraWithTimeout();
|
|
}
|
|
|
|
// 使用新的网络请求管理器执行验证
|
|
orderVerificationManager.performVerification(modeType, verifyCode, null);
|
|
}
|
|
|
|
/**
|
|
* 处理扫码按钮点击事件
|
|
*/
|
|
private void handleScanQRClick() {
|
|
LogManager.logInfo(TAG, "用户点击扫码按钮");
|
|
|
|
// 调用华为统一扫码功能
|
|
com.ouxuan.oxface.device.HuaWeiScanManager.doScan(this, new com.ouxuan.oxface.device.HuaWeiScanManager.ScanResultListener() {
|
|
@Override
|
|
public void onScanSuccess(String scanResult) {
|
|
// 输出扫码结果到log
|
|
LogManager.logInfo(TAG, "华为扫码成功,结果: " + scanResult);
|
|
Log.d(TAG, "扫码结果: " + scanResult);
|
|
|
|
// 显示扫码结果Toast
|
|
runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
Toast.makeText(OXFaceOnlineActivity.this, "扫码成功: " + scanResult, Toast.LENGTH_SHORT).show();
|
|
|
|
// 调用checkOrder函数进行扫码核销
|
|
performScanVerification();
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void onScanFailed(int errorCode, String errorMsg) {
|
|
LogManager.logError(TAG, "华为扫码失败,错误码: " + errorCode + ", 错误信息: " + errorMsg);
|
|
Log.e(TAG, "扫码失败,错误码: " + errorCode + ", 错误信息: " + errorMsg);
|
|
|
|
runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
Toast.makeText(OXFaceOnlineActivity.this, "扫码失败: " + errorMsg, Toast.LENGTH_SHORT).show();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 处理扫码器开门点击事件
|
|
*/
|
|
private void handleScannerDoorClick() {
|
|
LogManager.logInfo(TAG, "用户点击扫码器开门");
|
|
|
|
// 检查是否有正在进行的人脸识别请求
|
|
if (orderVerificationManager != null && orderVerificationManager.isRequestInProgress()) {
|
|
LogManager.logInfo(TAG, "有正在进行的订单验证请求,取消扫码操作");
|
|
Toast.makeText(this, "正在处理其他请求,请稍后再试", Toast.LENGTH_SHORT).show();
|
|
return;
|
|
}
|
|
|
|
// 暂停摄像头预览
|
|
pauseCameraWithTimeout();
|
|
LogManager.logInfo(TAG, "已暂停摄像头预览");
|
|
|
|
// 调用华为统一扫码功能
|
|
com.ouxuan.oxface.device.HuaWeiScanManager.doScan(this, new com.ouxuan.oxface.device.HuaWeiScanManager.ScanResultListener() {
|
|
@Override
|
|
public void onScanSuccess(String scanResult) {
|
|
LogManager.logInfo(TAG, "华为扫码器扫码成功,结果: " + scanResult);
|
|
|
|
runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
Toast.makeText(OXFaceOnlineActivity.this, "扫码成功: " + scanResult, Toast.LENGTH_SHORT).show();
|
|
|
|
// 设置模式为4(扫码器验证)
|
|
modeType = OrderVerificationManager.TYPE_SCANNER_VERIFICATION;
|
|
verifyCode = scanResult;
|
|
|
|
// 执行扫码器核销(注意:getCheckOrder会再次暂停摄像头,所以我们不需要在这里恢复)
|
|
getCheckOrder();
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void onScanFailed(int errorCode, String errorMsg) {
|
|
LogManager.logError(TAG, "华为扫码器扫码失败,错误码: " + errorCode + ", 错误信息: " + errorMsg);
|
|
|
|
// 恢复摄像头预览
|
|
resumeCamera();
|
|
LogManager.logInfo(TAG, "扫码失败,已恢复摄像头预览");
|
|
|
|
runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
Toast.makeText(OXFaceOnlineActivity.this, "扫码失败: " + errorMsg, Toast.LENGTH_SHORT).show();
|
|
|
|
// 显示状态提示
|
|
if (layoutCompareStatus != null) {
|
|
layoutCompareStatus.setVisibility(View.VISIBLE);
|
|
textCompareStatus.setTextColor(Color.parseColor("#FF0000"));
|
|
textCompareStatus.setText("扫码失败: " + errorMsg);
|
|
|
|
// 3秒后隐藏状态提示
|
|
new Handler().postDelayed(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (layoutCompareStatus != null) {
|
|
layoutCompareStatus.setVisibility(View.GONE);
|
|
}
|
|
}
|
|
}, 3000);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 执行扫码核销
|
|
*/
|
|
private void performScanVerification() {
|
|
// 设置模式为3(扫码验证)
|
|
modeType = OrderVerificationManager.TYPE_SCAN_VERIFICATION;
|
|
|
|
// 调用订单查询方法进行扫码验证
|
|
getCheckOrder();
|
|
}
|
|
|
|
/**
|
|
* 显示加载状态
|
|
*/
|
|
private void showLoadingStatus(String message) {
|
|
runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (layoutCompareStatus != null) {
|
|
layoutCompareStatus.setVisibility(View.VISIBLE);
|
|
textCompareStatus.setTextColor(Color.parseColor("#009874"));
|
|
textCompareStatus.setText(message);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 隐藏加载状态
|
|
*/
|
|
private void hideLoadingStatus() {
|
|
runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (layoutCompareStatus != null) {
|
|
layoutCompareStatus.setVisibility(View.GONE);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 显示Toast消息
|
|
*/
|
|
private void showToast(String message) {
|
|
runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
Toast.makeText(OXFaceOnlineActivity.this, message, Toast.LENGTH_SHORT).show();
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 初始化摄像头控制广播接收器
|
|
*/
|
|
private void initCameraControlReceiver() {
|
|
cameraControlReceiver = new CameraControlReceiver();
|
|
IntentFilter filter = new IntentFilter();
|
|
filter.addAction("com.ouxuan.oxface.ACTION_PAUSE_CAMERA");
|
|
filter.addAction("com.ouxuan.oxface.ACTION_RESUME_CAMERA");
|
|
registerReceiver(cameraControlReceiver, filter);
|
|
LogManager.logInfo(TAG, "摄像头控制广播接收器已注册");
|
|
}
|
|
|
|
/**
|
|
* 摄像头控制广播接收器
|
|
*/
|
|
private class CameraControlReceiver extends BroadcastReceiver {
|
|
@Override
|
|
public void onReceive(Context context, Intent intent) {
|
|
String action = intent.getAction();
|
|
if ("com.ouxuan.oxface.ACTION_PAUSE_CAMERA".equals(action)) {
|
|
// 暂停摄像头预览
|
|
pauseCameraWithTimeout();
|
|
LogManager.logInfo(TAG, "接收到暂停摄像头预览广播");
|
|
} else if ("com.ouxuan.oxface.ACTION_RESUME_CAMERA".equals(action)) {
|
|
// 恢复摄像头预览
|
|
resumeCamera();
|
|
LogManager.logInfo(TAG, "接收到恢复摄像头预览广播");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 暂停摄像头并设置超时恢复
|
|
*/
|
|
private void pauseCameraWithTimeout() {
|
|
pauseRequestCount++;
|
|
isCameraPausedByDialog = true;
|
|
isNeedCamera = false;
|
|
|
|
// 取消之前的超时任务
|
|
if (cameraTimeoutHandler != null) {
|
|
cameraTimeoutHandler.removeCallbacksAndMessages(null);
|
|
}
|
|
|
|
// 设置新的超时任务
|
|
cameraTimeoutHandler = new Handler();
|
|
cameraTimeoutHandler.postDelayed(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
LogManager.logWarning(TAG, "摄像头暂停超时,自动恢复并关闭所有对话框");
|
|
forceResumeAndCloseAllDialogs();
|
|
}
|
|
}, CAMERA_RESUME_TIMEOUT);
|
|
|
|
LogManager.logInfo(TAG, "摄像头已暂停,请求计数: " + pauseRequestCount + ", 超时时间: " + CAMERA_RESUME_TIMEOUT + "ms");
|
|
}
|
|
|
|
/**
|
|
* 恢复摄像头
|
|
*/
|
|
private void resumeCamera() {
|
|
if (pauseRequestCount > 0) {
|
|
pauseRequestCount--;
|
|
}
|
|
|
|
// 只有当所有暂停请求都结束时才恢复
|
|
if (pauseRequestCount <= 0) {
|
|
isCameraPausedByDialog = false;
|
|
isNeedCamera = true;
|
|
pauseRequestCount = 0; // 确保不为负数
|
|
|
|
// 取消超时任务
|
|
if (cameraTimeoutHandler != null) {
|
|
cameraTimeoutHandler.removeCallbacksAndMessages(null);
|
|
cameraTimeoutHandler = null;
|
|
}
|
|
|
|
LogManager.logInfo(TAG, "摄像头已恢复");
|
|
} else {
|
|
LogManager.logInfo(TAG, "仍有暂停请求,暂不恢复摄像头,剩余请求数: " + pauseRequestCount);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 执行离场校验
|
|
* @param faceBase64 人脸base64数据
|
|
*/
|
|
private void performLeaveVerification(String faceBase64) {
|
|
LogManager.logInfo(TAG, "开始执行离场校验");
|
|
|
|
// 离场校验使用人脸识别,不需要暂停摄像头,保持视频流继续运行
|
|
|
|
// 调用离场校验管理器执行人脸离场校验
|
|
if (leaveVerificationManager != null) {
|
|
leaveVerificationManager.performFaceLeaveVerification(faceBase64);
|
|
} else {
|
|
LogManager.logError(TAG, "离场校验管理器未初始化");
|
|
showToast("离场校验服务未初始化");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 强制恢复摄像头并关闭所有对话框(超时后调用)
|
|
*/
|
|
private void forceResumeAndCloseAllDialogs() {
|
|
// 强制恢复摄像头
|
|
isCameraPausedByDialog = false;
|
|
isNeedCamera = true;
|
|
pauseRequestCount = 0;
|
|
|
|
// 取消超时任务
|
|
if (cameraTimeoutHandler != null) {
|
|
cameraTimeoutHandler.removeCallbacksAndMessages(null);
|
|
cameraTimeoutHandler = null;
|
|
}
|
|
|
|
// 关闭所有可能的对话框(发送广播通知所有Activity关闭)
|
|
try {
|
|
Intent intent = new Intent("com.ouxuan.oxface.ACTION_FORCE_CLOSE_DIALOGS");
|
|
sendBroadcast(intent);
|
|
LogManager.logInfo(TAG, "已发送强制关闭对话框广播");
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "发送强制关闭对话框广播失败", e);
|
|
}
|
|
|
|
LogManager.logWarning(TAG, "摄像头已强制恢复,所有对话框已关闭");
|
|
}
|
|
|
|
/**
|
|
* 检查网络是否可用
|
|
* @return true 如果网络可用,false 否则
|
|
*/
|
|
private boolean isNetworkAvailable() {
|
|
try {
|
|
// 使用NetworkUtils检查网络连接状态
|
|
if (!com.blankj.utilcode.util.NetworkUtils.isConnected()) {
|
|
LogManager.logWarning(TAG, "网络未连接");
|
|
return false;
|
|
}
|
|
|
|
// 进一步检查网络质量(可选)
|
|
if (networkStatusIndicator != null) {
|
|
NetworkStatusIndicator.NetworkStatus currentStatus = networkStatusIndicator.getCurrentNetworkStatus();
|
|
if (currentStatus == NetworkStatusIndicator.NetworkStatus.POOR) {
|
|
LogManager.logWarning(TAG, "网络状态不佳:" + currentStatus.getDescription());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
LogManager.logDebug(TAG, "网络状态正常");
|
|
return true;
|
|
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "检查网络状态失败", e);
|
|
return false;
|
|
}
|
|
}
|
|
}
|