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.
3756 lines
168 KiB
3756 lines
168 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 java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
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.device.DeviceUtils;
|
|
import com.ouxuan.oxface.device.OxPadBar;
|
|
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.network.api.PadApiService;
|
|
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 com.ouxuan.oxface.device.voice.VoicePlayerManager;
|
|
import com.ouxuan.oxface.device.voice.VoiceType;
|
|
|
|
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 final int PREFER_WIDTH = 1280;
|
|
// private static final int PERFER_HEIGH = 720;
|
|
|
|
// 新增控制变量
|
|
private static int PROCESS_FRAME_INTERVAL = 2; // 快速处理频率,每隔3帧处理一次
|
|
private int frameCounter = 0;
|
|
private boolean needSendFaceImage = false; // 是否需要将人脸图片转为base64发送
|
|
private long lastProcessTime = 0;
|
|
private long lastBackgroundProcessTime = 0; // 应用在后台时的最后处理时间
|
|
private static long MIN_PROCESS_INTERVAL = 300; // 快速处理间隔300ms
|
|
|
|
// 智能调节处理频率的控制变量
|
|
private static final int FAST_PROCESS_FRAME_INTERVAL = 2; // 快速处理频率:每3帧处理一次
|
|
private static final long FAST_MIN_PROCESS_INTERVAL = 300; // 快速处理间隔:300ms
|
|
private static final int SLOW_PROCESS_FRAME_INTERVAL = 10; // 慢速处理频率:每30帧处理一次
|
|
private static final long SLOW_MIN_PROCESS_INTERVAL = 2000; // 慢速处理间隔:3000ms
|
|
private static final long FACE_DETECTION_TIMEOUT = 8000; // 人脸检测超时时间:5秒
|
|
|
|
private long lastFaceDetectedTime = 0; // 上次检测到人脸的时间
|
|
private boolean isFaceDetected = false; // 当前是否检测到人脸
|
|
private int consecutiveNoFaceCount = 0; // 连续未检测到人脸的次数
|
|
private static final int CONSECUTIVE_NO_FACE_THRESHOLD = 20; // 连续未检测到人脸的阈值
|
|
|
|
// FPS统计相关变量
|
|
private long lastFpsCheckTime = 0; // 上次检查FPS的时间
|
|
private int frameCountInSecond = 0; // 每秒内的帧数计数
|
|
private float currentFps = 0; // 当前FPS值
|
|
|
|
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 TextView tvScanDoorText; // 扫码开门文本
|
|
private Button btnVerificationCode;
|
|
private Button btnScannerDoor;
|
|
|
|
// 新增店铺名称相关变量
|
|
private TextView tvStoreName;
|
|
private int storeNameClickCount = 0;
|
|
private long lastStoreNameClickTime = 0;
|
|
private static final int MAX_CLICK_COUNT = 1; // 修改为6次
|
|
private static final long CLICK_INTERVAL = 1000; // 1秒内点击有效
|
|
|
|
// 新增解锁密码弹窗相关变量
|
|
private com.ouxuan.oxface.device.UnlockPasswordDialog unlockPasswordDialog;
|
|
|
|
// 新增订单查验相关变量
|
|
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; // 60秒超时
|
|
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门检测是否开启
|
|
|
|
// 新增语音播放管理器
|
|
private VoicePlayerManager voicePlayerManager;
|
|
|
|
// 动态人数检测相关变量
|
|
private boolean isDynamicPeopleDetectionEnabled = false; // 是否开启动态人数检测
|
|
private boolean isDynamicPeopleDetectionRunning = false; // 动态检测是否正在运行
|
|
private List<Integer> peopleDetectionResults = new ArrayList<>(); // 检测结果数组
|
|
private int gateLoopCountPeopleTimes = 0; // 防尾随记次
|
|
private Handler dynamicDetectionHandler = new Handler(Looper.getMainLooper()); // 动态检测Handler
|
|
private Runnable dynamicDetectionRunnable; // 动态检测任务
|
|
private static final int MAX_DETECTION_COUNT = 100; // 最大检测次数
|
|
private int currentDetectionCount = 0; // 当前检测次数
|
|
private boolean hasPlayedPeopleAnomalyVoice = false; // 是否已经播放过人数异常语音提醒
|
|
private boolean hasShownPeopleAnomalyDialog = false; // 是否已经显示过人数异常弹窗
|
|
|
|
// 方案二:添加状态标识控制订单选择页面的显示,避免重复弹出
|
|
private static boolean isOrderSelectionActivityShowing = false; // 订单选择页面是否正在显示
|
|
private static boolean isOrderVerificationResultActivityShowing = false; // 订单核销结果页面是否正在显示
|
|
public static boolean isVerificationCodeActivityShowing = false; // 验证码页面是否正在显示
|
|
|
|
// 设备类型相关变量
|
|
private int deviceType = -1; // 设备类型:5表示第6批,6表示第7批,7表示第8批等
|
|
@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开始");
|
|
|
|
// 方案二:初始化订单选择页面显示状态
|
|
isOrderSelectionActivityShowing = false;
|
|
|
|
mContext = this;
|
|
// 初始化LoginDataManager
|
|
loginDataManager = com.ouxuan.oxface.data.LoginDataManager.getInstance(this);
|
|
|
|
// 获取设备类型
|
|
deviceType = DeviceSelectDataManager.getInstance(this).getDeviceType();
|
|
Log.d(TAG, "OXFaceOnlineActivity初始化设备类型: " + deviceType);
|
|
|
|
// 初始化网络请求管理器
|
|
initNetworkManagers();
|
|
|
|
// 初始化离场校验管理器
|
|
initLeaveVerificationManager();
|
|
|
|
// 初始化摄像头控制广播接收器
|
|
initCameraControlReceiver();
|
|
|
|
// 初始化解锁密码弹窗
|
|
initUnlockPasswordDialog();
|
|
|
|
// 初始化语音播放管理器
|
|
initVoicePlayerManager();
|
|
|
|
initView();
|
|
|
|
// 初始化网络状态指示器
|
|
initNetworkStatusIndicator();
|
|
|
|
// 初始化AB门禁管理和不可用弹窗
|
|
initGateUnavailableDialog();
|
|
|
|
initHideStateBar();
|
|
|
|
// 初始化人脸检测状态
|
|
lastFaceDetectedTime = System.currentTimeMillis();
|
|
isFaceDetected = false;
|
|
consecutiveNoFaceCount = 0;
|
|
|
|
// 初始化处理频率为快速模式,避免启动时的延迟
|
|
PROCESS_FRAME_INTERVAL = FAST_PROCESS_FRAME_INTERVAL;
|
|
MIN_PROCESS_INTERVAL = FAST_MIN_PROCESS_INTERVAL;
|
|
LogManager.logInfo(TAG, "初始化处理频率:每" + PROCESS_FRAME_INTERVAL + "帧,最小间隔" + MIN_PROCESS_INTERVAL + "ms");
|
|
|
|
// 检查并请求相机权限
|
|
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 initHideStateBar(){
|
|
// 隐藏状态栏和导航栏
|
|
OxPadBar.hidePadBar(this);
|
|
}
|
|
//解锁状态栏
|
|
private void releaseHideStateBar(){
|
|
// 显示状态栏和导航栏
|
|
OxPadBar.showPadBar(this);
|
|
}
|
|
|
|
/**
|
|
* 初始化离场校验管理器
|
|
*/
|
|
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() {
|
|
// 离场校验完成后不立即恢复摄像头,等待结果页面关闭后再恢复
|
|
// 因为即将跳转到核销结果页面,需要保持摄像头暂停状态
|
|
LogManager.logInfo(TAG, "离场校验完成,等待结果页面关闭后恢复摄像头");
|
|
}
|
|
|
|
@Override
|
|
public void showToast(String message) {
|
|
OXFaceOnlineActivity.this.showToast(message);
|
|
}
|
|
|
|
@Override
|
|
public void showLeaveVerificationResultPage(int errorCode, String errorMessage, boolean isSuccess, PadApiService.CheckLeaveResult result, int verificationType) {
|
|
// 跳转到离场校验结果页面
|
|
runOnUiThread(() -> {
|
|
try {
|
|
Intent intent = new Intent(OXFaceOnlineActivity.this, OrderVerificationResultActivity.class);
|
|
|
|
// 设置验证类型
|
|
intent.putExtra("verification_type", verificationType);
|
|
|
|
// 设置结果状态
|
|
if (isSuccess) {
|
|
intent.putExtra("status", "离场成功");
|
|
// 修复:使用result.getMessage()而不是errorMessage来显示服务端返回的完整消息
|
|
intent.putExtra("message", result != null && result.getMessage() != null ? result.getMessage() : "离场成功");
|
|
} else {
|
|
intent.putExtra("status", "离场失败");
|
|
intent.putExtra("message", errorMessage != null ? errorMessage : "离场失败");
|
|
}
|
|
|
|
// 设置错误代码
|
|
intent.putExtra("error_code", errorCode);
|
|
|
|
// 如果有结果数据,传入相关信息
|
|
if (result != null) {
|
|
// CheckLeaveResult只有message和data属性,没有orderNo和project
|
|
// 使用默认值
|
|
intent.putExtra("order_no", "");
|
|
intent.putExtra("project", "离场验证");
|
|
} else {
|
|
intent.putExtra("order_no", "");
|
|
intent.putExtra("project", "离场验证");
|
|
}
|
|
|
|
// 设置默认值
|
|
intent.putExtra("order_type", 0); // 默认订单类型
|
|
intent.putExtra("verification_code", "");
|
|
intent.putExtra("card_no", "");
|
|
|
|
LogManager.logInfo(TAG, "跳转到离场校验结果页面,状态: " + (isSuccess ? "成功" : "失败") + ", 错误代码: " + errorCode + ", 验证类型: " + verificationType);
|
|
startActivity(intent);
|
|
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "跳转到离场校验结果页面失败", e);
|
|
// 如果跳转失败,回退到显示toast的方式
|
|
showToast(errorMessage != null ? errorMessage : (isSuccess ? "离场成功" : "离场失败"));
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
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) {
|
|
// 人脸识别开始,停止动态人数检测
|
|
onFaceRecognitionStarted();
|
|
// 直接处理页面跳转逻辑,不在这里开门
|
|
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) {
|
|
// 方案二:检查订单选择页面是否已经显示,避免重复弹出
|
|
if (isOrderSelectionActivityShowing) {
|
|
LogManager.logInfo(TAG, "订单选择页面已经在显示中,避免重复启动");
|
|
return;
|
|
}
|
|
|
|
// 设置标识为true,表示订单选择页面即将显示
|
|
isOrderSelectionActivityShowing = true;
|
|
|
|
// 在跳转到订单选择页面时暂停摄像头
|
|
// pauseCameraWithTimeout();
|
|
pauseCamera();
|
|
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
|
|
}
|
|
|
|
@Override
|
|
public void pauseCamera() {
|
|
OXFaceOnlineActivity.this.pauseCamera();
|
|
}
|
|
|
|
@Override
|
|
public void resumeCamera() {
|
|
OXFaceOnlineActivity.this.resumeCamera();
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
tvScanDoorText = findViewById(R.id.tv_scan_door_text); // 初始化扫码开门文本
|
|
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 updateButtonVisibility() {
|
|
try {
|
|
// 使用VenueSceneUtils工具类获取按钮显示配置
|
|
com.ouxuan.oxface.utils.VenueSceneUtils.PadConfigButtonVisibility buttonVisibility =
|
|
com.ouxuan.oxface.utils.VenueSceneUtils.getPadConfigButtonVisibility(this);
|
|
|
|
// 控制开门按钮显示/隐藏
|
|
// data.pad_config.is_show_gate_btn == 1 显示开门按钮
|
|
if (btnOpenDoor != null) {
|
|
btnOpenDoor.setVisibility(buttonVisibility.isShowGateBtn ? View.VISIBLE : View.GONE);
|
|
LogManager.logInfo(TAG, "开门按钮显示状态更新: " + (buttonVisibility.isShowGateBtn ? "显示" : "隐藏"));
|
|
}
|
|
|
|
// 控制验证码开门按钮显示/隐藏
|
|
// data.pad_config.is_code_verify == 1 开启验证码验证
|
|
if (btnVerificationCode != null) {
|
|
btnVerificationCode.setVisibility(buttonVisibility.isCodeVerify ? View.VISIBLE : View.GONE);
|
|
LogManager.logInfo(TAG, "验证码开门按钮显示状态更新: " + (buttonVisibility.isCodeVerify ? "显示" : "隐藏"));
|
|
}
|
|
|
|
// 控制扫码器开门按钮显示/隐藏
|
|
// data.pad_config.is_scan_code_verify == 1 扫码器验证
|
|
if (btnScannerDoor != null) {
|
|
btnScannerDoor.setVisibility(buttonVisibility.isScanCodeVerify ? View.VISIBLE : View.GONE);
|
|
LogManager.logInfo(TAG, "扫码器开门按钮显示状态更新: " + (buttonVisibility.isScanCodeVerify ? "显示" : "隐藏"));
|
|
}
|
|
|
|
// 控制扫码开门按钮显示/隐藏
|
|
// data.pad_config.is_scan_verify == 1 扫码开门验证
|
|
if (imgScanDoorQRCode != null) {
|
|
imgScanDoorQRCode.setVisibility(buttonVisibility.isScanVerify ? View.VISIBLE : View.GONE);
|
|
LogManager.logInfo(TAG, "扫码开门按钮显示状态更新: " + (buttonVisibility.isScanVerify ? "显示" : "隐藏"));
|
|
}
|
|
|
|
// 控制扫码开门文本显示/隐藏(与扫码开门按钮一致)
|
|
if (tvScanDoorText != null) {
|
|
tvScanDoorText.setVisibility(buttonVisibility.isScanVerify ? View.VISIBLE : View.GONE);
|
|
LogManager.logInfo(TAG, "扫码开门文本显示状态更新: " + (buttonVisibility.isScanVerify ? "显示" : "隐藏"));
|
|
}
|
|
|
|
// 新用户扫码上传人脸按钮始终显示
|
|
if (imgMiniProgramCode != null) {
|
|
imgMiniProgramCode.setVisibility(View.VISIBLE);
|
|
LogManager.logInfo(TAG, "新用户扫码上传人脸按钮始终显示");
|
|
}
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "更新按钮显示状态时发生异常", e);
|
|
// 异常情况下显示所有按钮和文本
|
|
if (btnOpenDoor != null) btnOpenDoor.setVisibility(View.VISIBLE);
|
|
if (btnVerificationCode != null) btnVerificationCode.setVisibility(View.VISIBLE);
|
|
if (btnScannerDoor != null) btnScannerDoor.setVisibility(View.VISIBLE);
|
|
if (imgScanDoorQRCode != null) imgScanDoorQRCode.setVisibility(View.VISIBLE);
|
|
if (tvScanDoorText != null) tvScanDoorText.setVisibility(View.VISIBLE);
|
|
if (imgMiniProgramCode != null) imgMiniProgramCode.setVisibility(View.VISIBLE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 初始化网络状态指示器
|
|
*/
|
|
private void initNetworkStatusIndicator() {
|
|
try {
|
|
networkStatusIndicator = new NetworkStatusIndicator(this, relativeLayout);
|
|
|
|
// 设置网络恢复监听器
|
|
networkStatusIndicator.setNetworkRecoveryListener(new NetworkStatusIndicator.NetworkRecoveryListener() {
|
|
@Override
|
|
public void onNetworkRecovered() {
|
|
LogManager.logInfo(TAG, "接收到网络恢复通知,恢复摄像头预览");
|
|
// 网络恢复时恢复摄像头预览
|
|
resumeCamera();
|
|
|
|
// 检查是否有门禁不可用弹窗显示,如果有则关闭它
|
|
if (gateUnavailableDialog != null) {
|
|
try {
|
|
// 使用反射调用isShowing方法检查弹窗是否显示
|
|
java.lang.reflect.Method isShowingMethod = gateUnavailableDialog.getClass().getMethod("isShowing");
|
|
boolean isShowing = (Boolean) isShowingMethod.invoke(gateUnavailableDialog);
|
|
|
|
if (isShowing) {
|
|
LogManager.logInfo(TAG, "检测到门禁不可用弹窗正在显示,网络恢复后自动关闭弹窗");
|
|
// 使用反射调用hide方法关闭弹窗
|
|
java.lang.reflect.Method hideMethod = gateUnavailableDialog.getClass().getMethod("hide");
|
|
hideMethod.invoke(gateUnavailableDialog);
|
|
LogManager.logInfo(TAG, "门禁不可用弹窗已因网络恢复而关闭");
|
|
} else {
|
|
LogManager.logDebug(TAG, "门禁不可用弹窗未显示,无需关闭");
|
|
}
|
|
} catch (Exception e) {
|
|
LogManager.logWarning(TAG, "检查或关闭门禁不可用弹窗失败: " + e.getMessage());
|
|
}
|
|
}
|
|
}
|
|
|
|
@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);
|
|
|
|
// 初始化门禁不可用弹窗
|
|
gateUnavailableDialog = new GateUnavailableDialog(this);
|
|
|
|
// 检查是否开启AB门检测
|
|
checkGateCheckEnabled();
|
|
|
|
// 只有当AB门检测开启时才设置门状态监听器(触发时机1)
|
|
if (isGateCheckEnabled) {
|
|
// 设置门状态监听器(触发时机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);
|
|
|
|
// 检查门禁不可用弹窗是否已初始化
|
|
if (gateUnavailableDialog == null) {
|
|
LogManager.logError(TAG, "门禁不可用弹窗未初始化,无法显示");
|
|
return;
|
|
}
|
|
|
|
// 检查是否应该显示弹窗
|
|
boolean shouldShow = gateUnavailableDialog.checkShow(OXFaceOnlineActivity.this, isNetworkAvailable());
|
|
if (shouldShow) {
|
|
// 获取当前门状态信息
|
|
GateABController.GateABState currentState = gateABController.getCurrentGateState();
|
|
// 如果已有弹窗存在,更新内容(带门状态信息);如果没有,显示新弹窗
|
|
gateUnavailableDialog.updateGateStateError(reason,
|
|
currentState.gateAOpen,
|
|
currentState.gateBOpen,
|
|
currentState.udpConnected);
|
|
|
|
pauseCamera();
|
|
LogManager.logInfo(TAG, " pauseCamera 已更新门禁不可用弹窗内容: " + reason);
|
|
} else {
|
|
LogManager.logInfo(TAG, "根据业务规则,不显示门禁不可用弹窗");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onGateAvailable() {
|
|
LogManager.logInfo(TAG, "门禁恢复可用,隐藏弹窗并恢复摄像头预览");
|
|
|
|
// 检查门禁不可用弹窗是否已初始化
|
|
if (gateUnavailableDialog == null) {
|
|
LogManager.logWarning(TAG, "门禁不可用弹窗未初始化,无法隐藏");
|
|
// 直接恢复摄像头预览
|
|
resumeCamera();
|
|
return;
|
|
}
|
|
|
|
// 隐藏门禁不可用弹窗
|
|
if (gateUnavailableDialog != null && gateUnavailableDialog.isShowing()) {
|
|
gateUnavailableDialog.hide();
|
|
}
|
|
|
|
// 恢复摄像头预览(与弹窗的onDialogHide回调保持一致)
|
|
resumeCamera();
|
|
}
|
|
|
|
@Override
|
|
public void onGateStatusUpdate(boolean gateAOpen, boolean gateBOpen, boolean udpConnected) {
|
|
// 检查门禁不可用弹窗是否已初始化
|
|
if (gateUnavailableDialog == null) {
|
|
return;
|
|
}
|
|
|
|
// 实时更新弹窗中的门状态显示
|
|
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();
|
|
// }
|
|
}
|
|
});
|
|
} else {
|
|
LogManager.logInfo(TAG, "AB门检测已关闭,跳过设置门状态监听器(触发时机1)");
|
|
}
|
|
|
|
// 设置门状态变化回调(用于动态人数检测)
|
|
gateABController.setGateStateChangeCallback(new GateABController.GateStateChangeCallback() {
|
|
@Override
|
|
public void onGateClosed() {
|
|
// 门关闭时开始动态人数检测 - 直接调用方法避免递归
|
|
if (!isDynamicPeopleDetectionRunning) {
|
|
LogManager.logInfo(TAG, "检测到门关闭,开始动态人数检测");
|
|
startDynamicPeopleDetection();
|
|
} else {
|
|
LogManager.logInfo(TAG, "动态人数检测已在运行,跳过门关闭触发");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onGateOpened() {
|
|
// 门开启时的处理(暂时不需要)
|
|
LogManager.logInfo(TAG, "门开启事件触发");
|
|
}
|
|
});
|
|
|
|
// 设置弹窗操作监听器
|
|
gateUnavailableDialog.setDialogListener(new GateUnavailableDialog.GateUnavailableDialogListener() {
|
|
@Override
|
|
public void onDialogShow() {
|
|
// 增强日志:记录当前场景信息
|
|
boolean isCurrentLeaveScene = false;
|
|
try {
|
|
isCurrentLeaveScene = com.ouxuan.oxface.utils.VenueSceneUtils.isLeaveScene(OXFaceOnlineActivity.this);
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "获取场景信息失败", e);
|
|
}
|
|
|
|
String sceneInfo = isCurrentLeaveScene ? "离场场景" : "进场场景";
|
|
LogManager.logInfo(TAG, "门禁不可用弹窗显示,暂停摄像头和中断操作 - " + sceneInfo);
|
|
|
|
// 暂停摄像头和中断其他操作
|
|
pauseCamera();
|
|
|
|
LogManager.logInfo(TAG, "摄像头暂停完成 - " + sceneInfo);
|
|
}
|
|
|
|
@Override
|
|
public void onDialogHide() {
|
|
// 增强日志:记录当前场景信息
|
|
boolean isCurrentLeaveScene = false;
|
|
try {
|
|
isCurrentLeaveScene = com.ouxuan.oxface.utils.VenueSceneUtils.isLeaveScene(OXFaceOnlineActivity.this);
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "获取场景信息失败", e);
|
|
}
|
|
|
|
String sceneInfo = isCurrentLeaveScene ? "离场场景" : "进场场景";
|
|
LogManager.logInfo(TAG, "门禁不可用弹窗隐藏,恢复摄像头和操作 - " + sceneInfo);
|
|
|
|
// 恢复摄像头和操作
|
|
resumeCamera();
|
|
|
|
LogManager.logInfo(TAG, "摄像头恢复完成 - " + sceneInfo);
|
|
}
|
|
|
|
@Override
|
|
public void onDialogClosed() {
|
|
LogManager.logInfo(TAG, "门禁不可用弹窗关闭,恢复正常门禁监控");
|
|
// 人数异常弹窗关闭后,恢复正常的门禁监控状态
|
|
try {
|
|
if (gateABController != null) {
|
|
// 修复关键问题:无论UDP是否初始化,都需要重新初始化来重置状态缓存
|
|
LogManager.logInfo(TAG, "重新初始化UDP门禁控制以重置状态缓存");
|
|
gateABController.reinitializeUDP();
|
|
|
|
// 稍等片刻后启动轮询,确保初始化完成
|
|
new Handler(Looper.getMainLooper()).postDelayed(() -> {
|
|
if (gateABController != null) {
|
|
gateABController.startUDPPolling();
|
|
LogManager.logInfo(TAG, "UDP门禁状态轮询已重新启动,状态缓存已重置");
|
|
}
|
|
}, 500);
|
|
|
|
LogManager.logInfo(TAG, "门禁监控已恢复,当前门状态监听器正常运行");
|
|
}
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "恢复UDP门禁监控失败", e);
|
|
}
|
|
}
|
|
});
|
|
|
|
// 设置解锁密码弹窗触发器
|
|
gateUnavailableDialog.setUnlockTrigger(new GateUnavailableDialog.UnlockPasswordDialogTrigger() {
|
|
@Override
|
|
public void triggerUnlockPasswordDialog() {
|
|
LogManager.logInfo(TAG, "门禁不可用弹窗标题触发解锁密码弹窗");
|
|
showUnlockPasswordDialog();
|
|
}
|
|
});
|
|
|
|
LogManager.logInfo(TAG, "AB门禁管理和不可用弹窗初始化完成");
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "AB门禁管理和不可用弹窗初始化失败", e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 显示新的门禁不可用弹窗
|
|
* @param reason 不可用原因
|
|
*/
|
|
private void showNewGateUnavailableDialog(String reason) {
|
|
try {
|
|
// 检查门禁不可用弹窗是否已初始化
|
|
if (gateUnavailableDialog == null) {
|
|
LogManager.logError(TAG, "门禁不可用弹窗未初始化,无法显示: " + reason);
|
|
return;
|
|
}
|
|
|
|
// 执行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 {
|
|
// 检查门禁不可用弹窗是否已初始化
|
|
if (gateUnavailableDialog == null) {
|
|
LogManager.logError(TAG, "门禁不可用弹窗未初始化,无法显示人数异常弹窗");
|
|
return;
|
|
}
|
|
|
|
// 执行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门检测是否开启
|
|
boolean gateCheckEnabled = false;
|
|
|
|
// 获取门禁配置信息
|
|
com.ouxuan.oxface.utils.VenueSceneUtils.GateConfig gateConfig =
|
|
com.ouxuan.oxface.utils.VenueSceneUtils.getGateConfig(this);
|
|
|
|
if (gateConfig != null) {
|
|
gateCheckEnabled = gateConfig.gateAbEnable;
|
|
LogManager.logInfo(TAG, "从配置获取AB门检测状态: " + (gateCheckEnabled ? "已开启" : "已关闭"));
|
|
} else {
|
|
LogManager.logWarning(TAG, "无法获取门禁配置,AB门检测默认关闭");
|
|
gateCheckEnabled = false;
|
|
}
|
|
|
|
isGateCheckEnabled = gateCheckEnabled;
|
|
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");
|
|
}
|
|
|
|
// 根据后台配置更新按钮显示状态
|
|
updateButtonVisibility();
|
|
|
|
resumeCamera();
|
|
|
|
LogManager.logInfo(TAG, "OXFaceOnlineActivity onResume");
|
|
}
|
|
|
|
private void startTestOpenDebugRegisterFunction() {
|
|
LogManager.logInfo(TAG, "启动摄像头预览");
|
|
try {
|
|
//输出SingleBaseConfig.getBaseConfig()
|
|
Log.d(TAG, "startTestOpenDebugRegisterFunction: 555:"+ SingleBaseConfig.getBaseConfig());
|
|
|
|
// 设置摄像头方向
|
|
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 {
|
|
// Log.d(TAG, "45466: 444:"+isNeedCamera);
|
|
frameCounter++;
|
|
|
|
// // FPS统计逻辑
|
|
// frameCountInSecond++;
|
|
|
|
// // 每秒计算一次FPS
|
|
// if (System.currentTimeMillis() - lastFpsCheckTime >= 1000) {
|
|
// currentFps = frameCountInSecond;
|
|
// LogManager.logInfo(TAG, "摄像头FPS: " + currentFps);
|
|
// frameCountInSecond = 0;
|
|
// lastFpsCheckTime = System.currentTimeMillis();
|
|
// }
|
|
|
|
// 摄像头预览数据进行人脸检测
|
|
if (isNeedCamera) {
|
|
// 增加帧计数器和时间间隔控制
|
|
// frameCounter++;
|
|
long currentTime = System.currentTimeMillis();
|
|
|
|
// 智能调节处理频率
|
|
adjustProcessFrequency(currentTime);
|
|
// Log.d(TAG, "frameCounter45466:"+(frameCounter % PROCESS_FRAME_INTERVAL == 0));
|
|
// 只有当满足处理条件时才进行人脸检测
|
|
if (frameCounter % PROCESS_FRAME_INTERVAL == 0) {
|
|
|
|
// if(true){
|
|
// Log.d(TAG, "满足条件: 0000000:"+isNeedCamera+"|"+needSendFaceImage);
|
|
lastProcessTime = currentTime;
|
|
// Log.d(TAG, "onGetCameraData: 666:"+FaceSDKManager.initStatus+"帧数阈值:"+SingleBaseConfig.getBaseConfig().getFramesThreshold());
|
|
// 检查应用是否处于前台
|
|
// if (!isApplicationInForeground()) {
|
|
//
|
|
// // 如果应用在后台,减少处理频率,每隔10秒处理一次
|
|
// if ((currentTime - lastBackgroundProcessTime) < 10000) {
|
|
// return;
|
|
// }
|
|
// lastBackgroundProcessTime = currentTime;
|
|
// }
|
|
// Log.d(TAG, "isApplicationInForeground: 777:"+isApplicationInForeground());
|
|
|
|
FaceSDKManager.getInstance().onDetectCheck(data, null, null,
|
|
height, width, 1, new FaceDetectCallBack() {
|
|
@Override
|
|
public void onFaceDetectCallback(LivenessModel livenessModel) {
|
|
|
|
Log.d(TAG, "onGetCameraData: 获取到人脸:"+livenessModel.getTrackFaceInfo().length+"|height:"+height+"| width:"+width);
|
|
try {
|
|
// 检查是否需要进行人脸检测
|
|
if (!isNeedCheckFace()) {
|
|
LogManager.logDebug(TAG, "当前有弹窗覆盖,跳过人脸检测处理");
|
|
layoutCompareStatus.setVisibility(View.GONE);//有弹窗时移除提示
|
|
showFrame(null);
|
|
return;
|
|
}
|
|
|
|
// 更新人脸检测状态
|
|
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);
|
|
// 绘制人脸框
|
|
if (!mAutoCameraPreviewView.isDraw) {
|
|
showFrame(livenessModel);
|
|
}
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "人脸框绘制回调处理异常", e);
|
|
}
|
|
}
|
|
});
|
|
} else if (frameCounter % 30 == 0 ) {
|
|
Log.e(TAG, "每30帧清空一次画布: 11111:");
|
|
// 对于跳过处理的帧,每30帧清空一次画布,确保无人脸时画面干净
|
|
try {
|
|
LivenessModel emptyModel = new LivenessModel();
|
|
emptyModel.setTrackFaceInfo(null);
|
|
showFrame(emptyModel);
|
|
layoutCompareStatus.setVisibility(View.GONE);
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "清空画布时发生异常", e);
|
|
}
|
|
}
|
|
}else{
|
|
// Log.e(TAG, "不满足条件: 0000000:");
|
|
showFrame(null);
|
|
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; // 重置连续未检测到人脸计数器
|
|
|
|
Log.e(TAG, "检测到人脸updateFaceDetectionStatus: ");
|
|
LogManager.logDebug(TAG, "检测到人脸");
|
|
} else {
|
|
// 未检测到人脸
|
|
consecutiveNoFaceCount++;
|
|
|
|
// 如果连续多次未检测到人脸,标记为未检测到人脸状态
|
|
if (consecutiveNoFaceCount >= CONSECUTIVE_NO_FACE_THRESHOLD) {
|
|
isFaceDetected = false;
|
|
LogManager.logDebug(TAG, "连续未检测到人脸,当前计数: " + consecutiveNoFaceCount);
|
|
Log.e(TAG, "连续未检测到人脸:当前计数 "+consecutiveNoFaceCount);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 绘制人脸框
|
|
*/
|
|
private void showFrame(final LivenessModel model) {
|
|
runOnUiThread(new Runnable() {
|
|
|
|
@Override
|
|
public void run() {
|
|
// Log.d(TAG, "showFrame888:"+model.toString());
|
|
Canvas canvas = mDrawDetectFaceView.lockCanvas();
|
|
if (canvas == null) {
|
|
mDrawDetectFaceView.unlockCanvasAndPost(canvas);
|
|
return;
|
|
}
|
|
|
|
// 始终清空canvas
|
|
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
|
|
|
|
// 如果没有模型数据,直接返回清空的画布
|
|
if (model == null) {
|
|
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
|
|
mDrawDetectFaceView.unlockCanvasAndPost(canvas);
|
|
return;
|
|
}
|
|
|
|
// 获取人脸信息
|
|
FaceInfo[] faceInfos = model.getTrackFaceInfo();
|
|
|
|
// 如果没有检测到人脸,直接返回清空的画布
|
|
if (faceInfos == null || faceInfos.length == 0) {
|
|
// 清空canvas
|
|
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
|
|
mDrawDetectFaceView.unlockCanvasAndPost(canvas);
|
|
return;
|
|
}
|
|
|
|
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
|
|
// 只在检测到人脸时绘制人脸框
|
|
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());
|
|
|
|
// 根据设备类型进行人脸框坐标适配
|
|
if (deviceType == 6) {
|
|
// 第7批设备需要180度旋转适配
|
|
float screenWidth = mAutoCameraPreviewView.getPreviewWidth();
|
|
float screenHeight = mAutoCameraPreviewView.getPreviewHeight();
|
|
float tempLeft = rectF.left;
|
|
float tempTop = rectF.top;
|
|
rectF.left = screenWidth - rectF.right;
|
|
rectF.right = screenWidth - tempLeft;
|
|
rectF.top = screenHeight - rectF.bottom;
|
|
rectF.bottom = screenHeight - tempTop;
|
|
Log.d(TAG, "第7批设备人脸框坐标已进行180度旋转适配");
|
|
}
|
|
if((rectF.right-rectF.left)-(mAutoCameraPreviewView.getPreviewWidth()/3)<0){
|
|
Log.d(TAG, "人像过小,不绘制");
|
|
continue;
|
|
}
|
|
|
|
|
|
|
|
// 人脸框颜色
|
|
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();
|
|
Toast.makeText(this, "请将核销码对准屏幕下方的扫码器", Toast.LENGTH_SHORT).show();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 处理扫码按钮点击事件
|
|
*/
|
|
// 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, "用户点击验证码开门");
|
|
|
|
// 使用新的暂停摄像头方法
|
|
pauseCamera();
|
|
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();
|
|
|
|
releaseHideStateBar();
|
|
|
|
// 释放语音播放器资源
|
|
if (voicePlayerManager != null) {
|
|
voicePlayerManager.release();
|
|
voicePlayerManager = null;
|
|
LogManager.logInfo(TAG, "语音播放器资源已释放");
|
|
}
|
|
|
|
// 销毁网络状态指示器
|
|
if (networkStatusIndicator != null) {
|
|
networkStatusIndicator.destroy();
|
|
networkStatusIndicator = null;
|
|
}
|
|
|
|
// 释放AB门禁管理和不可用弹窗资源
|
|
if (gateUnavailableDialog != null) {
|
|
gateUnavailableDialog.release();
|
|
gateUnavailableDialog = null;
|
|
LogManager.logInfo(TAG, "门禁不可用弹窗资源已释放");
|
|
}
|
|
|
|
// 注意:GateABController是全局单例,不应该在这里释放
|
|
// 只需要将引用置为null,避免内存泄漏
|
|
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 (unlockPasswordDialog != null) {
|
|
unlockPasswordDialog.release();
|
|
unlockPasswordDialog = null;
|
|
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));
|
|
|
|
layoutCompareStatus.setVisibility(View.VISIBLE);
|
|
textCompareStatus.setTextColor(Color.parseColor("#fec133"));
|
|
textCompareStatus.setText("未检测到人脸");
|
|
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.getFaceInfo().centerY>500){
|
|
layoutCompareStatus.setVisibility(View.VISIBLE);
|
|
textCompareStatus.setTextColor(Color.parseColor("#fec133"));
|
|
textCompareStatus.setText("请移动人脸置于屏幕中");
|
|
return;
|
|
}
|
|
|
|
//通过检测
|
|
if (livenessModel != null) {
|
|
layoutCompareStatus.setVisibility(View.VISIBLE);
|
|
textCompareStatus.setTextColor(Color.parseColor("#009874"));
|
|
textCompareStatus.setText("人脸订单检测中");
|
|
Log.d(TAG, "人脸订单检测: "+livenessModel.getFaceInfo().score+" | "+livenessModel.getFaceInfo().bestImageScore+ " | "+livenessModel.getFaceInfo().centerX+" | "+livenessModel.getFaceInfo().centerY);
|
|
checkResultOnline(livenessModel);//在线人脸检测
|
|
} else {
|
|
currentLivenessModel = null;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
private void checkResultOnline(LivenessModel livenessModel) {
|
|
this.currentLivenessModel = livenessModel;
|
|
|
|
|
|
if(!isNeedCamera){
|
|
LogManager.logInfo(TAG, "摄像头暂停,停止接收livenessModel");
|
|
return;
|
|
}
|
|
// 判断是进场还是离场场景
|
|
if (VenueSceneUtils.isLeaveScene(OXFaceOnlineActivity.this)) {
|
|
// 离场场景:检查gate_open_enable配置(0922沟通后确认使用gate_open_enable字段)
|
|
VenueSceneUtils.GateConfig gateConfig = VenueSceneUtils.getGateConfig(OXFaceOnlineActivity.this);
|
|
// 修改为判断gateOpenEnable字段,而不是gateEnterOpenEnable
|
|
if (gateConfig != null && gateConfig.gateOpenEnable) {
|
|
// 如果 gate_open_enable 为 true,直接开门不进行网络核销
|
|
LogManager.logInfo(TAG, "检测到离场场景,gate_open_enable为true,直接开启B门");
|
|
showLoadingStatus("门已打开,请直接离场");
|
|
|
|
// 直接开启B门
|
|
if (gateABController != null) {
|
|
LogManager.logInfo(TAG, "gate_open_enable为true,直接开启B门");
|
|
gateABController.handleFaceRecognitionSuccess(false); // 参数保留兼容性,实际都开B门
|
|
|
|
}
|
|
pauseCamera();
|
|
// 5秒后隐藏提示并恢复摄像头
|
|
new Handler().postDelayed(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
hideLoadingStatus();
|
|
resumeCamera();
|
|
}
|
|
}, 5000);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
// 离场网络中断检测逻辑:检测(当前是离场&&当前网络中断){直接执行openGateAB,而不再继续执行后续逻辑}
|
|
try {
|
|
boolean isLeaveScene = VenueSceneUtils.isLeaveScene(this);
|
|
boolean isNetworkAvailable = isNetworkAvailable();
|
|
|
|
if (isLeaveScene && !isNetworkAvailable) {
|
|
LogManager.logInfo(TAG, "检测到离场场景且网络中断,直接执行openGateAB开门");
|
|
|
|
// 直接执行AB门开门操作
|
|
if (gateABController != null) {
|
|
|
|
// 暂停摄像头预览10秒后再恢复,避免频繁执行开门操作
|
|
pauseCamera();
|
|
LogManager.logInfo(TAG, "暂停摄像头预览10秒,避免频繁执行开门操作");
|
|
|
|
// 10秒后恢复摄像头预览
|
|
new Handler().postDelayed(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
resumeCamera();
|
|
LogManager.logInfo(TAG, "10秒后恢复摄像头预览");
|
|
}
|
|
}, 10000);
|
|
|
|
gateABController.openGateAB(new GateABController.GateControlCallback() {
|
|
@Override
|
|
public void onSuccess(String message) {
|
|
LogManager.logInfo(TAG, "离场网络中断直接开门成功: " + message);
|
|
|
|
// 显示简单的成功提示
|
|
runOnUiThread(() -> {
|
|
showLoadingStatus("开门成功,网络异常");
|
|
|
|
// 5秒后隐藏提示
|
|
new Handler().postDelayed(() -> {
|
|
hideLoadingStatus();
|
|
}, 5000);
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void onError(String errorMessage) {
|
|
LogManager.logError(TAG, "离场网络中断直接开门失败: " + errorMessage);
|
|
|
|
// 显示错误提示
|
|
runOnUiThread(() -> {
|
|
showLoadingStatus("开门失败: " + errorMessage);
|
|
|
|
// 5秒后隐藏提示
|
|
new Handler().postDelayed(() -> {
|
|
hideLoadingStatus();
|
|
}, 5000);
|
|
});
|
|
}
|
|
});
|
|
} else {
|
|
LogManager.logError(TAG, "gateABController为空,无法执行开门操作");
|
|
}
|
|
|
|
// 直接返回,不再继续执行后续逻辑
|
|
return;
|
|
}
|
|
|
|
LogManager.logInfo(TAG, "继续执行正常的人脸识别流程 - 场景: " + (isLeaveScene ? "离场" : "进场") + ", 网络: " + (isNetworkAvailable ? "可用" : "不可用"));
|
|
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "离场检测异常,继续正常流程", e);
|
|
}
|
|
|
|
// 当未检测到人脸UI显示
|
|
runOnUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (livenessModel == null) {
|
|
LogManager.logError(TAG, "livenessModel为空");
|
|
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 {
|
|
// 执行人数检测
|
|
LogManager.logInfo(TAG, "准备调用ABPeopleCheck方法");
|
|
boolean peopleCheckPassed = abGateManager.ABPeopleCheck();
|
|
LogManager.logInfo(TAG, "ABPeopleCheck方法返回结果: " + peopleCheckPassed);
|
|
// peopleCheckPassed = true; //TODO test参数,跳过人脸验证
|
|
if (!peopleCheckPassed) {
|
|
LogManager.logWarning(TAG, "人数检测未通过,准备显示弹窗");
|
|
// 人数检测未通过,显示弹窗
|
|
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);
|
|
showToast("获取人数失败: " + errorMessage);
|
|
// 直接更新弹窗内容,不需要检查重复弹窗
|
|
// showNewGateUnavailableDialog("门禁内人数不符合要求,请等待");
|
|
}
|
|
});
|
|
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "处理人数检测结果异常", e);
|
|
}
|
|
});
|
|
|
|
// 人数检测未通过,中断后续的人脸识别流程
|
|
LogManager.logInfo(TAG, "AB门人数检测未通过,中断后续人脸识别流程");
|
|
return;
|
|
} else {
|
|
LogManager.logInfo(TAG, "AB门人数棈测通过,继续人脸识别流程");
|
|
|
|
// AB门检查通过后,开始动态人数检测(防止尾随进入)
|
|
if (checkDynamicPeopleDetectionEnabled()) {
|
|
LogManager.logInfo(TAG, "AB门检查通过,开始动态人数检测");
|
|
startDynamicPeopleDetection();
|
|
} else {
|
|
LogManager.logInfo(TAG, "动态人数检测功能未开启");
|
|
}
|
|
}
|
|
|
|
} 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数据");
|
|
|
|
// 重置连续未检测到人脸计数器
|
|
consecutiveNoFaceCount = 0;
|
|
|
|
// 保存人脸base64数据到verifyCode变量
|
|
verifyCode = base64img;
|
|
|
|
// 设置模式为2(人脸验证)
|
|
modeType = OrderVerificationManager.TYPE_FACE_VERIFICATION;
|
|
|
|
// 判断是进场还是离场场景
|
|
if (VenueSceneUtils.isLeaveScene(OXFaceOnlineActivity.this)) {
|
|
// 离场场景:检查gate_open_enable配置(0922沟通后确认使用gate_open_enable字段)
|
|
VenueSceneUtils.GateConfig gateConfig = VenueSceneUtils.getGateConfig(OXFaceOnlineActivity.this);
|
|
// 修改为判断gateOpenEnable字段,而不是gateEnterOpenEnable
|
|
if (gateConfig != null && gateConfig.gateOpenEnable) {
|
|
// 如果 gate_open_enable 为 true,直接开门不进行网络核销,该逻辑已转移
|
|
} else {
|
|
// gate_open_enable 为 false 或配置不存在,按正常流程进行网络校验
|
|
if (isNetworkAvailable()) {
|
|
LogManager.logInfo(TAG, "检测到离场场景,网络可用,执行离场校验");
|
|
|
|
// 检查是否已有正在进行的离场验证请求,避免重复请求
|
|
if (leaveVerificationManager != null && leaveVerificationManager.isRequestInProgress()) {
|
|
LogManager.logInfo(TAG, "已有正在进行的离场验证请求,跳过本次请求");
|
|
return;
|
|
}
|
|
|
|
performLeaveVerification(base64img);
|
|
}
|
|
else {
|
|
LogManager.logWarning(TAG, "检测到离场场景,但网络不可用,直接开启B门");
|
|
showLoadingStatus("网络不可用,直接开门");
|
|
|
|
// 直接开启B门,防止用户卡在场内
|
|
if (gateABController != null) {
|
|
LogManager.logInfo(TAG, "离场场景网络不可用,直接开启B门");
|
|
// 检查GateABController是否已经被释放
|
|
try {
|
|
gateABController.handleFaceRecognitionSuccess(false); // 开启B门
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "调用GateABController.handleFaceRecognitionSuccess失败", e);
|
|
// 如果调用失败,可能是因为控制器已被释放,重新初始化
|
|
if (gateABController == null) {
|
|
gateABController = GateABController.getInstance();
|
|
gateABController.initialize(OXFaceOnlineActivity.this);
|
|
gateABController.handleFaceRecognitionSuccess(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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();
|
|
|
|
// 成功获取到人脸base64数据时,重置连续未检测到人脸计数器
|
|
consecutiveNoFaceCount = 0;
|
|
|
|
return bitmap_str;
|
|
} else {
|
|
// 如果不需要发送,则不进行base64转换,直接释放资源
|
|
image.destory();
|
|
if (bitmap != null && !bitmap.isRecycled()) {
|
|
bitmap.recycle();
|
|
}
|
|
Log.d(TAG, "getFaceImageBase64: 释放base64");
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
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);
|
|
|
|
// 如果连续点击达到6次,触发解锁密码弹窗
|
|
if (storeNameClickCount >= MAX_CLICK_COUNT) {
|
|
LogManager.logInfo(TAG, "连续点击6次店铺名称,触发解锁密码弹窗");
|
|
|
|
// 重置计数器
|
|
storeNameClickCount = 0;
|
|
|
|
// 显示解锁密码弹窗
|
|
showUnlockPasswordDialog();
|
|
} else if (storeNameClickCount >= 6) {
|
|
// 当点击次数达到3次时,提示用户还需几次点击
|
|
int remainingClicks = MAX_CLICK_COUNT - storeNameClickCount;
|
|
Toast.makeText(this, "请输入登录密码解锁", Toast.LENGTH_SHORT).show();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 初始化解锁密码弹窗
|
|
*/
|
|
private void initUnlockPasswordDialog() {
|
|
unlockPasswordDialog = new com.ouxuan.oxface.device.UnlockPasswordDialog(this);
|
|
unlockPasswordDialog.setListener(new com.ouxuan.oxface.device.UnlockPasswordDialog.UnlockDialogListener() {
|
|
@Override
|
|
public void onUnlockSuccess() {
|
|
LogManager.logInfo(TAG, "解锁成功,准备退出人脸识别");
|
|
unLockAndLeaveFaceDetect();
|
|
}
|
|
|
|
@Override
|
|
public void onDialogShow() {
|
|
// 暂停摄像头预览
|
|
pauseCamera();
|
|
LogManager.logInfo(TAG, "解锁密码弹窗显示,摄像头已暂停");
|
|
}
|
|
|
|
@Override
|
|
public void onDialogDismiss() {
|
|
// 恢复摄像头预览
|
|
resumeCamera();
|
|
LogManager.logInfo(TAG, "解锁密码弹窗关闭,摄像头已恢复");
|
|
}
|
|
});
|
|
|
|
LogManager.logInfo(TAG, "解锁密码弹窗初始化完成");
|
|
}
|
|
|
|
/**
|
|
* 显示解锁密码弹窗
|
|
*/
|
|
private void showUnlockPasswordDialog() {
|
|
if (unlockPasswordDialog != null && !unlockPasswordDialog.isShowing()) {
|
|
unlockPasswordDialog.show();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 解锁并退出人脸识别界面
|
|
*/
|
|
private void unLockAndLeaveFaceDetect() {
|
|
LogManager.logInfo(TAG, "执行解锁退出操作");
|
|
|
|
try {
|
|
// 停止摄像头预览
|
|
// if (CameraPreviewManager.getInstance() != null) {
|
|
// CameraPreviewManager.getInstance().stopPreview();
|
|
// }
|
|
finish();
|
|
|
|
// 返回登录界面,添加标志表示是从解锁离开返回的
|
|
Intent intent = new Intent(this, MainActivity.class);
|
|
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
intent.putExtra("from_unlock_leave", true); // 添加标志
|
|
startActivity(intent);
|
|
|
|
// 关闭当前界面
|
|
|
|
|
|
LogManager.logInfo(TAG, "已退出人脸识别界面,返回登录界面");
|
|
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "解锁退出操作失败", e);
|
|
Toast.makeText(this, "退出失败,请重试", Toast.LENGTH_SHORT).show();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 处理验证码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方法");
|
|
|
|
// 方案二:重置订单选择页面显示状态
|
|
isOrderSelectionActivityShowing = false;
|
|
|
|
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;
|
|
|
|
// 判断当前场景
|
|
boolean isLeaveScene = VenueSceneUtils.isLeaveScene(this);
|
|
|
|
if (isLeaveScene) {
|
|
// 离场场景:调用离场验证码核销逻辑
|
|
LogManager.logInfo(TAG, "离场场景验证码验证,验证码: " + verificationCode);
|
|
leaveVerificationManager.performVerification(LeaveVerificationManager.TYPE_VERIFICATION_CODE, verificationCode, null);
|
|
} else {
|
|
// 进场场景:调用订单查询方法进行验证码验证
|
|
LogManager.logInfo(TAG, "进场场景验证码验证,验证码: " + 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();
|
|
}
|
|
|
|
/**
|
|
* 初始化语音播放管理器
|
|
*/
|
|
private void initVoicePlayerManager() {
|
|
try {
|
|
voicePlayerManager = VoicePlayerManager.getInstance();
|
|
voicePlayerManager.initialize(this);
|
|
|
|
LogManager.logInfo(TAG, "语音播放管理器初始化完成");
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "语音播放管理器初始化失败", e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 查验订单列表
|
|
* 根据modeType和verifyCode等参数获取相应的数据查询参数并发送网络请求
|
|
*/
|
|
private void getCheckOrder() {
|
|
LogManager.logInfo(TAG, "开始查验订单列表,modeType: " + modeType);
|
|
|
|
// 播放正在进行人脸识别语音
|
|
if (voicePlayerManager != null) {
|
|
voicePlayerManager.playVoice(VoiceType.FACE_RECOGNITION);
|
|
}
|
|
|
|
// 人脸验证模式下不需要暂停摄像头,保持视频流继续运行
|
|
// if (modeType != OrderVerificationManager.TYPE_FACE_VERIFICATION) {
|
|
// // 非人脸验证模式(验证码、扫码等)才暂停摄像头
|
|
// pauseCameraWithTimeout();
|
|
// }
|
|
|
|
pauseCamera(); //全部模式下都需要进行摄像头暂停
|
|
|
|
// 使用新的网络请求管理器执行验证
|
|
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 handleOpenDoorClick() {
|
|
LogManager.logInfo(TAG, "用户点击开门按钮");
|
|
|
|
// 检查GateABController是否已初始化
|
|
if (gateABController == null) {
|
|
LogManager.logError(TAG, "GateABController未初始化,无法执行开门操作");
|
|
Toast.makeText(this, "门禁控制器未初始化", Toast.LENGTH_SHORT).show();
|
|
return;
|
|
}
|
|
|
|
// 显示开门状态
|
|
if (layoutCompareStatus != null) {
|
|
layoutCompareStatus.setVisibility(View.VISIBLE);
|
|
textCompareStatus.setTextColor(Color.parseColor("#009874"));
|
|
textCompareStatus.setText("正在开门...");
|
|
}
|
|
|
|
// 执行AB门开门操作
|
|
// 检查GateABController是否已经被释放
|
|
try {
|
|
gateABController.openGateAB(new GateABController.GateControlCallback() {
|
|
@Override
|
|
public void onSuccess(String message) {
|
|
LogManager.logInfo(TAG, "开门成功: " + message);
|
|
|
|
runOnUiThread(() -> {
|
|
Toast.makeText(OXFaceOnlineActivity.this, "开门成功: " + message, Toast.LENGTH_SHORT).show();
|
|
|
|
// 更新UI状态
|
|
if (layoutCompareStatus != null) {
|
|
textCompareStatus.setText("开门成功");
|
|
textCompareStatus.setTextColor(Color.parseColor("#009874"));
|
|
|
|
// 3秒后隐藏状态提示
|
|
new Handler().postDelayed(() -> {
|
|
layoutCompareStatus.setVisibility(View.GONE);
|
|
}, 3000);
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void onError(String errorMessage) {
|
|
LogManager.logError(TAG, "开门失败: " + errorMessage);
|
|
|
|
runOnUiThread(() -> {
|
|
Toast.makeText(OXFaceOnlineActivity.this, "开门失败: " + errorMessage, Toast.LENGTH_SHORT).show();
|
|
|
|
// 更新UI状态
|
|
if (layoutCompareStatus != null) {
|
|
textCompareStatus.setText("开门失败: " + errorMessage);
|
|
textCompareStatus.setTextColor(Color.RED);
|
|
|
|
// 3秒后隐藏状态提示
|
|
new Handler().postDelayed(() -> {
|
|
layoutCompareStatus.setVisibility(View.GONE);
|
|
}, 3000);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "调用GateABController.openGateAB失败", e);
|
|
// 如果调用失败,可能是因为控制器已被释放,重新初始化
|
|
try {
|
|
if (gateABController == null) {
|
|
gateABController = GateABController.getInstance();
|
|
gateABController.initialize(OXFaceOnlineActivity.this);
|
|
}
|
|
gateABController.openGateAB(new GateABController.GateControlCallback() {
|
|
@Override
|
|
public void onSuccess(String message) {
|
|
LogManager.logInfo(TAG, "开门成功: " + message);
|
|
|
|
runOnUiThread(() -> {
|
|
Toast.makeText(OXFaceOnlineActivity.this, "开门成功: " + message, Toast.LENGTH_SHORT).show();
|
|
|
|
// 更新UI状态
|
|
if (layoutCompareStatus != null) {
|
|
textCompareStatus.setText("开门成功");
|
|
textCompareStatus.setTextColor(Color.parseColor("#009874"));
|
|
|
|
// 3秒后隐藏状态提示
|
|
new Handler().postDelayed(() -> {
|
|
layoutCompareStatus.setVisibility(View.GONE);
|
|
}, 3000);
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void onError(String errorMessage) {
|
|
LogManager.logError(TAG, "开门失败: " + errorMessage);
|
|
|
|
runOnUiThread(() -> {
|
|
Toast.makeText(OXFaceOnlineActivity.this, "开门失败: " + errorMessage, Toast.LENGTH_SHORT).show();
|
|
|
|
// 更新UI状态
|
|
if (layoutCompareStatus != null) {
|
|
textCompareStatus.setText("开门失败: " + errorMessage);
|
|
textCompareStatus.setTextColor(Color.RED);
|
|
|
|
// 3秒后隐藏状态提示
|
|
new Handler().postDelayed(() -> {
|
|
layoutCompareStatus.setVisibility(View.GONE);
|
|
}, 3000);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
} catch (Exception reInitException) {
|
|
LogManager.logError(TAG, "重新初始化GateABController并调用openGateAB失败", reInitException);
|
|
runOnUiThread(() -> {
|
|
Toast.makeText(OXFaceOnlineActivity.this, "开门失败: 控制器初始化失败", Toast.LENGTH_SHORT).show();
|
|
|
|
// 更新UI状态
|
|
if (layoutCompareStatus != null) {
|
|
textCompareStatus.setText("开门失败: 控制器初始化失败");
|
|
textCompareStatus.setTextColor(Color.RED);
|
|
|
|
// 3秒后隐藏状态提示
|
|
new Handler().postDelayed(() -> {
|
|
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");
|
|
filter.addAction("com.ouxuan.oxface.ACTION_RESET_ORDER_SELECTION_STATUS"); // 添加新的广播动作
|
|
filter.addAction("com.ouxuan.oxface.ACTION_ORDER_VERIFICATION_RESULT_SHOWING"); // 订单核销结果页面正在显示
|
|
filter.addAction("com.ouxuan.oxface.ACTION_ORDER_VERIFICATION_RESULT_HIDDEN"); // 订单核销结果页面已隐藏
|
|
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)) {
|
|
// 暂停摄像头预览
|
|
pauseCamera();
|
|
LogManager.logInfo(TAG, "接收到暂停摄像头预览广播");
|
|
} else if ("com.ouxuan.oxface.ACTION_RESUME_CAMERA".equals(action)) {
|
|
// 检查是否有订单核销结果页面正在显示,如果有则不恢复摄像头预览
|
|
if (isOrderVerificationResultActivityShowing) {
|
|
LogManager.logInfo(TAG, "订单核销结果页面正在显示,不恢复摄像头预览");
|
|
return;
|
|
}
|
|
|
|
// 恢复摄像头预览
|
|
resumeCamera();
|
|
LogManager.logInfo(TAG, "接收到恢复摄像头预览广播");
|
|
} else if ("com.ouxuan.oxface.ACTION_RESET_ORDER_SELECTION_STATUS".equals(action)) {
|
|
// 方案二:重置订单选择页面显示状态
|
|
isOrderSelectionActivityShowing = false;
|
|
LogManager.logInfo(TAG, "接收到重置订单选择页面显示状态广播");
|
|
} else if ("com.ouxuan.oxface.ACTION_ORDER_VERIFICATION_RESULT_SHOWING".equals(action)) {
|
|
// 订单核销结果页面正在显示
|
|
pauseCamera();
|
|
isOrderVerificationResultActivityShowing = true;
|
|
LogManager.logInfo(TAG, "接收到订单核销结果页面正在显示广播");
|
|
} else if ("com.ouxuan.oxface.ACTION_ORDER_VERIFICATION_RESULT_HIDDEN".equals(action)) {
|
|
// 订单核销结果页面已隐藏
|
|
isOrderVerificationResultActivityShowing = false;
|
|
LogManager.logInfo(TAG, "接收到订单核销结果页面已隐藏广播");
|
|
|
|
// 恢复摄像头预览
|
|
resumeCamera();
|
|
LogManager.logInfo(TAG, "订单核销结果页面已隐藏,恢复摄像头预览");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 暂停摄像头
|
|
*/
|
|
private void pauseCamera() {
|
|
pauseRequestCount++;
|
|
isCameraPausedByDialog = true;
|
|
isNeedCamera = false;
|
|
|
|
// 取消之前的超时任务
|
|
if (cameraTimeoutHandler != null) {
|
|
cameraTimeoutHandler.removeCallbacksAndMessages(null);
|
|
}
|
|
|
|
// 清空一次画布,确保暂停时画面干净
|
|
try {
|
|
LivenessModel emptyModel = new LivenessModel();
|
|
emptyModel.setTrackFaceInfo(null);
|
|
showFrame(emptyModel);
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "清空画布时发生异常", e);
|
|
}
|
|
|
|
// 设置新的超时任务
|
|
// cameraTimeoutHandler = new Handler();
|
|
// cameraTimeoutHandler.postDelayed(new Runnable() {
|
|
// @Override
|
|
// public void run() {
|
|
// LogManager.logWarning(TAG, "摄像头暂停超时,自动恢复并关闭所有对话框1111111");
|
|
// forceResumeAndCloseAllDialogs();
|
|
// }
|
|
// }, 10000);
|
|
|
|
LogManager.logInfo(TAG, "摄像头已暂停,请求计数: " + pauseRequestCount + ", 超时时间: " + "ms");
|
|
}
|
|
/**
|
|
* 暂停摄像头并设置超时恢复
|
|
*/
|
|
private void pauseCameraWithTimeout() {
|
|
pauseRequestCount++;
|
|
isCameraPausedByDialog = true;
|
|
isNeedCamera = false;
|
|
|
|
// 取消之前的超时任务
|
|
if (cameraTimeoutHandler != null) {
|
|
cameraTimeoutHandler.removeCallbacksAndMessages(null);
|
|
}
|
|
|
|
// 清空一次画布,确保暂停时画面干净
|
|
try {
|
|
LivenessModel emptyModel = new LivenessModel();
|
|
emptyModel.setTrackFaceInfo(null);
|
|
showFrame(emptyModel);
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "清空画布时发生异常", e);
|
|
}
|
|
// 设置新的超时任务
|
|
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--;
|
|
}
|
|
|
|
pauseRequestCount = 0; //恢复摄像头
|
|
// 只有当所有暂停请求都结束时才恢复
|
|
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) {
|
|
// 2. 播放"正在进行人脸识别"语音
|
|
if (voicePlayerManager != null) {
|
|
voicePlayerManager.playVoice(VoiceType.FACE_RECOGNITION);
|
|
}
|
|
leaveVerificationManager.performVerification(LeaveVerificationManager.TYPE_FACE_VERIFICATION, faceBase64, null);
|
|
} 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);
|
|
}
|
|
|
|
// 方案二:重置订单选择页面显示状态
|
|
isOrderSelectionActivityShowing = false;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 检查是否开启动态人数检测功能
|
|
*/
|
|
private boolean checkDynamicPeopleDetectionEnabled() {
|
|
try {
|
|
VenueSceneUtils.GateConfig gateConfig = VenueSceneUtils.getGateConfig(this);
|
|
return gateConfig != null && gateConfig.gateCameraDetectHardLevel;
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "检查动态人数检测开关失败", e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 开始动态人数检测
|
|
*/
|
|
private void startDynamicPeopleDetection() {
|
|
if (!checkDynamicPeopleDetectionEnabled()) {
|
|
LogManager.logInfo(TAG, "动态人数检测功能未开启");
|
|
return;
|
|
}
|
|
|
|
if (isDynamicPeopleDetectionRunning) {
|
|
LogManager.logInfo(TAG, "动态人数检测已在运行中");
|
|
return;
|
|
}
|
|
|
|
LogManager.logInfo(TAG, "开始动态人数检测");
|
|
|
|
// 初始化检测状态
|
|
isDynamicPeopleDetectionRunning = true;
|
|
peopleDetectionResults.clear();
|
|
currentDetectionCount = 0;
|
|
gateLoopCountPeopleTimes = 0;
|
|
hasPlayedPeopleAnomalyVoice = false; // 重置语音提醒标志
|
|
hasShownPeopleAnomalyDialog = false; // 重置弹窗显示标志
|
|
|
|
// 创建检测任务
|
|
dynamicDetectionRunnable = new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (!isDynamicPeopleDetectionRunning || currentDetectionCount >= MAX_DETECTION_COUNT) {
|
|
stopDynamicPeopleDetection();
|
|
return;
|
|
}
|
|
|
|
currentDetectionCount++;
|
|
LogManager.logDebug(TAG, "执行动态人数检测,第" + currentDetectionCount + "次");
|
|
|
|
// 获取门内人数
|
|
if (gateABController != null) {
|
|
gateABController.get485PeopleNum(new GateABController.PeopleNumCallback() {
|
|
@Override
|
|
public void onSuccess(int peopleNum) {
|
|
LogManager.logDebug(TAG, "检测到门内人数: " + peopleNum);
|
|
|
|
// 记录检测结果
|
|
peopleDetectionResults.add(peopleNum);
|
|
//输出peopleDetectionResults
|
|
LogManager.logDebug(TAG, "当前人数检测结果列表: " + peopleDetectionResults.toString());
|
|
|
|
// 如果人数>1,增加计数
|
|
if (peopleNum > 1) {
|
|
gateLoopCountPeopleTimes++;
|
|
LogManager.logWarning(TAG, "检测到人数异常,当前计数: " + gateLoopCountPeopleTimes);
|
|
}
|
|
|
|
// 检查是否门内已清空
|
|
if (isGateEmpty(currentDetectionCount, peopleDetectionResults)) {
|
|
LogManager.logInfo(TAG, "检测到门内已清空,停止动态检测");
|
|
stopDynamicPeopleDetection();
|
|
return;
|
|
}
|
|
|
|
// 检查并处理人数异常 - 传入当前检测到的人数
|
|
checkPeopleCountAnomaly(peopleNum);
|
|
|
|
// 计算下次检测间隔
|
|
long nextInterval = calculateNextDetectionInterval(currentDetectionCount);
|
|
|
|
// 安排下次检测
|
|
if (isDynamicPeopleDetectionRunning && currentDetectionCount < MAX_DETECTION_COUNT) {
|
|
dynamicDetectionHandler.postDelayed(dynamicDetectionRunnable, nextInterval);
|
|
} else {
|
|
stopDynamicPeopleDetection();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onError(String errorMessage) {
|
|
LogManager.logError(TAG, "获取门内人数失败: " + errorMessage);
|
|
|
|
// 即使失败也继续检测,但记录0
|
|
peopleDetectionResults.add(0);
|
|
|
|
// 计算下次检测间隔
|
|
long nextInterval = calculateNextDetectionInterval(currentDetectionCount);
|
|
|
|
// 安排下次检测
|
|
if (isDynamicPeopleDetectionRunning && currentDetectionCount < MAX_DETECTION_COUNT) {
|
|
dynamicDetectionHandler.postDelayed(dynamicDetectionRunnable, nextInterval);
|
|
} else {
|
|
stopDynamicPeopleDetection();
|
|
}
|
|
}
|
|
});
|
|
} else {
|
|
LogManager.logError(TAG, "GateABController未初始化,无法进行人数检测");
|
|
stopDynamicPeopleDetection();
|
|
}
|
|
}
|
|
};
|
|
|
|
// 开始第一次检测
|
|
dynamicDetectionHandler.post(dynamicDetectionRunnable);
|
|
}
|
|
|
|
/**
|
|
* 停止动态人数检测
|
|
*/
|
|
private void stopDynamicPeopleDetection() {
|
|
if (!isDynamicPeopleDetectionRunning) {
|
|
return;
|
|
}
|
|
|
|
LogManager.logInfo(TAG, "停止动态人数检测,总检测次数: " + currentDetectionCount + ", 异常计数: " + gateLoopCountPeopleTimes);
|
|
|
|
isDynamicPeopleDetectionRunning = false;
|
|
|
|
// 移除待执行的任务
|
|
if (dynamicDetectionRunnable != null) {
|
|
dynamicDetectionHandler.removeCallbacks(dynamicDetectionRunnable);
|
|
}
|
|
|
|
// 最终检查人数
|
|
checkPeopleCountAnomalyFinal(peopleDetectionResults);
|
|
}
|
|
|
|
/**
|
|
* 计算下次检测间隔时间(毫秒)
|
|
*/
|
|
private long calculateNextDetectionInterval(int currentCount) {
|
|
long interval;
|
|
if (currentCount < 5) {
|
|
interval = 50; // 开始阶段:50ms
|
|
} else if (currentCount < 10) {
|
|
interval = 300; // 5-10次:300ms
|
|
} else if (currentCount < 15) {
|
|
interval = 600; // 10-15次:600ms
|
|
} else if (currentCount < 20) {
|
|
interval = 1000; // 15-20次:1000ms
|
|
} else if (currentCount < 30) {
|
|
interval = 1500; // 20-30次:1500ms
|
|
} else {
|
|
interval = 1500; // 30次以后:1500ms
|
|
}
|
|
return interval;
|
|
}
|
|
|
|
/**
|
|
* 检查门内是否已清空
|
|
*/
|
|
private boolean isGateEmpty(int currentIndex, List<Integer> results) {
|
|
if (currentIndex >= 3 && results.size() >= 3) {
|
|
int recentSum = 0;
|
|
int startIndex = Math.max(0, currentIndex - 3);
|
|
for (int i = startIndex; i <= currentIndex && i < results.size(); i++) {
|
|
recentSum += results.get(i);
|
|
}
|
|
if (recentSum < 1) {
|
|
LogManager.logInfo(TAG, "最近4次检测总人数为0,判断门内已清空");
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* 检查人数异常并播放语音
|
|
*/
|
|
private void checkPeopleCountAnomaly(int currentPeopleCount) {
|
|
if (currentPeopleCount > 1) {
|
|
// 当前检测到人数异常,只在第一次时播放提醒语音和显示弹窗
|
|
if (!hasPlayedPeopleAnomalyVoice) {
|
|
hasPlayedPeopleAnomalyVoice = true;
|
|
hasShownPeopleAnomalyDialog = true;
|
|
playVoiceReminder("010"); // 请离场,确认门内外只有1人后再重新进入AB门
|
|
showMultiplePeopleDetectedDialog(); // 显示人数异常弹窗
|
|
LogManager.logInfo(TAG, "首次检测到人数异常,播放语音提醒并显示弹窗");
|
|
|
|
// 停止动态检测
|
|
stopDynamicPeopleDetection();
|
|
} else {
|
|
LogManager.logDebug(TAG, "检测到人数异常,但已播放过语音提醒,跳过");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 最终检查人数异常
|
|
*/
|
|
private void checkPeopleCountAnomalyFinal(List<Integer> results) {
|
|
if (results.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
// 检查是否所有检测结果都小于2
|
|
boolean allLessThanTwo = true;
|
|
for (int num : results) {
|
|
if (num >= 2) {
|
|
allLessThanTwo = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (allLessThanTwo) {
|
|
// 重置计数器
|
|
gateLoopCountPeopleTimes = 0;
|
|
LogManager.logInfo(TAG, "所有检测结果都正常,重置异常计数器");
|
|
} else {
|
|
// 增加计数器
|
|
gateLoopCountPeopleTimes++;
|
|
LogManager.logWarning(TAG, "检测到异常人数,最终异常计数: " + gateLoopCountPeopleTimes);
|
|
|
|
// 如果异常严重,显示弹窗
|
|
if (gateLoopCountPeopleTimes >= 3) {
|
|
showMultiplePeopleDetectedDialog();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 显示多人检测弹窗
|
|
*/
|
|
private void showMultiplePeopleDetectedDialog() {
|
|
runOnUiThread(() -> {
|
|
try {
|
|
if (gateUnavailableDialog != null) {
|
|
// 显示进场场景人数异常弹窗,使用当前检测到的人数
|
|
int currentPeopleCount = peopleDetectionResults.isEmpty() ? 0 : peopleDetectionResults.get(peopleDetectionResults.size() - 1);
|
|
gateUnavailableDialog.updatePeopleCountError(false, currentPeopleCount);
|
|
LogManager.logWarning(TAG, "显示多人检测异常弹窗,当前人数: " + currentPeopleCount);
|
|
} else {
|
|
LogManager.logError(TAG, "GateUnavailableDialog未初始化,无法显示人数异常弹窗");
|
|
}
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "显示多人检测弹窗失败", e);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 播放语音提醒
|
|
*/
|
|
private void playVoiceReminder(String voiceType) {
|
|
try {
|
|
if (voicePlayerManager != null) {
|
|
// 根据voiceType字符串找到对应的VoiceType枚举
|
|
VoiceType voiceEnum = null;
|
|
switch (voiceType) {
|
|
case "010":
|
|
// 暂时使用ONE_PERSON_ENTRY作为人数异常提醒
|
|
voiceEnum = VoiceType.ONE_PERSON_ENTRY;
|
|
break;
|
|
default:
|
|
LogManager.logWarning(TAG, "未知的语音类型: " + voiceType);
|
|
return;
|
|
}
|
|
|
|
if (voiceEnum != null) {
|
|
voicePlayerManager.playVoice(voiceEnum);
|
|
LogManager.logInfo(TAG, "播放语音提醒: " + voiceType + " -> " + voiceEnum.getDescription());
|
|
}
|
|
} else {
|
|
LogManager.logWarning(TAG, "VoicePlayerManager未初始化,无法播放语音");
|
|
}
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "播放语音提醒失败", e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 当门关闭时触发动态人数检测
|
|
*/
|
|
public void onGateClosed() {
|
|
// 如果动态检测已经在运行(通过AB门检查启动的),则不再重复启动
|
|
if (isDynamicPeopleDetectionRunning) {
|
|
LogManager.logInfo(TAG, "动态人数检测已在运行,跳过门关闭触发");
|
|
return;
|
|
}
|
|
|
|
LogManager.logInfo(TAG, "检测到门关闭,开始动态人数检测");
|
|
startDynamicPeopleDetection();
|
|
}
|
|
|
|
/**
|
|
* 当人脸识别开始时停止动态检测
|
|
*/
|
|
public void onFaceRecognitionStarted() {
|
|
LogManager.logInfo(TAG, "人脸识别开始,停止动态人数检测");
|
|
stopDynamicPeopleDetection();
|
|
}
|
|
|
|
/**
|
|
* 检查是否需要进行人脸检测
|
|
* 当有其他弹窗覆盖在视频流上时,不进行人脸检测
|
|
* @return true表示需要检测,false表示不需要检测
|
|
*/
|
|
private boolean isNeedCheckFace() {
|
|
// 1. 当查询到当前GateUnavailableDialog.java已经弹出时,返回false
|
|
if (gateUnavailableDialog != null && gateUnavailableDialog.isShowing()) {
|
|
return false;
|
|
}
|
|
|
|
// 2. 当查询到当前OrderSelectionActivity.java已经弹出时,返回false
|
|
if (isOrderSelectionActivityShowing) {
|
|
return false;
|
|
}
|
|
|
|
// 3. 当查询到当前OrderVerificationResultActivity.java已经弹出时,返回false
|
|
if (isOrderVerificationResultActivityShowing) {
|
|
return false;
|
|
}
|
|
|
|
// 4. 当查询到当前VerificationCodeActivity.java已经弹出时,返回false
|
|
if (isVerificationCodeActivityShowing) {
|
|
return false;
|
|
}
|
|
|
|
// 5. 当查询到当前UnlockPasswordDialog.java已经弹出时,返回false
|
|
if (unlockPasswordDialog != null && unlockPasswordDialog.isShowing()) {
|
|
return false;
|
|
}
|
|
|
|
// 其它情况返回true
|
|
return true;
|
|
}
|
|
}
|