From 7fd2ccb960cba5009d87c1203adfc321deac7ed8 Mon Sep 17 00:00:00 2001 From: zxj <275873859@qq.com> Date: Wed, 23 Jul 2025 14:22:11 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4=E4=BA=86=E9=83=A8=E5=88=86?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/sw/platecabinet/GlobalData.kt | 11 +- .../main/java/com/sw/platecabinet/MyApp.kt | 10 + .../sw/platecabinet/activity/BaseActivity.kt | 105 +++++++- .../activity/LoginByFaceActivity.kt | 15 +- .../activity/LoginByPwdActivity.kt | 28 +-- .../sw/platecabinet/activity/MainActivity.kt | 18 ++ .../adapter/GenericPageAdapter.kt | 29 ++- .../sw/platecabinet/fragment/BaseFragment.kt | 17 +- .../fragment/BindPlateFragment.kt | 32 ++- .../fragment/SettingListFragment.kt | 231 ++++++++++-------- .../fragment/UnBindPlateFragment.kt | 16 +- .../platecabinet/model/request/BindParam.kt | 16 +- .../model/request/EquipmentParam.kt | 8 +- .../platecabinet/model/request/LoginParam.kt | 6 +- .../platecabinet/model/request/SearchParam.kt | 9 + .../model/response/EquipmentUserInfo.kt | 13 +- .../model/response/SearchResult.kt | 3 + .../sw/platecabinet/network/api/ApiService.kt | 16 +- .../repository/RemoteRepository.kt | 23 +- .../sw/platecabinet/utils/DateTimeUtils.kt | 2 - .../viewmodel/SettingViewModel.kt | 39 ++- .../platecabinet/viewmodel/UserViewModel.kt | 3 + .../main/res/layout/activity_login_by_pwd.xml | 2 +- .../main/res/layout/fragment_bind_plate.xml | 11 +- .../main/res/layout/fragment_unbind_plate.xml | 4 +- .../sw/plate/utils/ScanGunKeyEventHelper.java | 10 +- .../com/sw/plate/utils/arcface/FaceApi.java | 10 +- .../com/sw/plate/utils/comn/SerialApi.java | 9 +- 28 files changed, 486 insertions(+), 210 deletions(-) diff --git a/app/src/main/java/com/sw/platecabinet/GlobalData.kt b/app/src/main/java/com/sw/platecabinet/GlobalData.kt index 60523d5..d9dda34 100644 --- a/app/src/main/java/com/sw/platecabinet/GlobalData.kt +++ b/app/src/main/java/com/sw/platecabinet/GlobalData.kt @@ -6,6 +6,11 @@ object GlobalData { */ var globalEquipmentCode: String = "202501171634" + /** + * app版本号 + */ + var appVersion: String = "" + /** * 横排数量 */ @@ -17,9 +22,11 @@ object GlobalData { var arrayVertical: Int = 11 /** - * 排列方式 + * 排列方式 0 垂直 1 水平 */ - var arrayMode: Int = 0 + var arrayMode: Int = 1 + + } /** diff --git a/app/src/main/java/com/sw/platecabinet/MyApp.kt b/app/src/main/java/com/sw/platecabinet/MyApp.kt index cd64514..65f0140 100644 --- a/app/src/main/java/com/sw/platecabinet/MyApp.kt +++ b/app/src/main/java/com/sw/platecabinet/MyApp.kt @@ -1,6 +1,7 @@ package com.sw.platecabinet import com.sw.plate.App +import com.sw.plate.utils.AppUtil import timber.log.Timber class MyApp : App() { @@ -12,5 +13,14 @@ class MyApp : App() { super.onCreate() Timber.plant(Timber.DebugTree()) Timber.d("初始化") + initGlobalData() + } + + /** + * 初始化全局数据 + */ + private fun initGlobalData() { + GlobalData.appVersion = AppUtil.getAppVersionName(this) + GlobalData.globalEquipmentCode = "202501171634" } } \ No newline at end of file diff --git a/app/src/main/java/com/sw/platecabinet/activity/BaseActivity.kt b/app/src/main/java/com/sw/platecabinet/activity/BaseActivity.kt index 5eaf62b..47a13b2 100644 --- a/app/src/main/java/com/sw/platecabinet/activity/BaseActivity.kt +++ b/app/src/main/java/com/sw/platecabinet/activity/BaseActivity.kt @@ -5,24 +5,34 @@ import android.content.Context import android.os.Build import android.os.Bundle import android.text.TextUtils +import android.view.KeyEvent import android.view.View import android.view.WindowInsetsController import android.view.WindowManager import android.widget.TextView import androidx.activity.enableEdgeToEdge +import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.core.view.WindowCompat +import androidx.lifecycle.lifecycleScope import androidx.viewbinding.ViewBinding import com.sw.inbound.utils.DateTimeUtils +import com.sw.plate.utils.ScanGunKeyEventHelper +import com.sw.plate.utils.ToastUtils +import com.sw.plate.utils.comn.SerialApi +import com.sw.plate.utils.comn.SerialPortManager import com.sw.platecabinet.R import com.sw.platecabinet.databinding.ItemTitleTimeBinding import com.sw.platecabinet.ext.setClickListeners import com.sw.platecabinet.utils.PermissionHelper import com.sw.platecabinet.view.CustomDialog +import com.sw.platecabinet.viewmodel.SettingViewModel import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.drop import kotlinx.coroutines.launch +import timber.log.Timber /** * activity 基类 @@ -34,6 +44,8 @@ abstract class BaseActivity : AppCompatActivity() { private var timeJob: Job? = null private var mDialogWaiting: CustomDialog? = null private val permissionHelpers = mutableMapOf() + protected var keyEventHelper: ScanGunKeyEventHelper? = null + private val viewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -49,6 +61,38 @@ abstract class BaseActivity : AppCompatActivity() { updateTime() registerDataChange() initialize() + registerKeyEvent() + } + + /** + * 监听扫描枪扫描事件 + */ + protected fun registerKeyEvent() { + keyEventHelper = + ScanGunKeyEventHelper(context, object : ScanGunKeyEventHelper.OnScanSuccessListener { + override fun onScanSuccess(barcode: String?) { + Timber.d("onScanSuccess barcode = $barcode") + if (barcode == null) return + handleScanKeyInfo(barcode) + } + }) + } + + /** + * 处理扫描枪数据 + */ + protected open fun handleScanKeyInfo(scanInfo: String) { + viewModel.findByPlateNumber(plateNumber = scanInfo) + } + + override fun dispatchKeyEvent(event: KeyEvent): Boolean { + if (keyEventHelper != null) { + if (keyEventHelper!!.isScanGunEvent(event)) { + keyEventHelper!!.analysisKeyEvent(event) + return true + } + } + return super.dispatchKeyEvent(event) } fun updateTime() { @@ -74,10 +118,16 @@ abstract class BaseActivity : AppCompatActivity() { } } + /** + * 左侧日期双击 + */ open fun onLeftDoubleClick() { } + /** + * 右侧时间双击 + */ open fun onRightDoubleClick() { MainActivity.start(context, pageType = PageType.SETTING_LIST) } @@ -139,6 +189,7 @@ abstract class BaseActivity : AppCompatActivity() { window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) } + private fun enforceImmersiveMode() { // 持续强制隐藏系统栏(防止手势触发) window.decorView.postDelayed({ @@ -177,10 +228,62 @@ abstract class BaseActivity : AppCompatActivity() { protected abstract fun initialize() - protected open fun registerDataChange() {} + /** + * 注册数据监听 + */ + protected open fun registerDataChange() { + lifecycleScope.launch { + viewModel.showLoading.collect { + if (it) { + showWaitingDialog("") + } else { + hideWaitingDialog() + } + } + } + lifecycleScope.launch { + viewModel.currentUserInfo.drop(1).collect { + if (it == null) return@collect + if (it.equipmentBoxCode?.isNotEmpty() == true) { + SerialApi.openPlate( + it.equipmentBoxCode.toInt(), + object : SerialPortManager.SendCallback { + override fun onSuccess() { + MainActivity.start(context, pageType = PageType.PLATE_OPEN) + } + + override fun onFail(e: Exception?) { + ToastUtils.showToast("柜门打开失败") + } + }) + } else { + viewModel.getEquipmentList() + } + } + } + lifecycleScope.launch { + viewModel.equipmentList.drop(1).collect { + if (it.isEmpty()) return@collect + val unbindList = it.filter { !it.isBound() } + if (unbindList.isEmpty()) { + MainActivity.start(context = context, pageType = PageType.PLATE_CABINET_FULL) + } else { + val firstInfo = unbindList[0] + val currentUserInfo = viewModel.currentUserInfo.value + firstInfo.plateNumber = currentUserInfo?.plateNumber ?: "" + MainActivity.start( + context = context, + pageType = PageType.BIND_PLATE, + equipmentUserInfo = firstInfo + ) + } + } + } + } override fun onDestroy() { timeJob?.cancel() + keyEventHelper?.onDestroy() super.onDestroy() } } \ No newline at end of file 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 27f4896..4ee8b93 100644 --- a/app/src/main/java/com/sw/platecabinet/activity/LoginByFaceActivity.kt +++ b/app/src/main/java/com/sw/platecabinet/activity/LoginByFaceActivity.kt @@ -32,6 +32,7 @@ import com.sw.platecabinet.databinding.ActivityLoginFaceBinding import com.sw.platecabinet.databinding.ItemTitleTimeBinding import com.sw.platecabinet.utils.PermissionHelper import com.sw.platecabinet.viewmodel.UserViewModel +import kotlinx.coroutines.flow.drop import kotlinx.coroutines.launch import timber.log.Timber @@ -73,8 +74,9 @@ class LoginByFaceActivity : BaseActivity(), } override fun registerDataChange() { + super.registerDataChange() lifecycleScope.launch { - viewModel.currentUserInfo.collect { + viewModel.currentUserInfo.drop(1).collect { Timber.d("currentUserInfo it = $it") if (it != null) { ToastUtils.showToast("登录成功") @@ -83,15 +85,6 @@ class LoginByFaceActivity : BaseActivity(), } } } - lifecycleScope.launch { - viewModel.showLoading.collect { - if (it) { - showWaitingDialog("") - } else { - hideWaitingDialog() - } - } - } } private fun checkCameraPermission() { @@ -314,7 +307,7 @@ class LoginByFaceActivity : BaseActivity(), ) ) .rotation(windowManager.defaultDisplay.rotation) - .additionalRotation(90) // 角度 + .additionalRotation(0) // 角度 .previewSize(recognizeViewModel.loadPreviewSize()) .specificCameraId(previewConfig.rgbCameraId) .isMirror(true) diff --git a/app/src/main/java/com/sw/platecabinet/activity/LoginByPwdActivity.kt b/app/src/main/java/com/sw/platecabinet/activity/LoginByPwdActivity.kt index d6030a6..44c7501 100644 --- a/app/src/main/java/com/sw/platecabinet/activity/LoginByPwdActivity.kt +++ b/app/src/main/java/com/sw/platecabinet/activity/LoginByPwdActivity.kt @@ -7,6 +7,7 @@ import com.sw.plate.utils.ToastUtils import com.sw.platecabinet.databinding.ActivityLoginByPwdBinding import com.sw.platecabinet.databinding.ItemTitleTimeBinding import com.sw.platecabinet.viewmodel.UserViewModel +import kotlinx.coroutines.flow.drop import kotlinx.coroutines.launch import timber.log.Timber @@ -34,28 +35,23 @@ class LoginByPwdActivity : BaseActivity() { binding.tvFaceRec.setOnClickListener { val intent = Intent(this, LoginByFaceActivity::class.java) startActivity(intent) +// handleScanKeyInfo(binding.etPwd.text.toString()) } } override fun registerDataChange() { + super.registerDataChange() lifecycleScope.launch { - viewModel.currentUserInfo.collect { - Timber.d("currentUserInfo it = $it") - if (it != null) { - ToastUtils.showToast("登录成功") - MainActivity.start(context, pageType = PageType.PLATE_CABINET_FULL) - finish() + viewModel.currentUserInfo + .drop(1) + .collect { + Timber.d("currentUserInfo it = $it") + if (it != null) { +// ToastUtils.showToast("登录成功") + MainActivity.start(context, pageType = PageType.PLATE_OPEN) + finish() + } } - } - } - lifecycleScope.launch { - viewModel.showLoading.collect { - if (it) { - showWaitingDialog("") - } else { - hideWaitingDialog() - } - } } } diff --git a/app/src/main/java/com/sw/platecabinet/activity/MainActivity.kt b/app/src/main/java/com/sw/platecabinet/activity/MainActivity.kt index 5d5373a..fe4b4e6 100644 --- a/app/src/main/java/com/sw/platecabinet/activity/MainActivity.kt +++ b/app/src/main/java/com/sw/platecabinet/activity/MainActivity.kt @@ -13,9 +13,12 @@ import com.sw.platecabinet.fragment.UnBindPlateFragment import com.sw.platecabinet.model.response.EquipmentUserInfo import com.sw.platecabinet.utils.FragmentHelper +typealias Callback = (String) -> Unit + class MainActivity : BaseActivity() { private lateinit var fragmentHelper: FragmentHelper private var pageType = PageType.SETTING_LIST + private var callback: Callback? = null override fun inflateViewBinding(): ActivityMainBinding { return ActivityMainBinding.inflate(layoutInflater) @@ -65,8 +68,23 @@ class MainActivity : BaseActivity() { override fun onRightDoubleClick() { } + + override fun handleScanKeyInfo(scanInfo: String) { + if (callback != null) { + callback!!.invoke(scanInfo) + } + } + + fun registerKeyEventInfo(callback: Callback) { + this.callback = callback + } + + override fun registerDataChange() { + super.registerDataChange() + } } + /** * 界面类型 */ diff --git a/app/src/main/java/com/sw/platecabinet/adapter/GenericPageAdapter.kt b/app/src/main/java/com/sw/platecabinet/adapter/GenericPageAdapter.kt index fe53a35..adbd966 100644 --- a/app/src/main/java/com/sw/platecabinet/adapter/GenericPageAdapter.kt +++ b/app/src/main/java/com/sw/platecabinet/adapter/GenericPageAdapter.kt @@ -25,7 +25,18 @@ class GenericPageAdapter( override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PageViewHolder { val view = LayoutInflater.from(parent.context).inflate(pageLayoutId, parent, false) - return PageViewHolder(view) + val holder = PageViewHolder(view) + + // 在创建ViewHolder时设置ItemDecoration,只设置一次 + holder.childRecyclerView.addItemDecoration( + GridSpacingItemDecoration( + spanCount = 2, + spacing = view.context.dpToPx(12), + includeEdge = true // 包含边缘间距 + ) + ) + + return holder } override fun onBindViewHolder(holder: PageViewHolder, position: Int) { @@ -35,19 +46,13 @@ class GenericPageAdapter( override fun getItemCount(): Int = pages.size inner class PageViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - private val childRecyclerView: RecyclerView = itemView.findViewById(R.id.childRecyclerView) + val childRecyclerView: RecyclerView = itemView.findViewById(R.id.childRecyclerView) + + init { + childRecyclerView.layoutManager = GridLayoutManager(itemView.context, 2) + } fun bind(items: List) { - childRecyclerView.layoutManager = GridLayoutManager(itemView.context, 2) - - childRecyclerView.addItemDecoration( - GridSpacingItemDecoration( - spanCount = 2, // 2列 - spacing = itemView.context.dpToPx(6), // 12dp - includeEdge = true // 包含边缘间距 - ) - ) - childRecyclerView.adapter = GenericItemAdapter(items, itemBindingInflater, itemBindCallback) } diff --git a/app/src/main/java/com/sw/platecabinet/fragment/BaseFragment.kt b/app/src/main/java/com/sw/platecabinet/fragment/BaseFragment.kt index 8017f08..4f54730 100644 --- a/app/src/main/java/com/sw/platecabinet/fragment/BaseFragment.kt +++ b/app/src/main/java/com/sw/platecabinet/fragment/BaseFragment.kt @@ -8,9 +8,13 @@ import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope import androidx.viewbinding.ViewBinding import com.sw.platecabinet.R import com.sw.platecabinet.view.CustomDialog +import com.sw.platecabinet.viewmodel.SettingViewModel +import kotlinx.coroutines.launch abstract class BaseFragment( private val bindingInflater: (inflater: LayoutInflater, parent: ViewGroup?, attachToParent: Boolean) -> VB @@ -18,6 +22,7 @@ abstract class BaseFragment( private var _binding: VB? = null protected val binding get() = _binding!! private var mDialogWaiting: CustomDialog? = null + protected val viewModel by viewModels() override fun onCreateView( inflater: LayoutInflater, @@ -33,7 +38,17 @@ abstract class BaseFragment( /** * 注册数据变化监听 */ - open fun registerDataChange() {} + open fun registerDataChange() { + lifecycleScope.launch { + viewModel.showLoading.collect { + if (it) { + showWaitingDialog("") + } else { + hideWaitingDialog() + } + } + } + } abstract fun initialize() diff --git a/app/src/main/java/com/sw/platecabinet/fragment/BindPlateFragment.kt b/app/src/main/java/com/sw/platecabinet/fragment/BindPlateFragment.kt index 0917ae6..ed4ce8f 100644 --- a/app/src/main/java/com/sw/platecabinet/fragment/BindPlateFragment.kt +++ b/app/src/main/java/com/sw/platecabinet/fragment/BindPlateFragment.kt @@ -4,14 +4,15 @@ import android.os.Bundle import android.os.Handler import android.os.Looper import android.text.Editable +import android.text.TextUtils import android.text.TextWatcher -import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.sw.plate.utils.ToastUtils import com.sw.platecabinet.R +import com.sw.platecabinet.activity.MainActivity import com.sw.platecabinet.adapter.GenericItemAdapter import com.sw.platecabinet.adapter.GridSpacingItemDecoration import com.sw.platecabinet.adapter.dpToPx @@ -21,7 +22,7 @@ import com.sw.platecabinet.ext.maskName import com.sw.platecabinet.ext.maskPhone import com.sw.platecabinet.model.response.EquipmentUserInfo import com.sw.platecabinet.model.response.SearchResult -import com.sw.platecabinet.viewmodel.SettingViewModel +import kotlinx.coroutines.flow.drop import kotlinx.coroutines.launch import timber.log.Timber @@ -32,7 +33,6 @@ class BindPlateFragment : BaseFragment( FragmentBindPlateBinding::inflate ) { private lateinit var adapter: GenericItemAdapter - private val viewModel by viewModels() internal val ARG_PARAM1 = "param1" private var info: EquipmentUserInfo? = null private var checkedItem: SearchResult.Member? = null @@ -53,6 +53,9 @@ class BindPlateFragment : BaseFragment( info = it.getParcelable(ARG_PARAM1) info?.let { binding.tvNum.text = "${it.equipmentName}-${it.equipmentBoxCode}" + if (!TextUtils.isEmpty(it.plateNumber)) { + binding.etCode.text = Editable.Factory.getInstance().newEditable(it.plateNumber) + } } } initView() @@ -88,6 +91,12 @@ class BindPlateFragment : BaseFragment( } } }) + (activity as MainActivity).registerKeyEventInfo { + binding.etCode.text = Editable.Factory.getInstance().newEditable(it) + } + } + + private fun initListener() { binding.etUserInfo.addTextChangedListener(object : TextWatcher { private val handler = Handler(Looper.getMainLooper()) private val debounceDelay = 500L // 延迟 500 毫秒 @@ -120,9 +129,6 @@ class BindPlateFragment : BaseFragment( }, debounceDelay) } }) - } - - private fun initListener() { binding.llBind.setOnClickListener { if (info == null || info!!.equipmentCode?.isEmpty() == true || info!!.equipmentBoxCode?.isEmpty() == true) { ToastUtils.showToast("传入的设备信息异常") @@ -156,13 +162,14 @@ class BindPlateFragment : BaseFragment( * 监听数据变化 */ override fun registerDataChange() { + super.registerDataChange() lifecycleScope.launch { - viewModel.searchMemberList.collect { + viewModel.searchMemberList.drop(1).collect { adapter.updateData(it) } } lifecycleScope.launch { - viewModel.bindStateChange.collect { + viewModel.bindStateChange.drop(1).collect { if (it.first == null) return@collect // 解绑状态过滤 val errorInfo = it.second if (errorInfo.isSuccess()) { @@ -173,15 +180,6 @@ class BindPlateFragment : BaseFragment( } } } - lifecycleScope.launch { - viewModel.showLoading.collect { - if (it) { - showWaitingDialog("") - } else { - hideWaitingDialog() - } - } - } } private fun createAdapter(): GenericItemAdapter { diff --git a/app/src/main/java/com/sw/platecabinet/fragment/SettingListFragment.kt b/app/src/main/java/com/sw/platecabinet/fragment/SettingListFragment.kt index d1ff7d7..c800956 100644 --- a/app/src/main/java/com/sw/platecabinet/fragment/SettingListFragment.kt +++ b/app/src/main/java/com/sw/platecabinet/fragment/SettingListFragment.kt @@ -1,24 +1,28 @@ package com.sw.platecabinet.fragment import android.view.View -import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import com.sw.inbound.utils.DateTimeUtils import com.sw.plate.utils.ToastUtils import com.sw.plate.utils.comn.SerialApi import com.sw.plate.utils.comn.SerialPortManager +import com.sw.platecabinet.GlobalData import com.sw.platecabinet.R import com.sw.platecabinet.activity.MainActivity import com.sw.platecabinet.activity.PageType import com.sw.platecabinet.adapter.GenericItemAdapter import com.sw.platecabinet.adapter.GenericPageAdapter +import com.sw.platecabinet.adapter.GridSpacingItemDecoration +import com.sw.platecabinet.adapter.dpToPx import com.sw.platecabinet.databinding.FragmentSettingListBinding import com.sw.platecabinet.databinding.ItemBindViewBinding import com.sw.platecabinet.ext.formatNumber import com.sw.platecabinet.ext.maskName import com.sw.platecabinet.model.response.EquipmentUserInfo -import com.sw.platecabinet.viewmodel.SettingViewModel +import kotlinx.coroutines.flow.drop import kotlinx.coroutines.launch import timber.log.Timber import kotlin.math.ceil @@ -28,35 +32,47 @@ import kotlin.math.ceil */ class SettingListFragment : BaseFragment(FragmentSettingListBinding::inflate) { - private val viewModel: SettingViewModel by viewModels() - private lateinit var pageAdapter: GenericPageAdapter - private lateinit var itemAdapter: GenericItemAdapter - private val itemsPerPage = 22 // 每行2个,每列11个,共22个 + + /** + * 需要横向滚动的adapter + */ + private var pageAdapter: GenericPageAdapter? = null + + /** + * 垂直滚动的adapter + */ + private var itemAdapter: GenericItemAdapter? = null + private val itemsPerPage = GlobalData.arrayCross * GlobalData.arrayVertical // 每行2个,每列11个,共22个 override fun registerDataChange() { + super.registerDataChange() lifecycleScope.launch { - viewModel.equipmentList.collect { it -> + viewModel.equipmentList.drop(1).collect { it -> Timber.d("registerDateChange updateAdapter it = $it") updateAdapter(it) } } - lifecycleScope.launch { - viewModel.showLoading.collect { - if (it) { - showWaitingDialog("") - } else { - hideWaitingDialog() - } - } - } } override fun initialize() { binding.mainRecyclerView.apply { - layoutManager = LinearLayoutManager(context) - - pageAdapter = createAdapter() - adapter = pageAdapter + if (GlobalData.arrayCross > 2) { + layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false) + pageAdapter = createPageAdapter() + adapter = pageAdapter + } else { + layoutManager = GridLayoutManager(context, 2) + // 添加间距装饰(12dp) + addItemDecoration( + GridSpacingItemDecoration( + spanCount = 2, + spacing = requireContext().dpToPx(12), + includeEdge = true // 包含边缘间距 + ) + ) + itemAdapter = createItemAdapter() + adapter = itemAdapter + } } } @@ -66,94 +82,115 @@ class SettingListFragment : viewModel.getEquipmentList() } - private fun createAdapter(): GenericPageAdapter { + private fun createItemAdapter(): GenericItemAdapter { + return GenericItemAdapter( + items = emptyList(), + bindingInflater = ItemBindViewBinding::inflate, + bindCallback = { item, position -> + setItemInfo(item, position) + } + ) + } + + private fun createPageAdapter(): GenericPageAdapter { return GenericPageAdapter( pages = emptyList(), pageLayoutId = R.layout.page_item_layout, itemBindingInflater = ItemBindViewBinding::inflate, itemBindCallback = { item, position -> - this.tvNum.text = formatNumber(item.equipmentBoxCode ?: "") - if (item.isBound()) { - val date = DateTimeUtils.parseDateTime(item.updateTime) - val timeInMillis = date?.time ?: 0L - val isOldTime = DateTimeUtils.isMoreThan36HoursFromNow(timeInMillis) - - this.llRoot.setBackgroundResource(R.drawable.grid_item_bind) - this.tvNum.setTextColor(resources.getColor(R.color.bind_4E535D)) - this.tvLastTime.setTextColor( - if (isOldTime) resources.getColor(R.color.bind_time_old) else resources.getColor( - R.color.bind_585868 - ) - ) - this.llOpen.setBackgroundResource(R.drawable.grid_button_bind) - this.tvOpen.setTextColor(resources.getColor(R.color.bind_F0C8B4)) - this.llBindInfo.visibility = View.VISIBLE - this.tvUnbind.visibility = View.GONE - - this.tvName.text = item.name.maskName() - this.tvLastTime.text = DateTimeUtils.getTimeAgo(date) - } else { - this.llRoot.setBackgroundResource(R.drawable.grid_item_unbind) - this.tvNum.setTextColor(resources.getColor(R.color.bind_F0C8B4)) - this.llOpen.setBackgroundResource(R.drawable.grid_button_unbind) - this.tvOpen.setTextColor(resources.getColor(R.color.unbind_32283C)) - this.llBindInfo.visibility = View.GONE - this.tvUnbind.visibility = View.VISIBLE - } - this.llRoot.setOnClickListener { - Timber.d("itemClick llRoot ${item.name}, position = $position") - if (item.isBound()) { - MainActivity.start( - requireContext(), - pageType = PageType.UNBIND_PLATE, - equipmentUserInfo = item - ) - } else { - MainActivity.start( - requireContext(), - pageType = PageType.BIND_PLATE, - equipmentUserInfo = item - ) - } - } - this.llOpen.setOnClickListener { - Timber.d("itemClick llOpen equipmentBoxCode = ${item.equipmentBoxCode}") - val equipmentBoxCode: Int = item.equipmentBoxCode?.toInt() ?: -1 - if (equipmentBoxCode < 0) { - ToastUtils.showToast("餐盘柜编号错误") - return@setOnClickListener - } - SerialApi.openPlate(equipmentBoxCode, object : SerialPortManager.SendCallback { - override fun onSuccess() { - MainActivity.start(requireContext(), pageType = PageType.PLATE_OPEN) - } - - override fun onFail(e: Exception?) { - ToastUtils.showToast("柜门打开失败") - } - }) - } + setItemInfo(item, position) } ) } - fun updateAdapter(items: List) { -// if (GlobalData.arrayCross > 2) { - var pages = if (items.size > itemsPerPage) { - items.chunked(itemsPerPage) { - convertToColumnFirst(it, 11) - } - } else { - listOf( - // 根据条件判断是否需要重新排列 -// items - convertToColumnFirst(items, 11) + private fun ItemBindViewBinding.setItemInfo( + item: EquipmentUserInfo, + position: Int + ) { + this.tvNum.text = formatNumber(item.equipmentBoxCode ?: "") + if (item.isBound()) { + val date = DateTimeUtils.parseDateTime(item.updateTime) + val timeInMillis = date?.time ?: 0L + val isOldTime = DateTimeUtils.isMoreThan36HoursFromNow(timeInMillis) + + this.llRoot.setBackgroundResource(R.drawable.grid_item_bind) + this.tvNum.setTextColor(resources.getColor(R.color.bind_4E535D)) + this.tvLastTime.setTextColor( + if (isOldTime) resources.getColor(R.color.bind_time_old) else resources.getColor( + R.color.bind_585868 + ) ) + this.llOpen.setBackgroundResource(R.drawable.grid_button_bind) + this.tvOpen.setTextColor(resources.getColor(R.color.bind_F0C8B4)) + this.llBindInfo.visibility = View.VISIBLE + this.tvUnbind.visibility = View.GONE + + this.tvName.text = item.name.maskName() + this.tvLastTime.text = DateTimeUtils.getTimeAgo(date) + } else { + this.llRoot.setBackgroundResource(R.drawable.grid_item_unbind) + this.tvNum.setTextColor(resources.getColor(R.color.bind_F0C8B4)) + this.llOpen.setBackgroundResource(R.drawable.grid_button_unbind) + this.tvOpen.setTextColor(resources.getColor(R.color.unbind_32283C)) + this.llBindInfo.visibility = View.GONE + this.tvUnbind.visibility = View.VISIBLE } - pageAdapter.updatePages(pages) -// }else{ -// -// } + this.llRoot.setOnClickListener { + Timber.d("itemClick llRoot ${item.name}, position = $position") + if (item.isBound()) { + MainActivity.start( + requireContext(), + pageType = PageType.UNBIND_PLATE, + equipmentUserInfo = item + ) + } else { + MainActivity.start( + requireContext(), + pageType = PageType.BIND_PLATE, + equipmentUserInfo = item + ) + } + } + this.llOpen.setOnClickListener { + Timber.d("itemClick llOpen equipmentBoxCode = ${item.equipmentBoxCode}") + val equipmentBoxCode: Int = item.equipmentBoxCode?.toInt() ?: -1 + if (equipmentBoxCode < 0) { + ToastUtils.showToast("餐盘柜编号错误") + return@setOnClickListener + } + showWaitingDialog("请稍等") + SerialApi.openPlate(equipmentBoxCode, object : SerialPortManager.SendCallback { + override fun onSuccess() { + hideWaitingDialog() + MainActivity.start(requireContext(), pageType = PageType.PLATE_OPEN) + } + + override fun onFail(e: Exception?) { + hideWaitingDialog() + ToastUtils.showToast("柜门打开失败") + } + }) + } + } + + fun updateAdapter(items: List) { + if (GlobalData.arrayCross > 2) { + var pages = if (items.size > itemsPerPage) { + items.chunked(itemsPerPage) { + convertToColumnFirst(it, 11) + } + } else { + listOf( + // 根据条件判断是否需要重新排列 +// items + convertToColumnFirst(items, 11) + ) + } + pageAdapter?.updatePages(pages) + } else { + itemAdapter?.updateData(items) + } + binding.mainRecyclerView.scrollToPosition(0) } /** diff --git a/app/src/main/java/com/sw/platecabinet/fragment/UnBindPlateFragment.kt b/app/src/main/java/com/sw/platecabinet/fragment/UnBindPlateFragment.kt index 84fe1d4..cdc3f81 100644 --- a/app/src/main/java/com/sw/platecabinet/fragment/UnBindPlateFragment.kt +++ b/app/src/main/java/com/sw/platecabinet/fragment/UnBindPlateFragment.kt @@ -1,7 +1,6 @@ package com.sw.platecabinet.fragment import android.os.Bundle -import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import com.sw.inbound.utils.DateTimeUtils import com.sw.plate.utils.ToastUtils @@ -9,7 +8,7 @@ import com.sw.platecabinet.databinding.FragmentUnbindPlateBinding import com.sw.platecabinet.ext.maskName import com.sw.platecabinet.ext.maskPhone import com.sw.platecabinet.model.response.EquipmentUserInfo -import com.sw.platecabinet.viewmodel.SettingViewModel +import kotlinx.coroutines.flow.drop import kotlinx.coroutines.launch /** @@ -20,7 +19,6 @@ class UnBindPlateFragment private constructor() : BaseFragment() companion object { @JvmStatic @@ -58,8 +56,9 @@ class UnBindPlateFragment private constructor() : BaseFragment? = listOf(), @SerializedName("createTime") diff --git a/app/src/main/java/com/sw/platecabinet/model/response/EquipmentUserInfo.kt b/app/src/main/java/com/sw/platecabinet/model/response/EquipmentUserInfo.kt index 67c33ef..71257a5 100644 --- a/app/src/main/java/com/sw/platecabinet/model/response/EquipmentUserInfo.kt +++ b/app/src/main/java/com/sw/platecabinet/model/response/EquipmentUserInfo.kt @@ -3,6 +3,7 @@ package com.sw.platecabinet.model.response import android.os.Parcelable import com.google.gson.annotations.SerializedName +import com.sw.platecabinet.GlobalData import kotlinx.parcelize.Parcelize /** @@ -50,7 +51,7 @@ data class EquipmentUserInfo( * 餐盘编号 */ @SerializedName("plateNumber") - val plateNumber: String? = "", + var plateNumber: String? = "", /** * * 更新时间 @@ -59,7 +60,17 @@ data class EquipmentUserInfo( val updateTime: String? = "" ) : Parcelable { + /** + * 是否已绑定 + */ fun isBound(): Boolean { return memberId != null } + + /** + * 是否是其他设备 + */ + fun isOtherEquipment(): Boolean { + return GlobalData.globalEquipmentCode != equipmentCode + } } \ No newline at end of file diff --git a/app/src/main/java/com/sw/platecabinet/model/response/SearchResult.kt b/app/src/main/java/com/sw/platecabinet/model/response/SearchResult.kt index 86cbcdd..998b152 100644 --- a/app/src/main/java/com/sw/platecabinet/model/response/SearchResult.kt +++ b/app/src/main/java/com/sw/platecabinet/model/response/SearchResult.kt @@ -5,6 +5,9 @@ import android.os.Parcelable import com.google.gson.annotations.SerializedName import kotlinx.parcelize.Parcelize +/** + * 搜索会员信息结果 + */ @Parcelize data class SearchResult( @SerializedName("endRow") diff --git a/app/src/main/java/com/sw/platecabinet/network/api/ApiService.kt b/app/src/main/java/com/sw/platecabinet/network/api/ApiService.kt index 83d9db3..585b847 100644 --- a/app/src/main/java/com/sw/platecabinet/network/api/ApiService.kt +++ b/app/src/main/java/com/sw/platecabinet/network/api/ApiService.kt @@ -19,13 +19,20 @@ interface ApiService { * 生成token */ @GET("/shuwei-zhct/scales/generateToken") - suspend fun generateToken(@Query("deviceId") deviceId: String): ApiResponse + suspend fun generateToken( + @Query("deviceId") deviceId: String, + @Query("appVersion") appVersion: String, + @Query("equipmentCode") equipmentCode: String + ): ApiResponse /** * 获取人脸数据 */ @GET("/shuwei-zhct/scales/getUserFaceCache") - suspend fun getUserFaceCache(): ApiResponse> + suspend fun getUserFaceCache( + @Query("appVersion") appVersion: String, + @Query("equipmentCode") equipmentCode: String + ): ApiResponse> /** * 餐盘用户信息获取 @@ -51,4 +58,9 @@ interface ApiService { @POST("/shuwei-user/swclientUserInfoShop/selectList") suspend fun searchUser(@Body param: SearchParam): ApiResponse + /** + * 通过餐盘号获取信息 + */ + @POST("/shuwei-zhct/swEquipmentRelUser/findByPlateNumber") + suspend fun findByPlateNumber(@Body param: BindParam): ApiResponse } \ No newline at end of file diff --git a/app/src/main/java/com/sw/platecabinet/repository/RemoteRepository.kt b/app/src/main/java/com/sw/platecabinet/repository/RemoteRepository.kt index af1ca38..4a9880e 100644 --- a/app/src/main/java/com/sw/platecabinet/repository/RemoteRepository.kt +++ b/app/src/main/java/com/sw/platecabinet/repository/RemoteRepository.kt @@ -1,5 +1,6 @@ package com.sw.platecabinet.repository +import com.sw.platecabinet.GlobalData import com.sw.platecabinet.model.request.BindParam import com.sw.platecabinet.model.request.EquipmentParam import com.sw.platecabinet.model.request.LoginParam @@ -20,14 +21,25 @@ class RemoteRepository constructor( * 生成token */ suspend fun generateToken(deviceId: String): ApiResponse { - return safeApiCall { apiService.generateToken(deviceId) } + return safeApiCall { + apiService.generateToken( + deviceId, + GlobalData.appVersion, + GlobalData.globalEquipmentCode + ) + } } /** * 获取人脸数据 */ suspend fun getUserFaceCache(): ApiResponse> { - return safeApiCall { apiService.getUserFaceCache() } + return safeApiCall { + apiService.getUserFaceCache( + GlobalData.appVersion, + GlobalData.globalEquipmentCode + ) + } } /** @@ -57,4 +69,11 @@ class RemoteRepository constructor( suspend fun searchUser(param: SearchParam): ApiResponse { return safeApiCall { apiService.searchUser(param) } } + + /** + * 通过餐盘编号获取绑定信息 + */ + suspend fun findByPlateNumber(param: BindParam): ApiResponse { + return safeApiCall { apiService.findByPlateNumber(param) } + } } \ No newline at end of file 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 ffda7d2..d8ee539 100644 --- a/app/src/main/java/com/sw/platecabinet/utils/DateTimeUtils.kt +++ b/app/src/main/java/com/sw/platecabinet/utils/DateTimeUtils.kt @@ -102,8 +102,6 @@ object DateTimeUtils { return when { hours < 1 -> "刚刚" hours < 24 -> "${hours}小时前" -// hours < 48 -> "昨天" -// hours < 72 -> "前天" else -> { val days = hours / 24 "${days}天前" 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 4157d4b..c31061b 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,7 @@ package com.sw.platecabinet.viewmodel +import com.sw.plate.utils.ToastUtils +import com.sw.platecabinet.GlobalData import com.sw.platecabinet.GlobalData.globalEquipmentCode import com.sw.platecabinet.model.ErrorInfo import com.sw.platecabinet.model.request.BindParam @@ -18,6 +20,9 @@ class SettingViewModel : BaseViewModel() { private val _searchMemberList = MutableStateFlow>(emptyList()) val searchMemberList: StateFlow> = _searchMemberList + private val _currentUserInfo = MutableStateFlow(null) + val currentUserInfo: StateFlow = _currentUserInfo + var currentPage = 1 var isLoading = false var canLoadMore = true @@ -34,10 +39,11 @@ class SettingViewModel : BaseViewModel() { */ fun getEquipmentList(equipmentCode: String = globalEquipmentCode) { launchWithLoading { - val param = EquipmentParam(equipmentCode = equipmentCode) + val param = + EquipmentParam(appVersion = GlobalData.appVersion, equipmentCode = equipmentCode) val response = repository.getEquipmentList(param) if (parseResponse(response)) { - _equipmentList.value = response.data ?: emptyList() + _equipmentList.value = response.data?.plus(response.data) ?: emptyList() } } } @@ -49,6 +55,7 @@ class SettingViewModel : BaseViewModel() { currentPage = pageNum launch { val param = SearchParam( + appVersion = GlobalData.appVersion, param = param, pageNum = pageNum, pageSize = pageSize @@ -79,6 +86,7 @@ class SettingViewModel : BaseViewModel() { ) { launchWithLoading { val bindParam = BindParam( + appVersion = GlobalData.appVersion, equipmentBoxCode = equipmentBoxCode, equipmentCode = equipmentCode, memberId = memberId, @@ -99,6 +107,7 @@ class SettingViewModel : BaseViewModel() { ) { launchWithLoading { val bindParam = BindParam( + appVersion = GlobalData.appVersion, equipmentBoxCode = equipmentBoxCode, equipmentCode = equipmentCode ) @@ -110,4 +119,30 @@ class SettingViewModel : BaseViewModel() { } } } + + fun findByPlateNumber( + plateNumber: String, + equipmentCode: String = globalEquipmentCode + ) { + launchWithLoading { + val bindParam = BindParam( + appVersion = GlobalData.appVersion, + plateNumber = plateNumber, + equipmentCode = equipmentCode + ) + val response = repository.findByPlateNumber(bindParam) + if (response.code == 200) { + if (response.data?.isOtherEquipment() == true) { + ToastUtils.showToast("") + return@launchWithLoading + } + _currentUserInfo.value = response.data + } else { + _currentUserInfo.value = EquipmentUserInfo( + plateNumber = plateNumber, + equipmentCode = equipmentCode + ) + } + } + } } \ No newline at end of file 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 bdf4a19..b1445fb 100644 --- a/app/src/main/java/com/sw/platecabinet/viewmodel/UserViewModel.kt +++ b/app/src/main/java/com/sw/platecabinet/viewmodel/UserViewModel.kt @@ -7,6 +7,7 @@ import com.sw.plate.utils.Base64 import com.sw.plate.utils.ToastUtils import com.sw.plate.utils.arcface.FaceApi import com.sw.plate.utils.arcface.facedb.entity.FaceEntity +import com.sw.platecabinet.GlobalData import com.sw.platecabinet.GlobalData.globalEquipmentCode import com.sw.platecabinet.GlobalKey import com.sw.platecabinet.model.request.LoginParam @@ -101,6 +102,7 @@ class UserViewModel : BaseViewModel() { fun loginWithPwd(equipmentCode: String = globalEquipmentCode, phone: String, password: String) { launchWithLoading { val loginParam = LoginParam( + appVersion = GlobalData.appVersion, equipmentCode = equipmentCode, phone = phone, password = password @@ -118,6 +120,7 @@ class UserViewModel : BaseViewModel() { fun getUserInfoById(equipmentCode: String = globalEquipmentCode, memberId: Int) { launchWithLoading { val loginParam = LoginParam( + appVersion = GlobalData.appVersion, equipmentCode = equipmentCode, memberId = memberId ) diff --git a/app/src/main/res/layout/activity_login_by_pwd.xml b/app/src/main/res/layout/activity_login_by_pwd.xml index 069549d..7ea4e15 100644 --- a/app/src/main/res/layout/activity_login_by_pwd.xml +++ b/app/src/main/res/layout/activity_login_by_pwd.xml @@ -86,7 +86,7 @@ android:layout_marginEnd="20dp" android:background="@android:color/transparent" android:hint="输入校验码" - android:inputType="textPassword" + android:inputType="numberPassword" android:maxLines="1" android:textColor="#F3D2BD" android:textColorHint="#7B6D6A" diff --git a/app/src/main/res/layout/fragment_bind_plate.xml b/app/src/main/res/layout/fragment_bind_plate.xml index 6b43ce2..54619b6 100644 --- a/app/src/main/res/layout/fragment_bind_plate.xml +++ b/app/src/main/res/layout/fragment_bind_plate.xml @@ -12,7 +12,7 @@ @@ -36,7 +36,7 @@ + tools:text="" /> diff --git a/app/src/main/res/layout/fragment_unbind_plate.xml b/app/src/main/res/layout/fragment_unbind_plate.xml index 8d39f69..f879658 100644 --- a/app/src/main/res/layout/fragment_unbind_plate.xml +++ b/app/src/main/res/layout/fragment_unbind_plate.xml @@ -12,7 +12,7 @@ diff --git a/lib_face/src/main/java/com/sw/plate/utils/ScanGunKeyEventHelper.java b/lib_face/src/main/java/com/sw/plate/utils/ScanGunKeyEventHelper.java index 1708f9e..218445b 100644 --- a/lib_face/src/main/java/com/sw/plate/utils/ScanGunKeyEventHelper.java +++ b/lib_face/src/main/java/com/sw/plate/utils/ScanGunKeyEventHelper.java @@ -155,12 +155,10 @@ public class ScanGunKeyEventHelper { * @return */ public boolean isScanGunEvent(KeyEvent event) { -// L.e("event===" + event.getDevice().getName() + -// "===Char===" + event.getCharacters() + -// "===Action===" + event.getAction()); -// return event.getDevice().getName().equals(mDeviceName); - - return true; + L.e("event===" + event.getDevice().getName() + + "===Char===" + event.getCharacters() + + "===Action===" + event.getAction()); + return event.getDevice().getName().equals(mDeviceName); } diff --git a/lib_face/src/main/java/com/sw/plate/utils/arcface/FaceApi.java b/lib_face/src/main/java/com/sw/plate/utils/arcface/FaceApi.java index cc1f68b..67c4d41 100644 --- a/lib_face/src/main/java/com/sw/plate/utils/arcface/FaceApi.java +++ b/lib_face/src/main/java/com/sw/plate/utils/arcface/FaceApi.java @@ -8,9 +8,9 @@ import com.sw.plate.App; import com.sw.plate.utils.L; import com.sw.plate.utils.arcface.face.model.FacePreviewInfo; import com.sw.plate.utils.arcface.facedb.FaceDatabase; +import com.sw.plate.utils.arcface.facedb.dao.FaceDao; import com.sw.plate.utils.arcface.facedb.entity.FaceEntity; -import java.util.ArrayList; import java.util.List; public class FaceApi { @@ -27,13 +27,13 @@ public class FaceApi { * @param list */ public void updateFaceData(int index, List list) { + FaceDao faceDao = FaceDatabase.getInstance(App.getContext()).faceDao(); if (index == 0) { - FaceDatabase.getInstance(App.getContext()).faceDao().deleteAll(); - FaceDatabase.getInstance(App.getContext()).faceDao().resetId(); + faceDao.deleteAll(); + faceDao.resetId(); } - List faceEntities = new ArrayList<>(); - FaceDatabase.getInstance(App.getContext()).faceDao().insert(faceEntities); + faceDao.insert(list); } /** diff --git a/lib_face/src/main/java/com/sw/plate/utils/comn/SerialApi.java b/lib_face/src/main/java/com/sw/plate/utils/comn/SerialApi.java index 61b91ea..e77d218 100644 --- a/lib_face/src/main/java/com/sw/plate/utils/comn/SerialApi.java +++ b/lib_face/src/main/java/com/sw/plate/utils/comn/SerialApi.java @@ -4,8 +4,6 @@ import static com.sw.plate.utils.CabinetLockCommand.generateOpenCommand; import android.serialport.SerialPort; -import com.sw.plate.utils.ToastUtils; - public class SerialApi { private static String path = "/dev/ttyS2"; private static int speed = 19200; @@ -15,9 +13,9 @@ public class SerialApi { public static void init() { serialPortManager = SerialPortManager.instance(); serialPort = serialPortManager.open(new Device(path, String.valueOf(speed))); - if (serialPort == null) { - ToastUtils.showToast("打开串口失败"); - } +// if (serialPort == null) { +// ToastUtils.showToast("打开串口失败"); +// } } /** @@ -31,6 +29,7 @@ public class SerialApi { init(); } if (serialPort == null) { + callback.onFail(new Exception("打开串口失败")); return; } serialPortManager.sendCommand(generateOpenCommand(boxNumber), callback);