diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 32446a6..fd36261 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,6 +7,11 @@ + + + + + @@ -103,6 +108,12 @@ android:name=".DebugActivity" android:exported="false" android:theme="@style/Theme.OxFaceLogin" /> + + + \ No newline at end of file diff --git a/app/src/main/java/com/baidu/idl/face/main/finance/utils/TestPopWindow.java b/app/src/main/java/com/baidu/idl/face/main/finance/utils/TestPopWindow.java new file mode 100644 index 0000000..8c7e770 --- /dev/null +++ b/app/src/main/java/com/baidu/idl/face/main/finance/utils/TestPopWindow.java @@ -0,0 +1,81 @@ +package com.baidu.idl.face.main.finance.utils; + +import android.content.Context; +import android.util.Log; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.PopupWindow; + +import com.baidu.idl.main.facesdk.financelibrary.R; + +public class TestPopWindow extends PopupWindow { + private String TAG = "TestPopWindow"; + private final Context gContext; + private View view; + + public TestPopWindow(Context context) { + this(context, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + Log.d(TAG, "TestPopWindow: " + ViewGroup.LayoutParams.WRAP_CONTENT + + "bbb:" + ViewGroup.LayoutParams.WRAP_CONTENT); + } + + public TestPopWindow(Context context, int width, int height) { + super(context); + this.gContext = context; + view = View.inflate(context, R.layout.layout_no_face_detected, null); + view.findViewById(R.id.retest_detectBtn).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + mOnClickFinance.rester(true); + } + }); + view.findViewById(R.id.back_homeBtn).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + mOnClickFinance.rester(false); + } + }); + + setContentView(view); + // 设置窗口的高和宽 + setWidth(width); + setHeight(height); + // 设置弹窗内科点击 + setTouchable(true); + setOutsideTouchable(true); + setFocusable(true); + // TODO去除背景 + setBackgroundDrawable(null); + + } + + /** + * 显示popupWindow + */ + public void showPopupWindow(View parent) { + if (!this.isShowing()) { + // 以下拉方式显示popupwindow,调整位置使其不会完全遮挡预览界面 + this.showAtLocation(parent, Gravity.CENTER, 0, -50); + } else { + this.dismiss(); + } + } + + public void closePopupWindow() { + if (this.isShowing()) { + this.dismiss(); + } + } + + + public void setmOnClickFinance(OnClickFinance mOnClickFinance) { + this.mOnClickFinance = mOnClickFinance; + } + + public OnClickFinance mOnClickFinance; + + public interface OnClickFinance { + void rester(boolean isReTest); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ouxuan/oxface/DebugActivity.java b/app/src/main/java/com/ouxuan/oxface/DebugActivity.java index c27f412..6ab71e0 100644 --- a/app/src/main/java/com/ouxuan/oxface/DebugActivity.java +++ b/app/src/main/java/com/ouxuan/oxface/DebugActivity.java @@ -171,6 +171,15 @@ public class DebugActivity extends Activity { } }); + // 人脸识别测试按钮 + Button btnFaceRecognitionTest = findViewById(R.id.btnFaceRecognitionTest); + btnFaceRecognitionTest.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + startFaceRecognitionTest(); + } + }); + // 关闭按钮 Button btnClose = findViewById(R.id.btnClose); btnClose.setOnClickListener(new View.OnClickListener() { @@ -461,6 +470,23 @@ public class DebugActivity extends Activity { } /** + * 启动人脸识别测试 + */ + private void startFaceRecognitionTest() { + logMessage("启动人脸识别测试..."); + try { + Intent intent = new Intent(this, OXFaceOnlineActivity.class); + startActivity(intent); + logMessage("已启动人脸识别测试界面"); + showToast("已启动人脸识别测试界面"); + } catch (Exception e) { + Log.e(TAG, "启动人脸识别测试失败", e); + logMessage("启动人脸识别测试失败: " + e.getMessage()); + showToast("启动人脸识别测试失败"); + } + } + + /** * 在日志输出区域添加消息 * @param message 要添加的消息 */ diff --git a/app/src/main/java/com/ouxuan/oxface/MainActivity.java b/app/src/main/java/com/ouxuan/oxface/MainActivity.java index a520b84..40c88f7 100644 --- a/app/src/main/java/com/ouxuan/oxface/MainActivity.java +++ b/app/src/main/java/com/ouxuan/oxface/MainActivity.java @@ -59,6 +59,7 @@ public class MainActivity extends AppCompatActivity { private DeviceSelectDataManager deviceSelectDataManager; // 设备选择数据管理器 private KeepAliveManager keepAliveManager; // 保持活跃管理器 private AutoStartManager autoStartManager; // 自启动管理器 + private Dialog currentDialog; // 用于跟踪当前显示的Dialog,防止WindowLeaked错误 @Override protected void onCreate(Bundle savedInstanceState) { @@ -110,6 +111,9 @@ public class MainActivity extends AppCompatActivity { // 设置长按登录按钮显示日志路径信息(调试功能) setupLogPathDebug(); + + //切换第六批设备 + switchCameraByDeviceType(5); } /** @@ -170,14 +174,17 @@ public class MainActivity extends AppCompatActivity { new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { @Override public void run() { - if (loginDataManager.isLoggedIn()) { - showToast("自动登录成功!"); - showLoginSuccessDialog(loginDataManager.getLoginData()); - } else { - android.util.Log.w("MainActivity", "自动登录失败,本地数据可能已失效"); - showToast("自动登录失败,请手动登录"); - // 清除可能已失效的登录数据 - loginDataManager.clearLoginData(); + // 检查Activity是否仍然有效 + if (!isFinishing() && !isDestroyed()) { + if (loginDataManager.isLoggedIn()) { + showToast("自动登录成功!"); + showLoginSuccessDialog(loginDataManager.getLoginData()); + } else { + android.util.Log.w("MainActivity", "自动登录失败,本地数据可能已失效"); + showToast("自动登录失败,请手动登录"); + // 清除可能已失效的登录数据 + loginDataManager.clearLoginData(); + } } } }, 1000); // 延迟1秒 @@ -305,10 +312,12 @@ public class MainActivity extends AppCompatActivity { // 打印存储的data android.util.Log.d("MainActivity", "Login Data: " + data.toString()); - - // 显示登录成功弹框,传入登录数据 - showToast("登录成功!"); - showLoginSuccessDialog(data); + // 检查Activity是否仍然有效再显示Dialog + if (!isFinishing() && !isDestroyed()) { + // 显示登录成功弹框,传入登录数据 + showToast("登录成功!"); + showLoginSuccessDialog(data); + } } @Override @@ -384,6 +393,7 @@ public class MainActivity extends AppCompatActivity { private void showLoginSuccessDialog(PadApiService.PadLoginResponse loginData) { // 创建自定义弹框 Dialog dialog = new Dialog(this); + currentDialog = dialog; // 保存Dialog引用以防止WindowLeaked错误 dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); dialog.setContentView(R.layout.dialog_login_success); @@ -437,6 +447,7 @@ public class MainActivity extends AppCompatActivity { @Override public void onClick(View v) { dialog.dismiss(); + currentDialog = null; // 清除引用 } }); @@ -496,6 +507,7 @@ public class MainActivity extends AppCompatActivity { // 显示成功消息并关闭弹框 showToast("进入 " + selectedPlatform + " 成功!"); dialog.dismiss(); + currentDialog = null; // 清除引用 // 这里可以添加跳转到主界面的逻辑 } @@ -697,6 +709,7 @@ public class MainActivity extends AppCompatActivity { initializeFaceSDKIfNeeded(); dialog.dismiss(); + currentDialog = null; // 清除引用 // 这里可以添加跳转到主界面的逻辑 } @@ -769,6 +782,19 @@ public class MainActivity extends AppCompatActivity { android.util.Log.d("MainActivity", logMessage); LogManager.logInfo("FaceSDK", logMessage); + // 先检查和初始化配置文件 + final boolean configSuccess = initAndCheckFaceConfig(this); + + // 在UI线程显示配置文件状态提示 + runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(MainActivity.this, + configSuccess ? "配置文件加载成功" : "初始配置失败,已重置为默认配置", + Toast.LENGTH_SHORT).show(); + } + }); + // 获取FaceSDKManager实例 com.baidu.idl.face.main.finance.manager.FaceSDKManager faceSDKManager = com.baidu.idl.face.main.finance.manager.FaceSDKManager.getInstance(); @@ -865,11 +891,11 @@ public class MainActivity extends AppCompatActivity { android.util.Log.e("MainActivity", logMessage, e); LogManager.logError("FaceSDK", logMessage, e); runOnUiThread(new Runnable() { - @Override - public void run() { - showToast("人脸SDK初始化异常: " + e.getMessage()); - } - }); + @Override + public void run() { + showToast("人脸SDK初始化异常: " + e.getMessage()); + } + }); } } @@ -887,6 +913,9 @@ public class MainActivity extends AppCompatActivity { android.util.Log.d("MainActivity", logMessage); LogManager.logInfo("FaceSDK", logMessage); + // 在初始化模型前再次检查配置文件,确保配置已正确加载 + initAndCheckFaceConfig(this); + faceSDKManager.initModel(this, new com.baidu.idl.face.main.finance.listener.SdkInitListener() { @Override public void initStart() { @@ -941,6 +970,39 @@ public class MainActivity extends AppCompatActivity { } } + /** + * 根据设备类型切换摄像头配置 + * @param deviceType 设备类型,5表示第6批设备,其他值表示非第6批设备 + */ + private void switchCameraByDeviceType(int deviceType) { + try { + // 获取FaceSDKManager实例 + com.baidu.idl.face.main.finance.manager.FaceSDKManager faceSDKManager = + com.baidu.idl.face.main.finance.manager.FaceSDKManager.getInstance(); + + // 调用FaceSDKManager中的方法 + faceSDKManager.switchCameraByDeviceType(deviceType); + + String logMessage = "已根据设备类型(" + deviceType + ")切换摄像头配置"; + android.util.Log.d("MainActivity", logMessage); + LogManager.logInfo("FaceSDK", logMessage); + + // 显示切换成功提示 + runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(MainActivity.this, + "摄像头配置已切换为" + (deviceType == 5 ? "第6批" : "非第6批") + "设备", + Toast.LENGTH_SHORT).show(); + } + }); + } catch (Exception e) { + String errorMessage = "切换摄像头配置失败: " + e.getMessage(); + android.util.Log.e("MainActivity", errorMessage, e); + LogManager.logError("FaceSDK", errorMessage, e); + } + } + @Override protected void onResume() { super.onResume(); @@ -964,6 +1026,12 @@ public class MainActivity extends AppCompatActivity { protected void onDestroy() { super.onDestroy(); + // 关闭任何正在显示的Dialog以防止WindowLeaked错误 + if (currentDialog != null && currentDialog.isShowing()) { + currentDialog.dismiss(); + currentDialog = null; + } + LogManager.logOperation("MainActivity", "主界面正在销毁"); // 只在应用真正退出时才停止保持活跃 @@ -1239,4 +1307,27 @@ public class MainActivity extends AppCompatActivity { showToast("自启动管理器未初始化"); } } + + /** + * 初始化并检查人脸识别配置文件 + * @param context 上下文 + * @return 配置文件是否已成功初始化 + */ + private boolean initAndCheckFaceConfig(Context context) { + boolean isFinanceConfigExit = com.baidu.idl.face.main.finance.utils.FinanceConfigUtils.isConfigExit(context); + boolean isFinanceInitConfig = com.baidu.idl.face.main.finance.utils.FinanceConfigUtils.initConfig(); + + String logMessage = "配置文件状态: 存在=" + isFinanceConfigExit + ", 初始化=" + isFinanceInitConfig; + android.util.Log.d("MainActivity", logMessage); + + if (isFinanceInitConfig && isFinanceConfigExit) { + LogManager.logInfo("FaceSDK", "配置文件加载成功"); + return true; + } else { + LogManager.logWarning("FaceSDK", "初始配置失败,将重置文件内容为默认配置"); + // 修改为默认配置 + com.baidu.idl.face.main.finance.utils.FinanceConfigUtils.modityJson(); + return false; + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/ouxuan/oxface/OXFaceOnlineActivity.java b/app/src/main/java/com/ouxuan/oxface/OXFaceOnlineActivity.java new file mode 100644 index 0000000..19ac470 --- /dev/null +++ b/app/src/main/java/com/ouxuan/oxface/OXFaceOnlineActivity.java @@ -0,0 +1,547 @@ +package com.ouxuan.oxface; + +import android.content.Context; +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.Bundle; +import android.os.Handler; +import android.util.Log; +import android.view.TextureView; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; + +import com.baidu.idl.face.main.finance.activity.BaseActivity; +import com.baidu.idl.face.main.finance.callback.CameraDataCallback; +import com.baidu.idl.face.main.finance.callback.FaceDetectCallBack; +import com.baidu.idl.face.main.finance.camera.AutoTexturePreviewView; +import com.baidu.idl.face.main.finance.camera.CameraPreviewManager; +import com.baidu.idl.face.main.finance.manager.FaceSDKManager; +import com.baidu.idl.face.main.finance.model.LivenessModel; +import com.baidu.idl.face.main.finance.model.SingleBaseConfig; +import com.baidu.idl.face.main.finance.utils.BitmapUtils; +import com.baidu.idl.face.main.finance.utils.FaceOnDrawTexturViewUtil; +import com.baidu.idl.face.main.finance.utils.TestPopWindow; +import com.baidu.idl.main.facesdk.FaceInfo; +import com.baidu.idl.main.facesdk.model.BDFaceImageInstance; +import com.ouxuan.oxface.utils.LogManager; + +/** + * 简化版人脸识别界面 - 只显示视频流 + */ +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 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; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_oxface_online); + + mContext = this; + initView(); + + // 检查并请求相机权限 + 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, "相机权限已授予"); + } + + LogManager.logInfo(TAG, "OXFaceOnlineActivity onCreate"); + } + + /** + * View + */ + private void initView() { + LogManager.logInfo(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); + } + + // 返回 + ImageView mButReturn = findViewById(R.id.btn_back); + mButReturn.setOnClickListener(this); + + // ***************预览模式************* + // 导航栏 +// preText = findViewById(R.id.preview_text); +// if (preText != null) { +// preText.setOnClickListener(this); +// preText.setTextColor(Color.parseColor("#ffffff")); +// } +// previewView = findViewById(R.id.preview_view); + // 信息展示 + preViewRelativeLayout = findViewById(R.id.yvlan_relativeLayout); + preToastText = findViewById(R.id.pre_toast_text); + progressLayout = findViewById(R.id.progress_layout); + progressBarView = findViewById(R.id.progress_bar_view); + // 隐藏圆形进度条 + if (progressLayout != null) { + progressLayout.setVisibility(View.GONE); + } + // 预览模式下提示 + payHintRl = findViewById(R.id.pay_hintRl); + + // ***************开发模式************* + // 导航栏 +// deveLop = findViewById(R.id.develop_text); +// if (deveLop != null) { +// deveLop.setOnClickListener(this); +// deveLop.setTextColor(Color.parseColor("#a9a9a9")); +// } +// developView = findViewById(R.id.develop_view); +// if (developView != null) { +// developView.setVisibility(View.GONE); +// } + // 信息展示 +// deveLopRelativeLayout = findViewById(R.id.kaifa_relativeLayout); + // isCheckImage = findViewById(R.id.is_check_image); +// detectSurfaceText = findViewById(R.id.detect_surface_text); +// if (detectSurfaceText != null) { +// detectSurfaceText.setVisibility(View.GONE); +// } + // 送检RGB 图像回显 +// mFaceDetectImageView = findViewById(R.id.face_detect_image_view); +// if (mFaceDetectImageView != null) { +// mFaceDetectImageView.setVisibility(View.GONE); +// } + // 检测耗时 +// mTvDetect = findViewById(R.id.tv_detect_time); + // RGB活体 +// mTvLive = findViewById(R.id.tv_rgb_live_time); +// mTvLiveScore = findViewById(R.id.tv_rgb_live_score); + // 总耗时 +// mTvAllTime = findViewById(R.id.tv_all_time); + layoutCompareStatus = findViewById(R.id.layout_compare_status); + if (layoutCompareStatus != null) { + layoutCompareStatus.setVisibility(View.GONE); + } + textCompareStatus = findViewById(R.id.text_compare_status); + // 存图按钮 + saveCamera = findViewById(R.id.save_camera); + if (saveCamera != null) { + saveCamera.setOnClickListener(this); + saveCamera.setVisibility(View.GONE); + } + spot = findViewById(R.id.spot); + + // 质量检测未通过 + financeQualityTestFailed = findViewById(R.id.finance_quality_test_failed); + qualityTestTimeTv = findViewById(R.id.quality_test_timeTv); + qualityDetectedTv = findViewById(R.id.quality_detectedTv); + qualityShelteredPart = findViewById(R.id.quality_sheltered_part); + qualityRetestDetectBtn = findViewById(R.id.quality_retest_detectBtn); + if (qualityRetestDetectBtn != null) { + qualityRetestDetectBtn.setOnClickListener(this); + } + + // 活体通过 + financeByLivingDetection = findViewById(R.id.finance_by_living_detection); + byLivingDetectionRgb = findViewById(R.id.by_living_detection_rgb); + byLivingDetectionNir = findViewById(R.id.by_living_detection_nir); + byLivingDetectionDepth = findViewById(R.id.by_living_detection_depth); + byLivingTetectionTime = findViewById(R.id.by_living_detection_time); + byLivingDetectionFrames = findViewById(R.id.by_living_detection_Frames); + Button byLivingDetectionBtn = findViewById(R.id.by_living_detection_btn); + if (byLivingDetectionBtn != null) { + byLivingDetectionBtn.setOnClickListener(this); + } + detectRegImageItem = findViewById(R.id.detect_reg_image_item); + + // 活体未通过 + financeFailedInVivoTest = findViewById(R.id.finance_failed_in_vivo_test); + failedInVivoTestRgb = findViewById(R.id.failed_in_vivo_test_rgb); + failedInVivoTestNir = findViewById(R.id.failed_in_vivo_test_nir); + failedInVivoTestDepth = findViewById(R.id.failed_in_vivo_test_depth); + failedInVivoTestTime = findViewById(R.id.failed_in_vivo_test_time); + failedInVivoTestFrames = findViewById(R.id.failed_in_vivo_test_Frames); + Button failed_in_vivo_testBtn = findViewById(R.id.failed_in_vivo_testBtn); + if (failed_in_vivo_testBtn != null) { + failed_in_vivo_testBtn.setOnClickListener(this); + } + noDetectRegImageItem = findViewById(R.id.no_detect_reg_image_item); + + 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(); + } + } + }); + } + + @Override + protected void onResume() { + super.onResume(); + // 只有在拥有相机权限时才启动预览 + if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA) + == PackageManager.PERMISSION_GRANTED) { + startTestOpenDebugRegisterFunction(); + } + LogManager.logInfo(TAG, "OXFaceOnlineActivity onResume"); + } + + private void startTestOpenDebugRegisterFunction() { + LogManager.logInfo(TAG, "启动摄像头预览"); + // 设置摄像头方向 + 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) { + // 摄像头预览数据进行人脸检测 + if (isNeedCamera) { + FaceSDKManager.getInstance().onDetectCheck(data, null, null, + height, width, 1, new FaceDetectCallBack() { + @Override + public void onFaceDetectCallback(LivenessModel livenessModel) { + // 开发模式结果输出 + checkOpenDebugResult(livenessModel); + } + + @Override + public void onTip(int code, String msg) { + LogManager.logInfo(TAG, "人脸检测提示 - 代码: " + code + ", 消息: " + msg); + } + + @Override + public void onFaceDetectDarwCallback(LivenessModel livenessModel) { + // 绘制人脸框 + showFrame(livenessModel); + } + }); + } + } + }); + } + + /** + * 绘制人脸框 + */ + private void showFrame(final LivenessModel model) { + runOnUiThread(new Runnable() { + @Override + public void run() { + Canvas canvas = mDrawDetectFaceView.lockCanvas(); + if (canvas == null) { + mDrawDetectFaceView.unlockCanvasAndPost(canvas); + return; + } + if (model == null) { + // 清空canvas + 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]; + + rectF.set(FaceOnDrawTexturViewUtil.getFaceRectTwo(faceInfo)); + // 检测图片的坐标和显示的坐标不一样,需要转换。 + FaceOnDrawTexturViewUtil.mapFromOriginalRect(rectF, + mAutoCameraPreviewView, model.getBdFaceImageInstance()); + // 人脸框颜色 + FaceOnDrawTexturViewUtil.drawFaceColor(paint, paintBg, liveStatus, model); + // 绘制人脸框 + FaceOnDrawTexturViewUtil.drawRect(canvas, + rectF, paint, 5f, 50f, 25f); + } + // 清空canvas + mDrawDetectFaceView.unlockCanvasAndPost(canvas); + } + }); + } + + @Override + public void onClick(View view) { + int id = view.getId(); + if (id == R.id.btn_back) { + finish(); + } + } + + @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(); + LogManager.logInfo(TAG, "OXFaceOnlineActivity onDestroy"); + } + + // ***************开发模式结果输出************* + private void checkOpenDebugResult(final LivenessModel livenessModel) { + // 当未检测到人脸UI显示 + runOnUiThread(new Runnable() { + @Override + public void run() { + layoutCompareStatus.setBackgroundColor(Color.parseColor("#CC000000")); + if (livenessModel == null) { + liveStatus = false; + layoutCompareStatus.setVisibility(View.GONE); +// mFaceDetectImageView.setImageResource(R.mipmap.ic_image_video); +// mTvDetect.setText(String.format("检测耗时 :%s ms", 0)); +// mTvLive.setText(String.format("RGB活体检测耗时 :%s ms", 0)); +// mTvLiveScore.setText(String.format("RGB活体检测结果 :%s", false)); +// mTvAllTime.setText(String.format("总耗时 :%s ms", 0)); + return; + } + + BDFaceImageInstance image = livenessModel.getBdFaceImageInstance(); + if (image != null) { +// mFaceDetectImageView.setImageBitmap(BitmapUtils.getInstaceBmp(image)); + float faceArea = livenessModel.getFaceInfo().width * livenessModel.getFaceInfo().height; + image.destory(); + + // 新增:人脸过小检测 + if (faceArea < MIN_FACE_AREA) { + layoutCompareStatus.setVisibility(View.VISIBLE); + textCompareStatus.setTextColor(Color.parseColor("#fec133")); + textCompareStatus.setText("请靠近设备"); + return; + } + } + + // 模糊结果过滤 + float blur = livenessModel.getFaceInfo().bluriness; + if (blur > SingleBaseConfig.getBaseConfig().getBlur()) { + //照片模糊 + layoutCompareStatus.setVisibility(View.VISIBLE); + textCompareStatus.setTextColor(Color.parseColor("#fec133")); + textCompareStatus.setText("人脸模糊,请靠近"); + return; + } + + //质量检测 + if (!livenessModel.isQualityCheck()) { + liveStatus = false; + } else { + // 活体检测逻辑 + if (SingleBaseConfig.getBaseConfig().isLivingControl()) { + if (livenessModel.isRGBLiveStatus()) { + liveStatus = true; + layoutCompareStatus.setVisibility(View.VISIBLE); + textCompareStatus.setTextColor(Color.parseColor("#009874")); + textCompareStatus.setText("通过活体检测"); + } else { + liveStatus = false; + layoutCompareStatus.setVisibility(View.VISIBLE); + textCompareStatus.setTextColor(Color.parseColor("#fec133")); + textCompareStatus.setText("未通过活体检测"); + return; + } + } + } + + FaceInfo faceInfo = livenessModel.getFaceInfo(); + if (faceInfo.bestImageScore < SingleBaseConfig.getBaseConfig().getBestImageScore()) { + layoutCompareStatus.setVisibility(View.VISIBLE); + textCompareStatus.setTextColor(Color.parseColor("#fec133")); + textCompareStatus.setText("阈值低于" + SingleBaseConfig.getBaseConfig().getBestImageScore()); + return; + } + + //通过检测 + if (livenessModel != null) { + checkResultOnline(livenessModel);//在线人脸检测 + } else { + currentLivenessModel = null; + } + } + }); + } + + private void checkResultOnline(LivenessModel livenessModel) { + this.currentLivenessModel = livenessModel; + // 当未检测到人脸UI显示 + runOnUiThread(new Runnable() { + @Override + public void run() { + if (livenessModel == null) { + return; + } + String base64img = getFaceImageBase64(currentLivenessModel); + + if (base64img != null) { + // Log.i(TAG, "checkResultOnline: " + base64img); + Log.i(TAG, "checkResultOnline: Yes! Got a base64img!" ); + } else { + Log.i(TAG, "run:checkResultOnline 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(); + + if (true) { //判定条件 + BDFaceImageInstance image = livenessModel.getBdFaceImageInstance(); + Bitmap bitmap = BitmapUtils.getInstaceBmp(image); + + String bitmap_str = BitmapUtils.bitmapToBase64(bitmap); + image.destory(); + return bitmap_str; // 简化实现,实际项目中需要实现 bitmap 转 base64 的功能 + } else { + return null; + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/finance_radius.xml b/app/src/main/res/drawable/finance_radius.xml new file mode 100644 index 0000000..625c3d6 --- /dev/null +++ b/app/src/main/res/drawable/finance_radius.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/gate_imageview_radius.xml b/app/src/main/res/drawable/gate_imageview_radius.xml new file mode 100644 index 0000000..c191f76 --- /dev/null +++ b/app/src/main/res/drawable/gate_imageview_radius.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/gate_radius_compare.xml b/app/src/main/res/drawable/gate_radius_compare.xml new file mode 100644 index 0000000..4904079 --- /dev/null +++ b/app/src/main/res/drawable/gate_radius_compare.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/spot.xml b/app/src/main/res/drawable/spot.xml new file mode 100644 index 0000000..7820b18 --- /dev/null +++ b/app/src/main/res/drawable/spot.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_debug.xml b/app/src/main/res/layout/activity_debug.xml index 3d4a4d9..e016668 100644 --- a/app/src/main/res/layout/activity_debug.xml +++ b/app/src/main/res/layout/activity_debug.xml @@ -193,15 +193,14 @@ android:textSize="12sp" />