feat(composite-layup): 重构复合材料铺贴任务界面和功能

- 更新离线语音命令列表,新增开始铺贴、确认并继续等命令
- 修改数据模型,为CompositeLayupApiData添加ply、direction、vacuum字段
- 添加partNo和partName到CompositeLayupTaskDetail数据类
- 重构CompositeLayupTaskActivity界面状态管理,新增多个屏幕模式
- 实现铺贴任务流程控制,包括拍照、识别、铺贴、确认等步骤
- 添加倒计时自动返回任务列表功能
- 优化进度显示和操作提示信息
- 修复相机异常处理逻辑
- 更新聊天界面加载动画显示逻辑
这个提交包含在:
徐勤民 2026-04-22 17:32:55 +08:00
父节点 e676e03cb3
当前提交 47fa2c4464
共有 7 个文件被更改,包括 499 次插入195 次删除

查看文件

@ -93,13 +93,15 @@ object OfflineCmdServiceHelper {
private val CMDS_COMPOSITE_LAYUP = listOf( private val CMDS_COMPOSITE_LAYUP = listOf(
OfflineCmdBean("开始", "kai shi"), OfflineCmdBean("开始", "kai shi"),
OfflineCmdBean("开始任务", "kai shi ren wu"), OfflineCmdBean("开始任务", "kai shi ren wu"),
OfflineCmdBean("下一步", "xia yi bu"),
OfflineCmdBean("继续识别", "ji xu shi bie"),
OfflineCmdBean("继续任务", "ji xu ren wu"),
OfflineCmdBean("重拍", "chong pai"), OfflineCmdBean("重拍", "chong pai"),
OfflineCmdBean("重新拍照", "chong xin pai zhao"), OfflineCmdBean("重新拍照", "chong xin pai zhao"),
OfflineCmdBean("重新拍摄", "chong xin pai she"), OfflineCmdBean("重新拍摄", "chong xin pai she"),
OfflineCmdBean("完成任务", "wan cheng ren wu") OfflineCmdBean("开始铺贴", "kai shi pu tie"),
OfflineCmdBean("确认并继续", "que ren bing ji xu"),
OfflineCmdBean("", "fou"),
OfflineCmdBean("完成", "wan cheng"),
OfflineCmdBean("完成任务", "wan cheng ren wu"),
OfflineCmdBean("返回任务列表", "fan hui ren wu lie biao")
) )
private val CMDS_WELCOME = listOf( private val CMDS_WELCOME = listOf(
OfflineCmdBean("决策中心", "jue ce zhong xin"), OfflineCmdBean("决策中心", "jue ce zhong xin"),

查看文件

@ -5,8 +5,9 @@ data class CompositeLayupDetailItem(
val taskId: Long = 0, val taskId: Long = 0,
val taskNo: String = "", val taskNo: String = "",
val stepSeq: Int = 0, val stepSeq: Int = 0,
val detailInfo: String = "", val ply: String = "",
val detailResult: String = "", val direction: String = "",
val vacuum: String = "",
val detailStatus: Int = 0, val detailStatus: Int = 0,
val createdAt: String = "", val createdAt: String = "",
val updatedAt: String = "", val updatedAt: String = "",
@ -17,6 +18,8 @@ data class CompositeLayupDetailItem(
data class CompositeLayupTaskDetail( data class CompositeLayupTaskDetail(
val id: Long = 0, val id: Long = 0,
val partNo: String = "",
val partName: String = "",
val taskNo: String = "", val taskNo: String = "",
val taskName: String = "", val taskName: String = "",
val taskType: Int = 0, val taskType: Int = 0,

查看文件

@ -41,11 +41,10 @@ class ChatActivity : BaseListFormLayoutNormalActivity<ChatItem, ChatVM, Activity
scrollToBottom() scrollToBottom()
} }
// loading=truepb 显示旋转 + 延一帧滚底(等 PagedList 提交到 adapter // loading=true显示旋转进度;loading=false显示静态占位图
// loading=falsepb 停转并隐藏
viewModel.loading.observe(this) { loading -> viewModel.loading.observe(this) { loading ->
binding.pb.visibility = if (loading) View.VISIBLE else View.INVISIBLE binding.loadingProgress.visibility = if (loading) View.VISIBLE else View.GONE
binding.pb1.visibility = if (!loading) View.VISIBLE else View.INVISIBLE binding.loadingIdleIcon.visibility = if (loading) View.GONE else View.VISIBLE
recyclerView.post { scrollToBottom() } recyclerView.post { scrollToBottom() }
} }

查看文件

@ -1,17 +1,25 @@
package com.nova.brain.glass.ui package com.nova.brain.glass.ui
import android.content.Intent
import android.os.Environment import android.os.Environment
import android.os.Handler
import android.os.Looper
import android.view.View import android.view.View
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.nova.brain.glass.R import com.nova.brain.glass.R
import com.nova.brain.glass.databinding.ActivityCompositeLayupTaskBinding import com.nova.brain.glass.databinding.ActivityCompositeLayupTaskBinding
import com.nova.brain.glass.helper.GlassMediaServiceHelper import com.nova.brain.glass.helper.GlassMediaServiceHelper
import com.nova.brain.glass.helper.OfflineCmdListener import com.nova.brain.glass.helper.OfflineCmdListener
import com.nova.brain.glass.helper.OfflineCmdServiceHelper import com.nova.brain.glass.helper.OfflineCmdServiceHelper
import com.nova.brain.glass.model.ItemItem
import com.nova.brain.glass.viewmodel.CompositeLayupRecognizeState import com.nova.brain.glass.viewmodel.CompositeLayupRecognizeState
import com.nova.brain.glass.viewmodel.CompositeLayupTaskVM import com.nova.brain.glass.viewmodel.CompositeLayupTaskVM
import com.rokid.security.glass3.sdk.base.data.media.PhotoResolution import com.rokid.security.glass3.sdk.base.data.media.PhotoResolution
import com.rokid.security.system.server.media.callback.PhotoFileCallback import com.rokid.security.system.server.media.callback.PhotoFileCallback
import com.xuqm.base.adapter.CommonAdapter
import com.xuqm.base.adapter.ViewHolder
import com.xuqm.base.common.LogHelper import com.xuqm.base.common.LogHelper
import com.xuqm.base.extensions.showMessage import com.xuqm.base.extensions.showMessage
import com.xuqm.base.ui.BaseActivity import com.xuqm.base.ui.BaseActivity
@ -23,10 +31,14 @@ class CompositeLayupTaskActivity : BaseActivity<ActivityCompositeLayupTaskBindin
override fun fullscreen(): Boolean = true override fun fullscreen(): Boolean = true
private enum class ScreenMode { private enum class ScreenMode {
START, TASK_INFO,
STEP_SUCCESS, CAPTURE,
FAILED, RECOGNIZE_SUCCESS,
FINISHED RECOGNIZE_FAILED,
LAYUP_PROMPT,
LAYUP_WORKING,
CONFIRM_FINISH,
COMPLETE
} }
private val viewModel: CompositeLayupTaskVM by lazy { private val viewModel: CompositeLayupTaskVM by lazy {
@ -35,24 +47,44 @@ class CompositeLayupTaskActivity : BaseActivity<ActivityCompositeLayupTaskBindin
private val taskNoFromIntent: String by lazy { private val taskNoFromIntent: String by lazy {
intent.getStringExtra(EXTRA_TASK_NO).orEmpty() intent.getStringExtra(EXTRA_TASK_NO).orEmpty()
} }
private val mainHandler = Handler(Looper.getMainLooper())
private var isPhotoFallback = false private var isPhotoFallback = false
private var isCaptureInFlight = false private var isCaptureInFlight = false
private var screenMode = ScreenMode.START private var screenMode = ScreenMode.TASK_INFO
private var autoReturned = false
private val layupWorkingRunnable = Runnable {
if (screenMode == ScreenMode.LAYUP_PROMPT) {
applyScreenMode(ScreenMode.LAYUP_WORKING)
}
}
private val completeRunnable = Runnable {
if (screenMode == ScreenMode.COMPLETE && !autoReturned) {
autoReturned = true
goTaskList()
}
}
private val listener = object : OfflineCmdListener { private val listener = object : OfflineCmdListener {
override fun onOfflineCmd(cmd: String) { override fun onOfflineCmd(cmd: String) {
runOnUiThread { runOnUiThread {
when (cmd) { when (cmd) {
"退出", "返回", "退回" -> finish() "退出", "返回", "退回" -> finish()
"开始", "开始任务" -> if (screenMode == ScreenMode.START) triggerCapture() "开始", "开始任务" -> if (screenMode == ScreenMode.TASK_INFO) startCaptureFlow()
"下一步", "继续识别", "继续任务" -> { "重新拍照", "重拍", "重新拍摄" -> {
if (screenMode == ScreenMode.STEP_SUCCESS) triggerCapture() if (screenMode == ScreenMode.RECOGNIZE_FAILED) startCaptureFlow()
} }
"重拍", "重新拍照", "重新拍摄" -> { "开始铺贴" -> if (screenMode == ScreenMode.RECOGNIZE_SUCCESS) startLayup()
if (screenMode == ScreenMode.FAILED) triggerCapture() "确认并继续", "继续" -> if (screenMode == ScreenMode.CONFIRM_FINISH) confirmAndContinue()
"" -> if (screenMode == ScreenMode.CONFIRM_FINISH) backToWorking()
"完成", "完成任务" -> {
if (screenMode == ScreenMode.CONFIRM_FINISH && viewModel.canFinishAfterCurrentStep()) {
finishCurrentTask()
} }
"完成任务" -> if (screenMode == ScreenMode.FINISHED) finish() }
"返回任务列表" -> if (screenMode == ScreenMode.COMPLETE) goTaskList()
} }
} }
} }
@ -75,7 +107,9 @@ class CompositeLayupTaskActivity : BaseActivity<ActivityCompositeLayupTaskBindin
} else { } else {
isCaptureInFlight = false isCaptureInFlight = false
runOnUiThread { runOnUiThread {
updateHint("相机异常,请重试") val message = "相机异常,请重试"
updateHint(message)
applyScreenMode(ScreenMode.RECOGNIZE_FAILED, message)
} }
"相机异常".showMessage() "相机异常".showMessage()
} }
@ -89,40 +123,50 @@ class CompositeLayupTaskActivity : BaseActivity<ActivityCompositeLayupTaskBindin
} }
} }
override fun initData() { private val actionAdapter = object : CommonAdapter<ItemItem>(R.layout.item_manual_result_action) {
super.initData() override fun convert(holder: ViewHolder, item: ItemItem, position: Int) {
window.addFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) holder.setText(R.id.text, item.text)
bindClicks() .setClickListener(R.id.actionRoot) {
applyScreenMode(ScreenMode.START) when (item.text) {
observeViewModel() "开始任务" -> startCaptureFlow()
if (taskNoFromIntent.isBlank()) { "重新拍照" -> startCaptureFlow()
updateHint("任务编号缺失") "开始铺贴" -> startLayup()
"任务编号缺失".showMessage() "确认并继续" -> confirmAndContinue()
} else { "" -> backToWorking()
viewModel.loadTaskDetail(taskNoFromIntent) "完成" -> finishCurrentTask()
"返回任务列表" -> goTaskList()
}
}
} }
} }
private fun bindClicks() { override fun initData() {
binding.btnAction.setOnClickListener { super.initData()
when (screenMode) { window.addFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
ScreenMode.START, ScreenMode.STEP_SUCCESS, ScreenMode.FAILED -> triggerCapture() binding.baseRecyclerView.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
ScreenMode.FINISHED -> finish() binding.baseRecyclerView.adapter = actionAdapter
binding.main.setOnClickListener {
if (screenMode == ScreenMode.LAYUP_WORKING) {
applyScreenMode(ScreenMode.CONFIRM_FINISH)
} }
} }
binding.btnSecondary.setOnClickListener { observeViewModel()
if (screenMode == ScreenMode.FINISHED) { applyScreenMode(ScreenMode.TASK_INFO)
finish() if (taskNoFromIntent.isBlank()) {
} val message = "任务编号缺失"
updateHint(message)
message.showMessage()
} else {
viewModel.loadTaskDetail(taskNoFromIntent)
} }
} }
private fun observeViewModel() { private fun observeViewModel() {
viewModel.taskDetail.observe(this) { detail -> viewModel.taskDetail.observe(this) { detail ->
if (detail != null) { if (detail != null) {
binding.tvTaskNo.text = "工号:${detail.taskNo.ifBlank { taskNoFromIntent }}" binding.tvTaskName.text = detail.taskName.ifBlank { "铺贴任务" }
binding.tvTaskName.text = detail.taskName.ifBlank { "铺贴任务列表" } renderTaskInfo()
updateProgressText() applyScreenMode(ScreenMode.TASK_INFO)
} }
} }
viewModel.taskDetailError.observe(this) { msg -> viewModel.taskDetailError.observe(this) { msg ->
@ -134,40 +178,45 @@ class CompositeLayupTaskActivity : BaseActivity<ActivityCompositeLayupTaskBindin
viewModel.recognizeState.observe(this) { state -> viewModel.recognizeState.observe(this) { state ->
when (state) { when (state) {
CompositeLayupRecognizeState.LOADING -> { CompositeLayupRecognizeState.LOADING -> {
binding.btnAction.isClickable = false
updateHint("OCR识别中,请稍后...") updateHint("OCR识别中,请稍后...")
renderActions(emptyList())
} }
CompositeLayupRecognizeState.SUCCESS -> { CompositeLayupRecognizeState.SUCCESS -> handleRecognizeResult()
binding.btnAction.isClickable = true
handleRecognizeResult()
}
CompositeLayupRecognizeState.FAILED -> { CompositeLayupRecognizeState.FAILED -> {
binding.btnAction.isClickable = true
val error = viewModel.recognizeError.value?.ifBlank { "OCR识别失败" } ?: "OCR识别失败" val error = viewModel.recognizeError.value?.ifBlank { "OCR识别失败" } ?: "OCR识别失败"
applyScreenMode(ScreenMode.FAILED, error) applyScreenMode(ScreenMode.RECOGNIZE_FAILED, error)
} }
else -> binding.btnAction.isClickable = true else -> Unit
} }
} }
} }
private fun renderTaskInfo() {
val detail = viewModel.taskDetail.value ?: return
binding.content1.text = "零件号:${detail.partNo.ifBlank { "-" }}"
binding.content2.text = "任务编号:${detail.taskNo.ifBlank { taskNoFromIntent }}"
binding.content3.text = "任务进度:${viewModel.currentProgressText()}"
}
private fun handleRecognizeResult() { private fun handleRecognizeResult() {
val result = viewModel.recognizeResult.value ?: return val result = viewModel.recognizeResult.value ?: return
when { if (result.success) {
result.success && result.finished -> applyScreenMode(ScreenMode.FINISHED) applyScreenMode(ScreenMode.RECOGNIZE_SUCCESS)
result.success -> applyScreenMode(ScreenMode.STEP_SUCCESS) } else {
else -> applyScreenMode(ScreenMode.FAILED, result.errorMessage.ifBlank { "识别铺贴错误,请重新拍取" }) applyScreenMode(
ScreenMode.RECOGNIZE_FAILED,
result.errorMessage.ifBlank { "识别失败,请重试!" }
)
} }
viewModel.resetRecognizeState() viewModel.resetRecognizeState()
} }
private fun triggerCapture() { private fun startCaptureFlow() {
if (isCaptureInFlight || taskNoFromIntent.isBlank()) { if (isCaptureInFlight || taskNoFromIntent.isBlank()) {
return return
} }
isCaptureInFlight = true isCaptureInFlight = true
binding.btnAction.isClickable = false applyScreenMode(ScreenMode.CAPTURE)
updateHint("拍照中,请稍后...")
takePhoto() takePhoto()
} }
@ -181,58 +230,144 @@ class CompositeLayupTaskActivity : BaseActivity<ActivityCompositeLayupTaskBindin
GlassMediaServiceHelper.takePhoto(PhotoResolution.RESOLUTION_720P, file.absolutePath) GlassMediaServiceHelper.takePhoto(PhotoResolution.RESOLUTION_720P, file.absolutePath)
} }
private fun updateProgressText() { private fun startLayup() {
binding.tvProgress.text = "任务进度:${viewModel.currentStepSeq}/${viewModel.totalSteps}" applyScreenMode(ScreenMode.LAYUP_PROMPT)
binding.tvStepHint.text = "识别到当前层第${viewModel.currentStepSeq}层 / 共${viewModel.totalSteps}" }
private fun backToWorking() {
applyScreenMode(ScreenMode.LAYUP_WORKING)
}
private fun confirmAndContinue() {
if (viewModel.canFinishAfterCurrentStep()) {
finishCurrentTask()
return
}
if (viewModel.moveToNextStep()) {
renderTaskInfo()
applyScreenMode(ScreenMode.TASK_INFO)
}
}
private fun finishCurrentTask() {
applyScreenMode(ScreenMode.COMPLETE)
} }
private fun applyScreenMode(mode: ScreenMode, errorMessage: String = "") { private fun applyScreenMode(mode: ScreenMode, errorMessage: String = "") {
screenMode = mode screenMode = mode
updateProgressText() mainHandler.removeCallbacks(layupWorkingRunnable)
binding.groupStart.visibility = if (mode == ScreenMode.START) View.VISIBLE else View.GONE mainHandler.removeCallbacks(completeRunnable)
binding.groupResult.visibility = if (mode == ScreenMode.START) View.GONE else View.VISIBLE renderTaskInfo()
binding.btnSecondary.visibility = if (mode == ScreenMode.FINISHED) View.VISIBLE else View.GONE binding.groupStart.visibility = if (mode == ScreenMode.TASK_INFO) View.VISIBLE else View.GONE
binding.groupCapture.visibility = if (mode == ScreenMode.CAPTURE) View.VISIBLE else View.GONE
binding.groupResult.visibility =
if (mode == ScreenMode.RECOGNIZE_SUCCESS || mode == ScreenMode.RECOGNIZE_FAILED) View.VISIBLE else View.GONE
binding.groupWork.visibility =
if (mode == ScreenMode.LAYUP_PROMPT || mode == ScreenMode.LAYUP_WORKING) View.VISIBLE else View.GONE
binding.groupConfirm.visibility = if (mode == ScreenMode.CONFIRM_FINISH) View.VISIBLE else View.GONE
binding.groupComplete.visibility = if (mode == ScreenMode.COMPLETE) View.VISIBLE else View.GONE
when (mode) { when (mode) {
ScreenMode.START -> { ScreenMode.TASK_INFO -> {
binding.resultIcon.setImageResource(R.mipmap.ocr_photo) binding.captureMessage.text = "请对准下一张复材铺贴物料上的字符内容"
binding.resultTitle.text = "请开始本层铺贴工作" binding.startIcon.setImageResource(R.mipmap.ocr_photo)
binding.resultSubtitle.text = "语音输入“开始任务”或点击按钮后拍照识别" renderActions(listOf("开始任务"))
binding.btnAction.text = "开工" updateHint("单击或语音输入\"开始\",进入下一步")
updateHint("左滑右滑选择任务,单击开工,开始该任务。")
} }
ScreenMode.STEP_SUCCESS -> { ScreenMode.CAPTURE -> {
binding.captureMessage.text = "请对准复材铺贴物料上的字符内容"
renderActions(emptyList())
updateHint("拍照中,请稍后...")
}
ScreenMode.RECOGNIZE_SUCCESS -> {
val currentDetail = viewModel.currentDetail()
binding.resultIcon.setImageResource(R.mipmap.ocr_true) binding.resultIcon.setImageResource(R.mipmap.ocr_true)
binding.resultTitle.text = "识别铺贴层与料号信息正确" binding.resultTitle.text = "铺贴层与零件信息正确"
binding.resultSubtitle.text = "请开始下一层铺贴工作" binding.resultSubtitle1.text = "识别到这是${viewModel.currentProgressText()}"
binding.btnAction.text = "下一步" binding.resultSubtitle2.text = buildRecognizeInstruction(currentDetail?.vacuum, currentDetail?.direction)
updateHint("当前层识别通过,可进入下一层识别。") renderActions(listOf("开始铺贴"))
updateHint("单击或语音输入\"开始铺贴\",进入下一步")
} }
ScreenMode.FAILED -> { ScreenMode.RECOGNIZE_FAILED -> {
binding.resultIcon.setImageResource(R.mipmap.ocr_false) binding.resultIcon.setImageResource(R.mipmap.ocr_false)
binding.resultTitle.text = errorMessage.ifBlank { "识别铺贴错误,请重新拍取" } binding.resultTitle.text = errorMessage.ifBlank { "识别失败,请重试!" }
binding.resultSubtitle.text = "请重新拍照识别" binding.resultSubtitle1.text = "识别到这是${viewModel.currentProgressText()}"
binding.btnAction.text = "重拍" binding.resultSubtitle2.text = "请重新拿取"
updateHint(errorMessage.ifBlank { "识别失败,请重试" }) renderActions(listOf("重新拍照"))
updateHint("单击或语音输入\"重新拍照\",进入下一步")
} }
ScreenMode.FINISHED -> { ScreenMode.LAYUP_PROMPT -> {
binding.resultIcon.setImageResource(R.mipmap.ocr_true) binding.workText.text = "请进行铺贴工作\n完成后,滑动唤醒眼镜!"
binding.resultTitle.text = "恭喜完成全部铺贴逐层任务!" renderActions(emptyList())
binding.resultSubtitle.text = "请点击返回,回到铺贴任务列表开启下一任务" updateHint("")
binding.btnAction.text = "返回" mainHandler.postDelayed(layupWorkingRunnable, 5000L)
binding.btnSecondary.text = "完成任务" }
updateHint("当前任务已全部完成。") ScreenMode.LAYUP_WORKING -> {
binding.workText.text = buildWorkingInstruction()
renderActions(emptyList())
updateHint("")
}
ScreenMode.CONFIRM_FINISH -> {
binding.confirmTitle.text = "请确认是否已完成第${viewModel.currentProgressText()}张铺贴"
val primary = if (viewModel.canFinishAfterCurrentStep()) "完成" else "确认并继续"
renderActions(listOf(primary, ""))
updateHint("点击或语音输入对应按钮,继续流程")
}
ScreenMode.COMPLETE -> {
autoReturned = false
binding.completeSubtitle.text = "3S后自动返回铺贴任务界面"
renderActions(listOf("返回任务列表"))
updateHint("")
mainHandler.postDelayed(completeRunnable, 3000L)
} }
} }
} }
private fun renderActions(actions: List<String>) {
actionAdapter.setmDatas(actions.map(::ItemItem))
binding.baseRecyclerView.layoutParams = binding.baseRecyclerView.layoutParams.apply {
height = dpToPx(
when {
actions.isEmpty() -> 88
actions.size == 1 -> 88
else -> 152
}
)
}
binding.baseRecyclerView.visibility = if (actions.isEmpty()) View.INVISIBLE else View.VISIBLE
}
private fun updateHint(text: String) { private fun updateHint(text: String) {
binding.hint.text = text binding.hint.text = text
binding.hint.visibility = if (text.isBlank()) View.INVISIBLE else View.VISIBLE
} }
private fun buildRecognizeInstruction(vacuum: String?, direction: String?): String {
val vacuumText = if (vacuum.isNullOrBlank()) "" else "请真空铺贴、"
val directionText = if (direction.isNullOrBlank()) "" else "纹理方向$direction"
return (vacuumText + directionText).ifBlank { "请开始铺贴" }
}
private fun buildWorkingInstruction(): String {
val detail = viewModel.currentDetail()
val stepText = "正在铺贴第${viewModel.currentStepSeq}"
val vacuumText = if (detail?.vacuum.isNullOrBlank()) "" else "请真空铺贴、"
val directionText = if (detail?.direction.isNullOrBlank()) "" else "纹理方向${detail?.direction}"
return "$stepText\n${vacuumText}${directionText}撕衬纸\n完成后,滑动唤醒眼镜!"
}
private fun goTaskList() {
startActivity(Intent(this, TaskListActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
})
finish()
}
private fun dpToPx(dp: Int): Int =
(dp * resources.displayMetrics.density).toInt()
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
isCaptureInFlight = false
binding.btnAction.isClickable = true
GlassMediaServiceHelper.addPhotoCallback(photoCallback) GlassMediaServiceHelper.addPhotoCallback(photoCallback)
OfflineCmdServiceHelper.addListenerCompositeLayup() OfflineCmdServiceHelper.addListenerCompositeLayup()
OfflineCmdServiceHelper.addOnLineListener(listener) OfflineCmdServiceHelper.addOnLineListener(listener)
@ -240,6 +375,8 @@ class CompositeLayupTaskActivity : BaseActivity<ActivityCompositeLayupTaskBindin
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
mainHandler.removeCallbacks(layupWorkingRunnable)
mainHandler.removeCallbacks(completeRunnable)
GlassMediaServiceHelper.removePhotoCallback(photoCallback) GlassMediaServiceHelper.removePhotoCallback(photoCallback)
OfflineCmdServiceHelper.removeListenerCompositeLayup() OfflineCmdServiceHelper.removeListenerCompositeLayup()
OfflineCmdServiceHelper.removeOnLineListener(listener) OfflineCmdServiceHelper.removeOnLineListener(listener)

查看文件

@ -3,6 +3,7 @@ package com.nova.brain.glass.viewmodel
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import com.nova.brain.glass.MyApplication import com.nova.brain.glass.MyApplication
import com.nova.brain.glass.model.data.CompositeLayupRecognizeResult import com.nova.brain.glass.model.data.CompositeLayupRecognizeResult
import com.nova.brain.glass.model.data.CompositeLayupDetailItem
import com.nova.brain.glass.model.data.CompositeLayupTaskDetail import com.nova.brain.glass.model.data.CompositeLayupTaskDetail
import com.nova.brain.glass.repository.Service4 import com.nova.brain.glass.repository.Service4
import com.xuqm.base.di.manager.HttpManager import com.xuqm.base.di.manager.HttpManager
@ -32,6 +33,8 @@ class CompositeLayupTaskVM : ViewModel() {
private set private set
var totalSteps: Int = 1 var totalSteps: Int = 1
private set private set
var lastRecognizeFinished: Boolean = false
private set
fun loadTaskDetail(taskNo: String) { fun loadTaskDetail(taskNo: String) {
this.taskNo = taskNo this.taskNo = taskNo
@ -76,10 +79,8 @@ class CompositeLayupTaskVM : ViewModel() {
val result = response.data val result = response.data
if (response.success && result != null) { if (response.success && result != null) {
recognizeResult.value = result recognizeResult.value = result
lastRecognizeFinished = result.finished
recognizeState.value = CompositeLayupRecognizeState.SUCCESS recognizeState.value = CompositeLayupRecognizeState.SUCCESS
if (result.success && !result.finished) {
currentStepSeq = (currentStepSeq + 1).coerceAtMost(totalSteps.coerceAtLeast(1))
}
} else { } else {
recognizeState.value = CompositeLayupRecognizeState.FAILED recognizeState.value = CompositeLayupRecognizeState.FAILED
recognizeError.value = response.message.ifBlank { "OCR识别失败" } recognizeError.value = response.message.ifBlank { "OCR识别失败" }
@ -97,6 +98,29 @@ class CompositeLayupTaskVM : ViewModel() {
recognizeError.value = "" recognizeError.value = ""
} }
fun currentDetail(): CompositeLayupDetailItem? =
taskDetail.value?.detailList
?.sortedBy { it.stepSeq }
?.firstOrNull { it.stepSeq == currentStepSeq }
?: taskDetail.value?.detailList
?.sortedBy { it.stepSeq }
?.getOrNull((currentStepSeq - 1).coerceAtLeast(0))
fun currentProgressText(): String = "${currentStepSeq}/${totalSteps}"
fun canFinishAfterCurrentStep(): Boolean =
lastRecognizeFinished || currentStepSeq >= totalSteps
fun moveToNextStep(): Boolean {
if (currentStepSeq >= totalSteps) {
return false
}
currentStepSeq += 1
lastRecognizeFinished = false
resetRecognizeState()
return true
}
private fun bindTaskDetail(detail: CompositeLayupTaskDetail) { private fun bindTaskDetail(detail: CompositeLayupTaskDetail) {
taskDetail.value = detail taskDetail.value = detail
this.taskNo = detail.taskNo.ifBlank { taskNo } this.taskNo = detail.taskNo.ifBlank { taskNo }
@ -109,6 +133,7 @@ class CompositeLayupTaskVM : ViewModel() {
} }
else -> 1 else -> 1
}.coerceIn(1, totalSteps) }.coerceIn(1, totalSteps)
lastRecognizeFinished = currentStepSeq >= totalSteps
} }
override fun onCleared() { override fun onCleared() {

查看文件

@ -13,22 +13,24 @@
android:layout_height="400dp" android:layout_height="400dp"
android:overScrollMode="never" /> android:overScrollMode="never" />
<ProgressBar <ProgressBar
android:id="@+id/pb" android:id="@+id/loadingProgress"
app:layout_constraintTop_toBottomOf="@+id/baseRecyclerView" app:layout_constraintTop_toBottomOf="@+id/baseRecyclerView"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@+id/baseRecyclerView" app:layout_constraintBottom_toBottomOf="@+id/baseRecyclerView"
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="40dp" android:layout_height="40dp"
android:visibility="gone"
android:indeterminateDrawable="@drawable/load_progress" /> android:indeterminateDrawable="@drawable/load_progress" />
<ImageView <ImageView
android:id="@+id/pb1" android:id="@+id/loadingIdleIcon"
app:layout_constraintTop_toBottomOf="@+id/baseRecyclerView" app:layout_constraintTop_toBottomOf="@+id/baseRecyclerView"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@+id/baseRecyclerView" app:layout_constraintBottom_toBottomOf="@+id/baseRecyclerView"
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="40dp" android:layout_height="40dp"
android:visibility="visible"
android:src="@drawable/loading"/> android:src="@drawable/loading"/>
<TextView <TextView

查看文件

@ -2,6 +2,7 @@
<layout> <layout>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/app_color_black"> android:background="@color/app_color_black">
@ -13,7 +14,7 @@
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:background="@drawable/bg_item" android:background="@drawable/bg_item"
android:gravity="center" android:gravity="center"
android:text="复材铺贴任务:逐层识别作业" android:text="复材铺贴任务"
android:textColor="#ff40FF5E" android:textColor="#ff40FF5E"
android:textSize="20sp" android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
@ -49,7 +50,17 @@
android:background="#ff40FF5E" /> android:background="#ff40FF5E" />
<TextView <TextView
android:id="@+id/tvTaskNo" android:id="@+id/content1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginTop="4dp"
android:text="零件号20293989-001"
android:textColor="#ff40FF5E"
android:textSize="14sp" />
<TextView
android:id="@+id/content2"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="15dp" android:layout_marginStart="15dp"
@ -59,22 +70,12 @@
android:textSize="14sp" /> android:textSize="14sp" />
<TextView <TextView
android:id="@+id/tvProgress" android:id="@+id/content3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginTop="4dp"
android:text="任务进度1/12"
android:textColor="#ff40FF5E"
android:textSize="14sp" />
<TextView
android:id="@+id/tvStepHint"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="15dp" android:layout_marginStart="15dp"
android:layout_marginVertical="4dp" android:layout_marginVertical="4dp"
android:text="当前识别层第1层 / 共12层" android:text="任务进度1/12"
android:textColor="#ff40FF5E" android:textColor="#ff40FF5E"
android:textSize="14sp" /> android:textSize="14sp" />
</LinearLayout> </LinearLayout>
@ -96,99 +97,234 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:gravity="center" android:gravity="center"
android:text="单击或语音输入&quot;开始&quot;,进入下一步" android:text="单击或语音输入&#34;开始&#34;,进入下一步"
android:textColor="#ff40FF5E" android:textColor="#ff40FF5E"
android:textSize="14sp" android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/baseRecyclerView" /> app:layout_constraintTop_toBottomOf="@+id/baseRecyclerView" />
<LinearLayout <FrameLayout
android:id="@+id/groupStart"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_marginStart="24dp"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:gravity="center" android:layout_marginEnd="24dp"
android:orientation="vertical" android:layout_marginBottom="12dp"
app:layout_constraintBottom_toTopOf="@+id/btnAction" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/hint"> app:layout_constraintTop_toBottomOf="@id/hint">
<LinearLayout
android:id="@+id/groupStart"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<FrameLayout
android:layout_width="128dp"
android:layout_height="128dp"
android:background="@drawable/bg_composite_circle">
<ImageView <ImageView
android:layout_width="120dp" android:id="@+id/startIcon"
android:layout_height="150dp" android:layout_width="52dp"
android:layout_height="52dp"
android:layout_gravity="center"
android:src="@mipmap/ocr_photo" /> android:src="@mipmap/ocr_photo" />
</FrameLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/groupCapture"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone">
<FrameLayout
android:layout_width="320dp"
android:layout_height="220dp">
<View
android:layout_width="60dp"
android:layout_height="4dp"
android:background="#ff40FF5E" />
<View
android:layout_width="4dp"
android:layout_height="60dp"
android:background="#ff40FF5E" />
<View
android:layout_width="60dp"
android:layout_height="4dp"
android:layout_gravity="top|end"
android:background="#ff40FF5E" />
<View
android:layout_width="4dp"
android:layout_height="60dp"
android:layout_gravity="top|end"
android:background="#ff40FF5E" />
<View
android:layout_width="60dp"
android:layout_height="4dp"
android:layout_gravity="bottom|start"
android:background="#ff40FF5E" />
<View
android:layout_width="4dp"
android:layout_height="60dp"
android:layout_gravity="bottom|start"
android:background="#ff40FF5E" />
<View
android:layout_width="60dp"
android:layout_height="4dp"
android:layout_gravity="bottom|end"
android:background="#ff40FF5E" />
<View
android:layout_width="4dp"
android:layout_height="60dp"
android:layout_gravity="bottom|end"
android:background="#ff40FF5E" />
<TextView
android:id="@+id/captureMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:text="请对准复材铺贴物料上的字符内容"
android:textColor="#ff40FF5E"
android:textSize="16sp" />
</FrameLayout>
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/groupResult" android:id="@+id/groupResult"
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="match_parent"
android:layout_marginTop="12dp" android:gravity="center"
android:gravity="center_horizontal"
android:orientation="vertical" android:orientation="vertical"
android:visibility="gone" android:visibility="gone">
app:layout_constraintBottom_toTopOf="@+id/btnAction"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/hint">
<ImageView <ImageView
android:id="@+id/resultIcon" android:id="@+id/resultIcon"
android:layout_width="120dp" android:layout_width="120dp"
android:layout_height="150dp" android:layout_height="120dp"
android:src="@mipmap/ocr_true" /> android:src="@mipmap/ocr_true" />
<TextView <TextView
android:id="@+id/resultTitle" android:id="@+id/resultTitle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="12dp" android:layout_marginTop="20dp"
android:gravity="center" android:gravity="center"
android:text="识别铺贴层与料号信息正确" android:text="铺贴层与料号信息正确"
android:textColor="#ff40FF5E" android:textColor="#ff40FF5E"
android:textSize="22sp" android:textSize="22sp"
android:textStyle="bold" /> android:textStyle="bold" />
<TextView <TextView
android:id="@+id/resultSubtitle" android:id="@+id/resultSubtitle1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:gravity="center"
android:text="识别到这是1/12张"
android:textColor="#ff40FF5E"
android:textSize="18sp" />
<TextView
android:id="@+id/resultSubtitle2"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:gravity="center" android:gravity="center"
android:text="请开始下一层铺贴工作" android:text="请真空铺贴、纹理方向+90"
android:textColor="#ff40FF5E" android:textColor="#ff40FF5E"
android:textSize="15sp" /> android:textSize="18sp" />
<Button
android:id="@+id/btnSecondary"
android:layout_width="138dp"
android:layout_height="48dp"
android:layout_marginTop="20dp"
android:background="@drawable/bg_composite_button_solid"
android:backgroundTint="@null"
android:text="完成任务"
android:textAllCaps="false"
android:textColor="#ff40FF5E"
android:textSize="20sp"
android:visibility="gone" />
</LinearLayout> </LinearLayout>
<Button <LinearLayout
android:id="@+id/btnAction" android:id="@+id/groupWork"
android:layout_width="148dp" android:layout_width="match_parent"
android:layout_height="48dp" android:layout_height="match_parent"
android:layout_marginBottom="28dp" android:gravity="center"
android:background="@drawable/bg_composite_button_outline" android:orientation="vertical"
android:backgroundTint="@null" android:visibility="gone">
android:text="开工"
android:textAllCaps="false" <TextView
android:id="@+id/workText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:lineSpacingExtra="6dp"
android:text="请进行铺贴工作"
android:textColor="#ff40FF5E" android:textColor="#ff40FF5E"
android:textSize="20sp" android:textSize="22sp" />
app:layout_constraintBottom_toBottomOf="parent" </LinearLayout>
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" /> <LinearLayout
android:id="@+id/groupConfirm"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone">
<TextView
android:id="@+id/confirmTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="请确认是否已完成第3/12张铺贴"
android:textColor="#ff40FF5E"
android:textSize="22sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/groupComplete"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone">
<ImageView
android:layout_width="120dp"
android:layout_height="120dp"
android:src="@mipmap/ocr_true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:text="恭喜完成当前铺贴任务!"
android:textColor="#ff40FF5E"
android:textSize="22sp"
android:textStyle="bold" />
<TextView
android:id="@+id/completeSubtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:gravity="center"
android:text="3S后自动返回铺贴任务界面"
android:textColor="#ff40FF5E"
android:textSize="18sp" />
</LinearLayout>
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</layout> </layout>