From 082011a939c0e06b4af0e15a3cd5fccd85242250 Mon Sep 17 00:00:00 2001 From: zxj <275873859@qq.com> Date: Thu, 24 Jul 2025 08:40:36 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4=E4=BA=86=E9=A4=90=E7=9B=98?= =?UTF-8?q?=E5=9C=A8=E4=B8=8D=E5=90=8C=E8=AE=BE=E5=A4=87=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 6 + .../java/com/sw/platecabinet/GlobalData.kt | 4 +- .../sw/platecabinet/activity/BaseActivity.kt | 5 +- .../activity/LoginByFaceActivity.kt | 18 +-- .../sw/platecabinet/utils/DateTimeUtils.kt | 7 + .../sw/platecabinet/utils/PermissionHelper.kt | 4 +- .../com/sw/platecabinet/utils/ThreadUtils.kt | 124 ++++++++++++++++++ .../viewmodel/SettingViewModel.kt | 23 +++- .../platecabinet/viewmodel/UserViewModel.kt | 28 ++-- .../main/res/layout/activity_login_face.xml | 5 +- app/src/main/res/layout/dialog_waiting.xml | 1 + .../layout/fragment_plate_cabinet_full.xml | 1 + .../main/res/layout/fragment_plate_open.xml | 1 + .../main/res/layout/fragment_setting_list.xml | 1 + .../utils/arcface/FaceRectTransformer.java | 2 +- .../plate/utils/arcface/face/FaceHelper.java | 1 + .../utils/arcface/faceserver/FaceServer.java | 2 +- 17 files changed, 198 insertions(+), 35 deletions(-) create mode 100644 app/src/main/java/com/sw/platecabinet/utils/ThreadUtils.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 12b6289..67ce82d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -8,6 +8,12 @@ + + : AppCompatActivity() { super.onCreate(savedInstanceState) enableEdgeToEdge() context = this - disableSystemUICompletely() +// disableSystemUICompletely() // 确保内容延伸到导航栏区域 - WindowCompat.setDecorFitsSystemWindows(window, false) +// WindowCompat.setDecorFitsSystemWindows(window, false) binding = inflateViewBinding() headerBinding = inflateTitleBinding() diff --git a/app/src/main/java/com/sw/platecabinet/activity/LoginByFaceActivity.kt b/app/src/main/java/com/sw/platecabinet/activity/LoginByFaceActivity.kt index 4ee8b93..8f4770a 100644 --- a/app/src/main/java/com/sw/platecabinet/activity/LoginByFaceActivity.kt +++ b/app/src/main/java/com/sw/platecabinet/activity/LoginByFaceActivity.kt @@ -60,8 +60,8 @@ class LoginByFaceActivity : BaseActivity(), } override fun initialize() { - // 请求权限 - checkCameraPermission() + +// viewModel.activeEngine() initArcViewModel() initArcView() openRectInfoDraw = true @@ -88,9 +88,11 @@ class LoginByFaceActivity : BaseActivity(), } private fun checkCameraPermission() { + Timber.i("checkCameraPermission") val permissionHelper = PermissionHelper.with(this, REQUIRED_PERMISSIONS, CAMERA_PERMISSION_REQUEST_CODE) .onGranted { + Timber.d("checkCameraPermission 权限已允许") // 权限已授予 recognizeViewModel.init() initRgbCamera() @@ -175,12 +177,14 @@ class LoginByFaceActivity : BaseActivity(), /** - * 调整View的宽高,使2个预览同时显示 + * 调整View的宽高,使预览显示正常且采集框固定为380x380 * + * @param rgbPreview RGB预览View * @param previewView 显示预览数据的view - * @param faceRectView 画框的view + * @param faceRectView 画框的view (380x380) * @param previewSize 预览大小 * @param displayOrientation 相机旋转角度 + * @param scale 缩放比例 * @return 调整后的LayoutParams */ private fun adjustPreviewViewSize( @@ -345,10 +349,8 @@ class LoginByFaceActivity : BaseActivity(), override fun onGlobalLayout() { binding.dualCameraTexturePreviewRgb.getViewTreeObserver().removeOnGlobalLayoutListener(this) - recognizeViewModel.init() - initRgbCamera() - resumeCamera() - // countDownTimer.start(); + // 请求权限 + checkCameraPermission() } diff --git a/app/src/main/java/com/sw/platecabinet/utils/DateTimeUtils.kt b/app/src/main/java/com/sw/platecabinet/utils/DateTimeUtils.kt index d8ee539..673eb9d 100644 --- a/app/src/main/java/com/sw/platecabinet/utils/DateTimeUtils.kt +++ b/app/src/main/java/com/sw/platecabinet/utils/DateTimeUtils.kt @@ -19,6 +19,13 @@ object DateTimeUtils { return SimpleDateFormat("yyyy年M月d日 EEEE", Locale.CHINA).format(date) } + /** + * 获取标准时间格式 + */ + fun getDateTimeString(date: Date = Date()): String { + return SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA).format(date) + } + /** * 获取带时间的完整中文格式(示例:2025年6月11日 星期三 14:30) */ diff --git a/app/src/main/java/com/sw/platecabinet/utils/PermissionHelper.kt b/app/src/main/java/com/sw/platecabinet/utils/PermissionHelper.kt index a8f635b..13129da 100644 --- a/app/src/main/java/com/sw/platecabinet/utils/PermissionHelper.kt +++ b/app/src/main/java/com/sw/platecabinet/utils/PermissionHelper.kt @@ -30,7 +30,7 @@ class PermissionHelper private constructor( /** * 检查是否已授予所有权限 */ - fun areAllPermissionsGranted(context: Context, permissions: Array): Boolean { + fun checkPermissions(context: Context, permissions: Array): Boolean { return permissions.all { permission -> ContextCompat.checkSelfPermission( context, @@ -121,7 +121,7 @@ class PermissionHelper private constructor( * 检查并请求权限 */ fun checkAndRequest() { - if (areAllPermissionsGranted(context, permissions)) { + if (checkPermissions(context, permissions)) { onGranted?.invoke() } else { requestPermissions() diff --git a/app/src/main/java/com/sw/platecabinet/utils/ThreadUtils.kt b/app/src/main/java/com/sw/platecabinet/utils/ThreadUtils.kt new file mode 100644 index 0000000..9fed1bd --- /dev/null +++ b/app/src/main/java/com/sw/platecabinet/utils/ThreadUtils.kt @@ -0,0 +1,124 @@ +package com.sw.platecabinet.utils + +import android.os.Handler +import android.os.Looper +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors +import kotlin.coroutines.CoroutineContext + +/** + * 多功能线程工具类 + * 结合协程、Handler和线程池实现线程切换 + */ +object ThreadUtils : CoroutineScope { + // 主线程Handler + private val mainHandler by lazy { Handler(Looper.getMainLooper()) } + + // 后台线程池(IO密集型任务) + private val ioThreadPool: ExecutorService by lazy { + Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2) + } + + // CPU密集型线程池 + private val cpuThreadPool: ExecutorService by lazy { + Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()) + } + + // 协程Job管理 + private val job = Job() + + override val coroutineContext: CoroutineContext + get() = Dispatchers.Main + job + + // ========== Handler相关方法 ========== + + /** + * 在主线程执行任务 + * @param delayMillis 延迟时间(毫秒) + */ + fun runOnUiThread(delayMillis: Long = 0, block: () -> Unit) { + if (delayMillis > 0) { + mainHandler.postDelayed(block, delayMillis) + } else { + if (isOnMainThread()) { + block() + } else { + mainHandler.post(block) + } + } + } + + /** + * 移除主线程任务 + */ + fun removeUiThreadTask(block: () -> Unit) { + mainHandler.removeCallbacks(block) + } + + // ========== 线程池相关方法 ========== + + /** + * 在IO线程执行任务 + */ + fun runOnIoThread(block: () -> Unit) { + ioThreadPool.execute(block) + } + + /** + * 在CPU计算线程执行任务 + */ + fun runOnCpuThread(block: () -> Unit) { + cpuThreadPool.execute(block) + } + + // ========== 协程相关方法 ========== + + /** + * 启动协程(默认在主线程) + */ + fun launch(block: suspend CoroutineScope.() -> Unit): Job { + return launch(coroutineContext, block = block) + } + + /** + * 在IO线程启动协程 + */ + fun launchOnIo(block: suspend CoroutineScope.() -> Unit): Job { + return launch(Dispatchers.IO, block = block) + } + + /** + * 切换到主线程(协程环境) + */ + suspend fun switchToMain(block: suspend CoroutineScope.() -> T): T { + return withContext(Dispatchers.Main, block) + } + + /** + * 切换到IO线程(协程环境) + */ + suspend fun switchToIo(block: suspend CoroutineScope.() -> T): T { + return withContext(Dispatchers.IO, block) + } + + /** + * 是否在主线程 + */ + fun isOnMainThread(): Boolean { + return Looper.myLooper() == Looper.getMainLooper() + } + + /** + * 释放资源 + */ + fun release() { + job.cancel() + ioThreadPool.shutdown() + cpuThreadPool.shutdown() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/sw/platecabinet/viewmodel/SettingViewModel.kt b/app/src/main/java/com/sw/platecabinet/viewmodel/SettingViewModel.kt index c31061b..0618be8 100644 --- a/app/src/main/java/com/sw/platecabinet/viewmodel/SettingViewModel.kt +++ b/app/src/main/java/com/sw/platecabinet/viewmodel/SettingViewModel.kt @@ -1,5 +1,6 @@ package com.sw.platecabinet.viewmodel +import com.sw.inbound.utils.DateTimeUtils import com.sw.plate.utils.ToastUtils import com.sw.platecabinet.GlobalData import com.sw.platecabinet.GlobalData.globalEquipmentCode @@ -43,7 +44,7 @@ class SettingViewModel : BaseViewModel() { EquipmentParam(appVersion = GlobalData.appVersion, equipmentCode = equipmentCode) val response = repository.getEquipmentList(param) if (parseResponse(response)) { - _equipmentList.value = response.data?.plus(response.data) ?: emptyList() + _equipmentList.value = response.data ?: emptyList() } } } @@ -94,6 +95,13 @@ class SettingViewModel : BaseViewModel() { ) val response = repository.bindEquipment(bindParam) if (parseResponse(response)) { + val userInfo = response.data + userInfo?.let { + if (it.isOtherEquipment()) { + ToastUtils.showToast("餐盘已在${it.equipmentName}绑定") + return@launchWithLoading + } + } _bindStateChange.value = true to ErrorInfo() } else { _bindStateChange.value = true to ErrorInfo(response.code, response.msg.toString()) @@ -132,15 +140,20 @@ class SettingViewModel : BaseViewModel() { ) val response = repository.findByPlateNumber(bindParam) if (response.code == 200) { - if (response.data?.isOtherEquipment() == true) { - ToastUtils.showToast("") - return@launchWithLoading + val userInfo = response.data + userInfo?.let { + if (it.isOtherEquipment()) { + ToastUtils.showToast("餐盘已在${it.equipmentName}绑定") + return@launchWithLoading + } } + _currentUserInfo.value = response.data } else { _currentUserInfo.value = EquipmentUserInfo( plateNumber = plateNumber, - equipmentCode = equipmentCode + equipmentCode = equipmentCode, + updateTime = DateTimeUtils.getDateTimeString() ) } } diff --git a/app/src/main/java/com/sw/platecabinet/viewmodel/UserViewModel.kt b/app/src/main/java/com/sw/platecabinet/viewmodel/UserViewModel.kt index b1445fb..6a902e1 100644 --- a/app/src/main/java/com/sw/platecabinet/viewmodel/UserViewModel.kt +++ b/app/src/main/java/com/sw/platecabinet/viewmodel/UserViewModel.kt @@ -13,6 +13,7 @@ import com.sw.platecabinet.GlobalKey import com.sw.platecabinet.model.request.LoginParam import com.sw.platecabinet.model.response.EquipmentUserInfo import com.sw.platecabinet.model.response.UserFaceModel +import com.sw.platecabinet.utils.ThreadUtils import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import timber.log.Timber @@ -63,7 +64,8 @@ class UserViewModel : BaseViewModel() { /** * 激活人脸识别引擎 */ - private fun activeEngine() { + fun activeEngine() { + Timber.d("activeEngine") var appId = "Hkz1rBk6PZXbS8KwKr67K2eZtsz8bRoMHLg64bUdgCZj" var sdkKey = "AabXs3sHM8UhhE7oCGf4LVMLrdkGb1nJNksbjjTdVt7k" var activeKey = "085F-118G-Q391-53YL" @@ -75,23 +77,27 @@ class UserViewModel : BaseViewModel() { object : FaceApi.ActiveCallback { override fun onSuccess(activeCode: Int) { Timber.d("activeEngine activeCode = $activeCode") - when (activeCode) { - ErrorInfo.MOK -> { - ToastUtils.showToast("激活引擎成功") - } + ThreadUtils.launch { + when (activeCode) { + ErrorInfo.MOK -> { + ToastUtils.showToast("激活引擎成功") + } - ErrorInfo.MERR_ASF_ALREADY_ACTIVATED -> { - ToastUtils.showToast("引擎已激活,无需再次激活") - } + ErrorInfo.MERR_ASF_ALREADY_ACTIVATED -> { +// ToastUtils.showToast("引擎已激活,无需再次激活") + } - else -> { - ToastUtils.showToast("激活引擎失败($activeCode)") + else -> { + ToastUtils.showToast("激活引擎失败($activeCode)") + } } } } override fun onFail(e: Exception?) { - ToastUtils.showToast("激活引擎异常,${e?.message}") + ThreadUtils.launch { + ToastUtils.showToast("激活引擎异常,${e?.message}") + } } }) } diff --git a/app/src/main/res/layout/activity_login_face.xml b/app/src/main/res/layout/activity_login_face.xml index 8e1dd43..ef23d7b 100644 --- a/app/src/main/res/layout/activity_login_face.xml +++ b/app/src/main/res/layout/activity_login_face.xml @@ -5,8 +5,8 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:background="#14141E" - android:orientation="vertical" android:fitsSystemWindows="false" + android:orientation="vertical" tools:context=".activity.LoginByFaceActivity"> + android:layout_height="match_parent" + android:layout_margin="10dp" /> diff --git a/lib_face/src/main/java/com/sw/plate/utils/arcface/FaceRectTransformer.java b/lib_face/src/main/java/com/sw/plate/utils/arcface/FaceRectTransformer.java index f1c7295..58c9d5d 100644 --- a/lib_face/src/main/java/com/sw/plate/utils/arcface/FaceRectTransformer.java +++ b/lib_face/src/main/java/com/sw/plate/utils/arcface/FaceRectTransformer.java @@ -77,7 +77,7 @@ public class FaceRectTransformer { rect.bottom *= verticalRatio; Rect newRect = new Rect(); - L.e("cameraDisplayOrientation" + cameraDisplayOrientation + "===" + cameraId); + L.e("cameraDisplayOrientation " + cameraDisplayOrientation + " === " + cameraId); switch (cameraDisplayOrientation) { case 0: if (cameraId == Camera.CameraInfo.CAMERA_FACING_FRONT) { diff --git a/lib_face/src/main/java/com/sw/plate/utils/arcface/face/FaceHelper.java b/lib_face/src/main/java/com/sw/plate/utils/arcface/face/FaceHelper.java index bf51353..3f78375 100644 --- a/lib_face/src/main/java/com/sw/plate/utils/arcface/face/FaceHelper.java +++ b/lib_face/src/main/java/com/sw/plate/utils/arcface/face/FaceHelper.java @@ -610,6 +610,7 @@ public class FaceHelper implements FaceListener { @Override public void onError(Throwable e) { + Log.e(TAG, "retryRecognizeDelayed e = " + e); e.printStackTrace(); } diff --git a/lib_face/src/main/java/com/sw/plate/utils/arcface/faceserver/FaceServer.java b/lib_face/src/main/java/com/sw/plate/utils/arcface/faceserver/FaceServer.java index 7baf8e7..e414751 100644 --- a/lib_face/src/main/java/com/sw/plate/utils/arcface/faceserver/FaceServer.java +++ b/lib_face/src/main/java/com/sw/plate/utils/arcface/faceserver/FaceServer.java @@ -584,7 +584,7 @@ public class FaceServer { } } } catch (IllegalArgumentException exception) { - Log.i(TAG, "exception:" + exception.getMessage()); + Log.i(TAG, "searchFaceFeature exception:" + exception.getMessage()); } return null; }