diff --git a/app/build.gradle b/app/build.gradle
index 34baf20..5136be5 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -56,7 +56,7 @@ android {
buildConfigField("String", "API_COOKIE", "\"__itrace_wid=87125211-8742-4f12-b5ca-32b9b6c860e4; locale=zh-Hans; _webtracing_device_id=t_13501877-b9b303fc-d3f52eb530e026b0\"")
buildConfigField("String", "API_ENVIRONMENT", "\"2\"")
buildConfigField("String", "API_CURRENT_USER_ID", "\"rokid\"")
- // 任务列表+审核+fo
+ // 任务列表+审核+fo+决策中心
buildConfigField("String", "API_BASE_URL_1", "\"http://10.230.4.80:12119\"")
// 意图识别
buildConfigField("String", "API_BASE_URL_2", "\"http://10.230.4.80\"")
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ff79028..39bb21f 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -78,6 +78,9 @@
+
diff --git a/app/src/main/java/com/nova/brain/glass/ui/CompositeLayupResultActivity.kt b/app/src/main/java/com/nova/brain/glass/ui/CompositeLayupResultActivity.kt
new file mode 100644
index 0000000..b46e5ce
--- /dev/null
+++ b/app/src/main/java/com/nova/brain/glass/ui/CompositeLayupResultActivity.kt
@@ -0,0 +1,319 @@
+package com.nova.brain.glass.ui
+
+import android.content.Intent
+import android.os.Environment
+import android.os.Handler
+import android.os.Looper
+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.databinding.ActivityCompositeLayupResultBinding
+import com.nova.brain.glass.helper.GlassMediaServiceHelper
+import com.nova.brain.glass.helper.OfflineCmdListener
+import com.nova.brain.glass.helper.OfflineCmdServiceHelper
+import com.nova.brain.glass.model.ItemItem
+import com.nova.brain.glass.viewmodel.CompositeLayupResultState
+import com.nova.brain.glass.viewmodel.CompositeLayupResultVM
+import com.rokid.security.glass3.sdk.base.data.media.PhotoResolution
+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.extensions.showMessage
+import com.xuqm.base.ui.BaseActivity
+import java.io.File
+import java.util.UUID
+
+class CompositeLayupResultActivity : BaseActivity() {
+
+ override fun getLayoutId(): Int = R.layout.activity_composite_layup_result
+ override fun fullscreen(): Boolean = true
+
+ private val viewModel: CompositeLayupResultVM by lazy {
+ ViewModelProvider(this)[CompositeLayupResultVM::class.java]
+ }
+
+ private enum class UiMode {
+ RECOGNIZE_SUCCESS,
+ RECOGNIZE_FAILED,
+ LAYUP_PROMPT,
+ LAYUP_WORKING,
+ CONFIRM_FINISH,
+ COMPLETE
+ }
+
+ private val taskNo: String by lazy { intent.getStringExtra(EXTRA_TASK_NO).orEmpty() }
+ private val taskName: String by lazy { intent.getStringExtra(EXTRA_TASK_NAME).orEmpty() }
+ private val partNo: String by lazy { intent.getStringExtra(EXTRA_PART_NO).orEmpty() }
+ private val photoPath: String by lazy { intent.getStringExtra(EXTRA_PHOTO_PATH).orEmpty() }
+ private val stepSeq: Int by lazy { intent.getIntExtra(EXTRA_STEP_SEQ, 1) }
+ private val totalSteps: Int by lazy { intent.getIntExtra(EXTRA_TOTAL_STEPS, 1) }
+ private val direction: String by lazy { intent.getStringExtra(EXTRA_DIRECTION).orEmpty() }
+ private val vacuum: String by lazy { intent.getStringExtra(EXTRA_VACUUM).orEmpty() }
+ private val uiHandler = Handler(Looper.getMainLooper())
+
+ private var isPhoto = false
+ private var uiMode = UiMode.RECOGNIZE_SUCCESS
+
+ private val layupWorkingRunnable: Runnable = Runnable {
+ if (uiMode == UiMode.LAYUP_PROMPT) {
+ applyMode(UiMode.LAYUP_WORKING)
+ }
+ }
+ private val completeRunnable: Runnable = Runnable {
+ if (uiMode == UiMode.COMPLETE) {
+ goTaskList()
+ }
+ }
+
+ private val listener: OfflineCmdListener = object : OfflineCmdListener {
+ override fun onOfflineCmd(cmd: String) {
+ runOnUiThread {
+ when (cmd) {
+ "退出", "返回", "退回" -> finish()
+ "重拍", "重新拍照", "重新拍摄" -> if (uiMode == UiMode.RECOGNIZE_FAILED) retake()
+ "开始铺贴" -> if (uiMode == UiMode.RECOGNIZE_SUCCESS) startLayup()
+ "确认并继续", "继续" -> if (uiMode == UiMode.CONFIRM_FINISH) confirmAndContinue()
+ "否" -> if (uiMode == UiMode.CONFIRM_FINISH) applyMode(UiMode.LAYUP_WORKING)
+ "完成", "完成任务" -> if (uiMode == UiMode.CONFIRM_FINISH && stepSeq >= totalSteps) finishCurrentTask()
+ "返回任务列表" -> if (uiMode == UiMode.COMPLETE) goTaskList()
+ }
+ }
+ }
+ }
+
+ private val photoCallbackId = UUID.randomUUID().toString()
+ private val photoCallback: PhotoFileCallback = object : PhotoFileCallback.Stub() {
+ override fun onTakePhoto(path: String) = Unit
+
+ override fun getCallbackId(): String = photoCallbackId
+
+ override fun onTakePhotoV2(path: String?, width: Int, height: Int) {
+ if (path == null) {
+ if (isPhoto) {
+ isPhoto = false
+ takePhoto()
+ } else {
+ binding.hint.text = "单击或语音输入“重新拍照”,进入下一步"
+ "相机异常".showMessage()
+ }
+ return
+ }
+ startActivity(Intent(this@CompositeLayupResultActivity, CompositeLayupResultActivity::class.java).apply {
+ putExtra(EXTRA_PHOTO_PATH, path)
+ putExtra(EXTRA_TASK_NO, taskNo)
+ putExtra(EXTRA_TASK_NAME, taskName)
+ putExtra(EXTRA_PART_NO, partNo)
+ putExtra(EXTRA_STEP_SEQ, stepSeq)
+ putExtra(EXTRA_TOTAL_STEPS, totalSteps)
+ putExtra(EXTRA_DIRECTION, direction)
+ putExtra(EXTRA_VACUUM, vacuum)
+ })
+ finish()
+ }
+ }
+
+ override fun initData() {
+ super.initData()
+ window.addFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
+ binding.baseRecyclerView.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
+ binding.baseRecyclerView.adapter = actionAdapter
+ binding.main.setOnClickListener {
+ if (uiMode == UiMode.LAYUP_WORKING) {
+ applyMode(UiMode.CONFIRM_FINISH)
+ }
+ }
+ viewModel.resultState.observe(this) { state ->
+ when (state) {
+ CompositeLayupResultState.LOADING -> {
+ binding.hint.text = "OCR识别中,请稍后..."
+ renderActions(emptyList())
+ }
+ CompositeLayupResultState.SUCCESS -> {
+ val result = viewModel.recognizeResult.value ?: return@observe
+ if (result.success) {
+ applyMode(UiMode.RECOGNIZE_SUCCESS)
+ } else {
+ applyMode(UiMode.RECOGNIZE_FAILED, result.errorMessage.ifBlank { "识别失败,请重试!" })
+ }
+ }
+ CompositeLayupResultState.FAILED -> {
+ applyMode(UiMode.RECOGNIZE_FAILED, viewModel.errorMessage.value ?: "识别失败,请重试!")
+ }
+ else -> Unit
+ }
+ }
+ if (taskNo.isNotBlank() && photoPath.isNotBlank()) {
+ viewModel.recognize(taskNo, stepSeq, photoPath)
+ }
+ }
+
+ private fun applyMode(mode: UiMode, errorMessage: String = "") {
+ uiMode = mode
+ uiHandler.removeCallbacks(layupWorkingRunnable)
+ uiHandler.removeCallbacks(completeRunnable)
+ binding.icon.visibility = android.view.View.VISIBLE
+ when (mode) {
+ UiMode.RECOGNIZE_SUCCESS -> {
+ binding.icon.setImageResource(R.mipmap.ocr_true)
+ binding.title.text = "铺贴层与零件信息正确"
+ binding.subtitle1.text = "识别到这是${stepSeq}/${totalSteps}张"
+ binding.subtitle2.text = buildInstruction()
+ renderActions(listOf("开始铺贴"))
+ binding.hint.text = "单击或语音输入“开始铺贴”,进入下一步"
+ }
+ UiMode.RECOGNIZE_FAILED -> {
+ binding.icon.setImageResource(R.mipmap.ocr_false)
+ binding.title.text = errorMessage
+ binding.subtitle1.text = "识别到这是${stepSeq}/${totalSteps}张"
+ binding.subtitle2.text = "请重新拿取"
+ renderActions(listOf("重新拍照"))
+ binding.hint.text = "单击或语音输入“重新拍照”,进入下一步"
+ }
+ UiMode.LAYUP_PROMPT -> {
+ binding.icon.visibility = android.view.View.INVISIBLE
+ binding.title.text = "请进行铺贴工作"
+ binding.subtitle1.text = "完成后,滑动唤醒眼镜!"
+ binding.subtitle2.text = ""
+ renderActions(emptyList())
+ binding.hint.text = ""
+ uiHandler.postDelayed(layupWorkingRunnable, 5000L)
+ }
+ UiMode.LAYUP_WORKING -> {
+ binding.icon.visibility = android.view.View.INVISIBLE
+ binding.title.text = "正在铺贴第${stepSeq}张"
+ binding.subtitle1.text = buildWorkingInstruction()
+ binding.subtitle2.text = ""
+ renderActions(emptyList())
+ binding.hint.text = ""
+ }
+ UiMode.CONFIRM_FINISH -> {
+ binding.icon.visibility = android.view.View.INVISIBLE
+ binding.title.text = "请确认是否已完成第${stepSeq}/${totalSteps}张铺贴"
+ binding.subtitle1.text = ""
+ binding.subtitle2.text = ""
+ renderActions(listOf(if (stepSeq >= totalSteps) "完成" else "确认并继续", "否"))
+ binding.hint.text = "点击或语音输入对应按钮,继续流程"
+ }
+ UiMode.COMPLETE -> {
+ binding.icon.setImageResource(R.mipmap.ocr_true)
+ binding.title.text = "恭喜完成当前铺贴任务!"
+ binding.subtitle1.text = "3S后自动返回铺贴任务界面"
+ binding.subtitle2.text = ""
+ renderActions(listOf("返回任务列表"))
+ binding.hint.text = ""
+ uiHandler.postDelayed(completeRunnable, 3000L)
+ }
+ }
+ }
+
+ private fun buildInstruction(): String {
+ val vacuumText = if (vacuum.isBlank()) "" else "请真空铺贴、"
+ val directionText = if (direction.isBlank()) "" else "纹理方向:$direction"
+ return (vacuumText + directionText).ifBlank { "请开始铺贴" }
+ }
+
+ private fun buildWorkingInstruction(): String {
+ val vacuumText = if (vacuum.isBlank()) "" else "请真空铺贴、"
+ val directionText = if (direction.isBlank()) "" else "纹理方向$direction、"
+ return "${vacuumText}${directionText}撕衬纸\n完成后,滑动唤醒眼镜!"
+ }
+
+ private fun renderActions(actions: List) {
+ actionAdapter.setmDatas(actions.map(::ItemItem))
+ binding.baseRecyclerView.visibility = if (actions.isEmpty()) android.view.View.INVISIBLE else android.view.View.VISIBLE
+ binding.baseRecyclerView.layoutParams = binding.baseRecyclerView.layoutParams.apply {
+ height = dpToPx(if (actions.size > 1) 152 else 88)
+ }
+ }
+
+ private fun startLayup() {
+ applyMode(UiMode.LAYUP_PROMPT)
+ }
+
+ private fun retake() {
+ binding.hint.text = "拍照中,请稍后..."
+ isPhoto = true
+ takePhoto()
+ }
+
+ private fun takePhoto() {
+ val fileName = "composite_layup_${System.currentTimeMillis()}.png"
+ val file = File(
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
+ fileName
+ )
+ GlassMediaServiceHelper.takePhoto(PhotoResolution.RESOLUTION_720P, file.absolutePath)
+ }
+
+ private fun confirmAndContinue() {
+ startActivity(Intent(this, CompositeLayupTaskActivity::class.java).apply {
+ putExtra(CompositeLayupTaskActivity.EXTRA_TASK_NO, taskNo)
+ putExtra(CompositeLayupTaskActivity.EXTRA_STEP_SEQ, stepSeq + 1)
+ })
+ finish()
+ }
+
+ private fun finishCurrentTask() {
+ applyMode(UiMode.COMPLETE)
+ }
+
+ 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() {
+ super.onResume()
+ GlassMediaServiceHelper.addPhotoCallback(photoCallback)
+ OfflineCmdServiceHelper.addListenerCompositeLayup()
+ OfflineCmdServiceHelper.addOnLineListener(listener)
+ }
+
+ override fun onPause() {
+ super.onPause()
+ uiHandler.removeCallbacks(layupWorkingRunnable)
+ uiHandler.removeCallbacks(completeRunnable)
+ GlassMediaServiceHelper.removePhotoCallback(photoCallback)
+ OfflineCmdServiceHelper.removeListenerCompositeLayup()
+ OfflineCmdServiceHelper.removeOnLineListener(listener)
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ window.clearFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
+ }
+
+ private val actionAdapter: CommonAdapter =
+ object : CommonAdapter(R.layout.item_manual_result_action) {
+ override fun convert(holder: ViewHolder, item: ItemItem, position: Int) {
+ holder.setText(R.id.text, item.text)
+ .setClickListener(R.id.actionRoot) {
+ when (item.text) {
+ "重新拍照" -> retake()
+ "开始铺贴" -> startLayup()
+ "确认并继续" -> confirmAndContinue()
+ "否" -> applyMode(UiMode.LAYUP_WORKING)
+ "完成" -> finishCurrentTask()
+ "返回任务列表" -> goTaskList()
+ }
+ }
+ }
+ }
+
+ companion object {
+ const val EXTRA_PHOTO_PATH = "extra_photo_path"
+ const val EXTRA_TASK_NO = "extra_task_no"
+ const val EXTRA_TASK_NAME = "extra_task_name"
+ const val EXTRA_PART_NO = "extra_part_no"
+ const val EXTRA_STEP_SEQ = "extra_step_seq"
+ const val EXTRA_TOTAL_STEPS = "extra_total_steps"
+ const val EXTRA_DIRECTION = "extra_direction"
+ const val EXTRA_VACUUM = "extra_vacuum"
+ }
+}
diff --git a/app/src/main/java/com/nova/brain/glass/ui/CompositeLayupTaskActivity.kt b/app/src/main/java/com/nova/brain/glass/ui/CompositeLayupTaskActivity.kt
index 0ccc085..016fe85 100644
--- a/app/src/main/java/com/nova/brain/glass/ui/CompositeLayupTaskActivity.kt
+++ b/app/src/main/java/com/nova/brain/glass/ui/CompositeLayupTaskActivity.kt
@@ -2,11 +2,6 @@ package com.nova.brain.glass.ui
import android.content.Intent
import android.os.Environment
-import android.os.Handler
-import android.os.Looper
-import android.view.View
-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.databinding.ActivityCompositeLayupTaskBinding
@@ -14,84 +9,49 @@ import com.nova.brain.glass.helper.GlassMediaServiceHelper
import com.nova.brain.glass.helper.OfflineCmdListener
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.CompositeLayupTaskVM
import com.rokid.security.glass3.sdk.base.data.media.PhotoResolution
import com.rokid.security.system.server.media.callback.PhotoFileCallback
-import com.xuqm.base.adapter.CommonAdapter
+import com.xuqm.base.adapter.BasePagedAdapter
+import com.xuqm.base.adapter.CommonPagedAdapter
import com.xuqm.base.adapter.ViewHolder
import com.xuqm.base.common.LogHelper
import com.xuqm.base.extensions.showMessage
-import com.xuqm.base.ui.BaseActivity
+import com.xuqm.base.ui.BaseListFormLayoutNormalActivity
import java.io.File
import java.util.UUID
-class CompositeLayupTaskActivity : BaseActivity() {
+class CompositeLayupTaskActivity :
+ BaseListFormLayoutNormalActivity() {
+
override fun getLayoutId(): Int = R.layout.activity_composite_layup_task
override fun fullscreen(): Boolean = true
+ override fun getRecyclerOrientation(): Int = RecyclerView.VERTICAL
- private enum class ScreenMode {
- TASK_INFO,
- CAPTURE,
- RECOGNIZE_SUCCESS,
- RECOGNIZE_FAILED,
- LAYUP_PROMPT,
- LAYUP_WORKING,
- CONFIRM_FINISH,
- COMPLETE
- }
-
- private val viewModel: CompositeLayupTaskVM by lazy {
- ViewModelProvider(this)[CompositeLayupTaskVM::class.java]
- }
private val taskNoFromIntent: String by lazy {
intent.getStringExtra(EXTRA_TASK_NO).orEmpty()
}
- private val mainHandler = Handler(Looper.getMainLooper())
+ private val stepSeqFromIntent: Int by lazy {
+ intent.getIntExtra(EXTRA_STEP_SEQ, 0)
+ }
- private var isPhotoFallback = false
+ private var isPhoto = false
private var isCaptureInFlight = false
- private var screenMode = ScreenMode.TASK_INFO
- private var autoReturned = false
+ private var hasNavigatedNextPage = 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: OfflineCmdListener = object : OfflineCmdListener {
override fun onOfflineCmd(cmd: String) {
runOnUiThread {
when (cmd) {
"退出", "返回", "退回" -> finish()
- "开始", "开始任务" -> if (screenMode == ScreenMode.TASK_INFO) startCaptureFlow()
- "重新拍照", "重拍", "重新拍摄" -> {
- if (screenMode == ScreenMode.RECOGNIZE_FAILED) startCaptureFlow()
- }
- "开始铺贴" -> if (screenMode == ScreenMode.RECOGNIZE_SUCCESS) startLayup()
- "确认并继续", "继续" -> if (screenMode == ScreenMode.CONFIRM_FINISH) confirmAndContinue()
- "否" -> if (screenMode == ScreenMode.CONFIRM_FINISH) backToWorking()
- "完成", "完成任务" -> {
- if (screenMode == ScreenMode.CONFIRM_FINISH && viewModel.canFinishAfterCurrentStep()) {
- finishCurrentTask()
- }
- }
- "返回任务列表" -> if (screenMode == ScreenMode.COMPLETE) goTaskList()
+ "开始", "拍照", "开始拍照", "开始任务" -> startCapture()
}
}
}
}
private val photoCallbackId = UUID.randomUUID().toString()
- private val photoCallback = object : PhotoFileCallback.Stub() {
+ private val photoCallback: PhotoFileCallback = object : PhotoFileCallback.Stub() {
override fun onTakePhoto(path: String) {
LogHelper.d("CompositeLayupTask onTakePhoto: $path")
}
@@ -101,293 +61,128 @@ class CompositeLayupTaskActivity : BaseActivity(R.layout.item_manual_result_action) {
- override fun convert(holder: ViewHolder, item: ItemItem, position: Int) {
- holder.setText(R.id.text, item.text)
- .setClickListener(R.id.actionRoot) {
- when (item.text) {
- "开始任务" -> startCaptureFlow()
- "重新拍照" -> startCaptureFlow()
- "开始铺贴" -> startLayup()
- "确认并继续" -> confirmAndContinue()
- "否" -> backToWorking()
- "完成" -> finishCurrentTask()
- "返回任务列表" -> goTaskList()
- }
- }
+ val taskDetail = viewModel.taskDetail.value
+ val currentDetail = viewModel.currentDetail()
+ val currentTaskStepSeq = currentDetail?.stepSeq ?: viewModel.currentStepSeq
+ isCaptureInFlight = false
+ hasNavigatedNextPage = true
+ GlassMediaServiceHelper.removePhotoCallback(photoCallback)
+ OfflineCmdServiceHelper.removeListenerCompositeLayup()
+ OfflineCmdServiceHelper.removeOnLineListener(listener)
+ startActivity(Intent(this@CompositeLayupTaskActivity, CompositeLayupResultActivity::class.java).apply {
+ putExtra(CompositeLayupResultActivity.EXTRA_PHOTO_PATH, path)
+ putExtra(CompositeLayupResultActivity.EXTRA_TASK_NO, viewModel.taskNo)
+ putExtra(CompositeLayupResultActivity.EXTRA_TASK_NAME, taskDetail?.taskName.orEmpty())
+ putExtra(CompositeLayupResultActivity.EXTRA_PART_NO, taskDetail?.partNo.orEmpty())
+ putExtra(CompositeLayupResultActivity.EXTRA_STEP_SEQ, currentTaskStepSeq)
+ putExtra(CompositeLayupResultActivity.EXTRA_TOTAL_STEPS, viewModel.totalSteps)
+ putExtra(CompositeLayupResultActivity.EXTRA_DIRECTION, currentDetail?.direction.orEmpty())
+ putExtra(CompositeLayupResultActivity.EXTRA_VACUUM, currentDetail?.vacuum.orEmpty())
+ })
+ finish()
}
}
override fun initData() {
super.initData()
window.addFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
- binding.baseRecyclerView.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
- binding.baseRecyclerView.adapter = actionAdapter
- binding.main.setOnClickListener {
- if (screenMode == ScreenMode.LAYUP_WORKING) {
- applyScreenMode(ScreenMode.CONFIRM_FINISH)
- }
- }
- observeViewModel()
- applyScreenMode(ScreenMode.TASK_INFO)
- if (taskNoFromIntent.isBlank()) {
- val message = "任务编号缺失"
- updateHint(message)
- message.showMessage()
- } else {
- viewModel.loadTaskDetail(taskNoFromIntent)
- }
- }
-
- private fun observeViewModel() {
+ binding.tvTaskHeader.text = "复材铺贴任务"
viewModel.taskDetail.observe(this) { detail ->
- if (detail != null) {
- binding.tvTaskName.text = detail.taskName.ifBlank { "铺贴任务" }
- renderTaskInfo()
- applyScreenMode(ScreenMode.TASK_INFO)
+ if (detail == null) return@observe
+ val currentDetail = viewModel.currentDetail()
+ binding.tvTaskName.text = currentDetail?.ply?.ifBlank {
+ detail.taskName.ifBlank { "铺贴任务" }
+ } ?: detail.taskName.ifBlank { "铺贴任务" }
+ binding.content1.text = "零件号:${detail.partNo.ifBlank { "-" }}"
+ binding.content2.text = "任务编号:${detail.taskNo.ifBlank { taskNoFromIntent }}"
+ binding.content3.text = "任务进度:${viewModel.currentStepSeq}/${detail.taskSteps}"
+ binding.hint.text = "单击或语音输入“开始”,进入下一步"
+ }
+ viewModel.taskDetailError.observe(this) { message ->
+ if (message.isNotBlank()) {
+ binding.hint.text = "单击或语音输入“开始”,进入下一步"
+ message.showMessage()
}
}
- viewModel.taskDetailError.observe(this) { msg ->
- if (msg.isNotBlank()) {
- updateHint(msg)
- msg.showMessage()
- }
- }
- viewModel.recognizeState.observe(this) { state ->
- when (state) {
- CompositeLayupRecognizeState.LOADING -> {
- updateHint("OCR识别中,请稍后...")
- renderActions(emptyList())
- }
- CompositeLayupRecognizeState.SUCCESS -> handleRecognizeResult()
- CompositeLayupRecognizeState.FAILED -> {
- val error = viewModel.recognizeError.value?.ifBlank { "OCR识别失败" } ?: "OCR识别失败"
- applyScreenMode(ScreenMode.RECOGNIZE_FAILED, error)
- }
- 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() {
- val result = viewModel.recognizeResult.value ?: return
- if (result.success) {
- applyScreenMode(ScreenMode.RECOGNIZE_SUCCESS)
+ if (taskNoFromIntent.isBlank()) {
+ binding.hint.text = "任务编号缺失"
} else {
- applyScreenMode(
- ScreenMode.RECOGNIZE_FAILED,
- result.errorMessage.ifBlank { "识别失败,请重试!" }
- )
- }
- viewModel.resetRecognizeState()
- }
-
- private fun startCaptureFlow() {
- if (isCaptureInFlight || taskNoFromIntent.isBlank()) {
- return
- }
- isCaptureInFlight = true
- applyScreenMode(ScreenMode.CAPTURE)
- takePhoto()
- }
-
- private fun takePhoto() {
- isPhotoFallback = true
- val fileName = "composite_layup_${System.currentTimeMillis()}.png"
- val file = File(
- Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
- fileName
- )
- GlassMediaServiceHelper.takePhoto(PhotoResolution.RESOLUTION_720P, file.absolutePath)
- }
-
- private fun startLayup() {
- applyScreenMode(ScreenMode.LAYUP_PROMPT)
- }
-
- private fun backToWorking() {
- applyScreenMode(ScreenMode.LAYUP_WORKING)
- }
-
- private fun confirmAndContinue() {
- if (viewModel.canFinishAfterCurrentStep()) {
- finishCurrentTask()
- return
- }
- if (viewModel.moveToNextStep()) {
- renderTaskInfo()
- applyScreenMode(ScreenMode.TASK_INFO)
+ viewModel.loadTaskDetail(taskNoFromIntent, stepSeqFromIntent.takeIf { it > 0 })
}
}
- private fun finishCurrentTask() {
- applyScreenMode(ScreenMode.COMPLETE)
- }
-
- private fun applyScreenMode(mode: ScreenMode, errorMessage: String = "") {
- screenMode = mode
- mainHandler.removeCallbacks(layupWorkingRunnable)
- mainHandler.removeCallbacks(completeRunnable)
- renderTaskInfo()
- 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) {
- ScreenMode.TASK_INFO -> {
- binding.captureMessage.text = "请对准下一张复材铺贴物料上的字符内容"
- binding.startIcon.setImageResource(R.mipmap.ocr_photo)
- renderActions(listOf("开始任务"))
- updateHint("单击或语音输入\"开始\",进入下一步")
- }
- ScreenMode.CAPTURE -> {
- binding.captureMessage.text = "请对准复材铺贴物料上的字符内容"
- renderActions(emptyList())
- updateHint("拍照中,请稍后...")
- }
- ScreenMode.RECOGNIZE_SUCCESS -> {
- val currentDetail = viewModel.currentDetail()
- binding.resultIcon.setImageResource(R.mipmap.ocr_true)
- binding.resultTitle.text = "铺贴层与零件信息正确"
- binding.resultSubtitle1.text = "识别到这是${viewModel.currentProgressText()}张"
- binding.resultSubtitle2.text = buildRecognizeInstruction(currentDetail?.vacuum, currentDetail?.direction)
- renderActions(listOf("开始铺贴"))
- updateHint("单击或语音输入\"开始铺贴\",进入下一步")
- }
- ScreenMode.RECOGNIZE_FAILED -> {
- binding.resultIcon.setImageResource(R.mipmap.ocr_false)
- binding.resultTitle.text = errorMessage.ifBlank { "识别失败,请重试!" }
- binding.resultSubtitle1.text = "识别到这是${viewModel.currentProgressText()}张"
- binding.resultSubtitle2.text = "请重新拿取"
- renderActions(listOf("重新拍照"))
- updateHint("单击或语音输入\"重新拍照\",进入下一步")
- }
- ScreenMode.LAYUP_PROMPT -> {
- binding.workText.text = "请进行铺贴工作\n完成后,滑动唤醒眼镜!"
- renderActions(emptyList())
- updateHint("")
- mainHandler.postDelayed(layupWorkingRunnable, 5000L)
- }
- 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) {
- 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) {
- 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() {
super.onResume()
+ hasNavigatedNextPage = false
+ isCaptureInFlight = false
GlassMediaServiceHelper.addPhotoCallback(photoCallback)
- OfflineCmdServiceHelper.addListenerCompositeLayup()
OfflineCmdServiceHelper.addOnLineListener(listener)
+ OfflineCmdServiceHelper.addListenerCompositeLayup()
}
override fun onPause() {
super.onPause()
- mainHandler.removeCallbacks(layupWorkingRunnable)
- mainHandler.removeCallbacks(completeRunnable)
- GlassMediaServiceHelper.removePhotoCallback(photoCallback)
OfflineCmdServiceHelper.removeListenerCompositeLayup()
OfflineCmdServiceHelper.removeOnLineListener(listener)
+ GlassMediaServiceHelper.removePhotoCallback(photoCallback)
}
override fun onDestroy() {
+ isCaptureInFlight = false
+ hasNavigatedNextPage = false
super.onDestroy()
window.clearFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
+ private fun startCapture() {
+ if (isCaptureInFlight || hasNavigatedNextPage || taskNoFromIntent.isBlank()) {
+ return
+ }
+ binding.hint.text = "拍照中,请稍后..."
+ isPhoto = true
+ isCaptureInFlight = true
+ takePhoto()
+ }
+
+ private fun takePhoto() {
+ val fileName = "composite_layup_${System.currentTimeMillis()}.png"
+ val publicPicturesDir =
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
+ val file = File(publicPicturesDir, fileName)
+ GlassMediaServiceHelper.takePhoto(PhotoResolution.RESOLUTION_720P, file.absolutePath)
+ }
+
+ private val adapter: BasePagedAdapter =
+ object : CommonPagedAdapter(R.layout.item_photo) {
+ override fun convert(holder: ViewHolder, item: ItemItem, position: Int) {
+ holder.setText(R.id.text, item.text)
+ .setClickListener(R.id.photo) {
+ if (item.text == "开始任务") {
+ startCapture()
+ }
+ }
+ }
+ }
+
+ override fun adapter(): BasePagedAdapter = adapter
+
companion object {
const val EXTRA_TASK_NO = "extra_task_no"
+ const val EXTRA_STEP_SEQ = "extra_step_seq"
}
}
diff --git a/app/src/main/java/com/nova/brain/glass/ui/WelcomeActivity.kt b/app/src/main/java/com/nova/brain/glass/ui/WelcomeActivity.kt
index 7f2b02d..21b9faa 100644
--- a/app/src/main/java/com/nova/brain/glass/ui/WelcomeActivity.kt
+++ b/app/src/main/java/com/nova/brain/glass/ui/WelcomeActivity.kt
@@ -56,8 +56,8 @@ class WelcomeActivity : BaseActivity() {
binding.tv.setOnClickListener {
runWithNetwork {
// triggerRecognize()
-// startActivity(Intent(this, TaskListActivity::class.java))
- startActivity(Intent(this, ChatActivity::class.java))
+ startActivity(Intent(this, TaskListActivity::class.java))
+// startActivity(Intent(this, ChatActivity::class.java))
}
}
}
diff --git a/app/src/main/java/com/nova/brain/glass/viewmodel/CompositeLayupResultVM.kt b/app/src/main/java/com/nova/brain/glass/viewmodel/CompositeLayupResultVM.kt
new file mode 100644
index 0000000..b93eaf6
--- /dev/null
+++ b/app/src/main/java/com/nova/brain/glass/viewmodel/CompositeLayupResultVM.kt
@@ -0,0 +1,68 @@
+package com.nova.brain.glass.viewmodel
+
+import androidx.lifecycle.MutableLiveData
+import com.nova.brain.glass.MyApplication
+import com.nova.brain.glass.model.ItemItem
+import com.nova.brain.glass.model.data.CompositeLayupRecognizeResult
+import com.nova.brain.glass.repository.Service4
+import com.xuqm.base.di.manager.HttpManager
+import com.xuqm.base.viewmodel.BaseListViewModel
+import com.xuqm.base.viewmodel.callback.Response
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.disposables.CompositeDisposable
+import io.reactivex.schedulers.Schedulers
+import okhttp3.MediaType.Companion.toMediaTypeOrNull
+import okhttp3.MultipartBody
+import okhttp3.RequestBody.Companion.asRequestBody
+import okhttp3.RequestBody.Companion.toRequestBody
+import java.io.File
+
+enum class CompositeLayupResultState { IDLE, LOADING, SUCCESS, FAILED }
+
+class CompositeLayupResultVM : BaseListViewModel() {
+ val resultState = MutableLiveData(CompositeLayupResultState.IDLE)
+ val recognizeResult = MutableLiveData()
+ val errorMessage = MutableLiveData()
+
+ private val disposables = CompositeDisposable()
+
+ override fun loadData(page: Int, onResponse: Response) {
+ onResponse.onResponse(arrayListOf())
+ }
+
+ fun recognize(taskNo: String, stepSeq: Int, photoPath: String) {
+ val file = File(photoPath)
+ if (!file.exists()) {
+ resultState.value = CompositeLayupResultState.FAILED
+ errorMessage.value = "图片不存在"
+ return
+ }
+ resultState.value = CompositeLayupResultState.LOADING
+ val taskNoBody = taskNo.toRequestBody("text/plain".toMediaTypeOrNull())
+ val stepSeqBody = stepSeq.toString().toRequestBody("text/plain".toMediaTypeOrNull())
+ val requestFile = file.asRequestBody("application/octet-stream".toMediaTypeOrNull())
+ val filePart = MultipartBody.Part.createFormData("file", file.name, requestFile)
+ val disposable = HttpManager.getApi(MyApplication.appComponent4, Service4::class.java)
+ .ocrRecognize(taskNoBody, stepSeqBody, filePart)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe({ response ->
+ if (response.success && response.data != null) {
+ recognizeResult.value = response.data
+ resultState.value = CompositeLayupResultState.SUCCESS
+ } else {
+ resultState.value = CompositeLayupResultState.FAILED
+ errorMessage.value = response.message.ifBlank { "OCR识别失败" }
+ }
+ }, { e ->
+ resultState.value = CompositeLayupResultState.FAILED
+ errorMessage.value = e.message ?: "OCR识别失败"
+ })
+ disposables.add(disposable)
+ }
+
+ override fun onCleared() {
+ super.onCleared()
+ disposables.clear()
+ }
+}
diff --git a/app/src/main/java/com/nova/brain/glass/viewmodel/CompositeLayupTaskVM.kt b/app/src/main/java/com/nova/brain/glass/viewmodel/CompositeLayupTaskVM.kt
index 78c9246..cd2878e 100644
--- a/app/src/main/java/com/nova/brain/glass/viewmodel/CompositeLayupTaskVM.kt
+++ b/app/src/main/java/com/nova/brain/glass/viewmodel/CompositeLayupTaskVM.kt
@@ -2,12 +2,14 @@ package com.nova.brain.glass.viewmodel
import androidx.lifecycle.MutableLiveData
import com.nova.brain.glass.MyApplication
+import com.nova.brain.glass.model.ItemItem
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.repository.Service4
import com.xuqm.base.di.manager.HttpManager
-import androidx.lifecycle.ViewModel
+import com.xuqm.base.viewmodel.BaseListViewModel
+import com.xuqm.base.viewmodel.callback.Response
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
@@ -19,7 +21,7 @@ import java.io.File
enum class CompositeLayupRecognizeState { IDLE, LOADING, SUCCESS, FAILED }
-class CompositeLayupTaskVM : ViewModel() {
+class CompositeLayupTaskVM : BaseListViewModel() {
val taskDetail = MutableLiveData()
val taskDetailError = MutableLiveData()
@@ -36,15 +38,19 @@ class CompositeLayupTaskVM : ViewModel() {
var lastRecognizeFinished: Boolean = false
private set
- fun loadTaskDetail(taskNo: String) {
+ override fun loadData(page: Int, onResponse: Response) {
+ onResponse.onResponse(arrayListOf(ItemItem("开始任务")))
+ }
+
+ fun loadTaskDetail(taskNo: String, stepOverride: Int? = null) {
this.taskNo = taskNo
val disposable = HttpManager.getApi(MyApplication.appComponent4, Service4::class.java)
.queryTask(taskNo)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ response ->
- if (response.success && response.data != null) {
- bindTaskDetail(response.data)
+ if (response.code == 200 && response.data != null) {
+ bindTaskDetail(response.data, stepOverride)
} else {
taskDetailError.value = response.message.ifBlank { "获取任务详情失败" }
}
@@ -101,10 +107,7 @@ class CompositeLayupTaskVM : ViewModel() {
fun currentDetail(): CompositeLayupDetailItem? =
taskDetail.value?.detailList
?.sortedBy { it.stepSeq }
- ?.firstOrNull { it.stepSeq == currentStepSeq }
- ?: taskDetail.value?.detailList
- ?.sortedBy { it.stepSeq }
- ?.getOrNull((currentStepSeq - 1).coerceAtLeast(0))
+ ?.getOrNull((currentStepSeq - 1).coerceAtLeast(0))
fun currentProgressText(): String = "${currentStepSeq}/${totalSteps}"
@@ -121,16 +124,13 @@ class CompositeLayupTaskVM : ViewModel() {
return true
}
- private fun bindTaskDetail(detail: CompositeLayupTaskDetail) {
+ private fun bindTaskDetail(detail: CompositeLayupTaskDetail, stepOverride: Int? = null) {
taskDetail.value = detail
this.taskNo = detail.taskNo.ifBlank { taskNo }
totalSteps = detail.taskSteps.coerceAtLeast(detail.detailList?.size ?: 1).coerceAtLeast(1)
currentStepSeq = when {
+ stepOverride != null && stepOverride > 0 -> stepOverride
detail.taskCurrentStep > 0 -> detail.taskCurrentStep
- !detail.detailList.isNullOrEmpty() -> {
- detail.detailList.firstOrNull { it.detailStatus != 9 }?.stepSeq
- ?: detail.detailList.last().stepSeq.coerceAtLeast(1)
- }
else -> 1
}.coerceIn(1, totalSteps)
lastRecognizeFinished = currentStepSeq >= totalSteps
diff --git a/app/src/main/res/layout/activity_composite_layup_result.xml b/app/src/main/res/layout/activity_composite_layup_result.xml
new file mode 100644
index 0000000..fdaad1e
--- /dev/null
+++ b/app/src/main/res/layout/activity_composite_layup_result.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_composite_layup_task.xml b/app/src/main/res/layout/activity_composite_layup_task.xml
index e47f62f..f347c15 100644
--- a/app/src/main/res/layout/activity_composite_layup_task.xml
+++ b/app/src/main/res/layout/activity_composite_layup_task.xml
@@ -55,7 +55,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginTop="4dp"
- android:text="零件号:20293989-001"
+ android:text="零件号:"
android:textColor="#ff40FF5E"
android:textSize="14sp" />
@@ -65,7 +65,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginTop="4dp"
- android:text="任务编号:PT20260422001"
+ android:text="任务编号:"
android:textColor="#ff40FF5E"
android:textSize="14sp" />
@@ -75,7 +75,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginVertical="4dp"
- android:text="任务进度:1/12"
+ android:text="任务进度:"
android:textColor="#ff40FF5E"
android:textSize="14sp" />
@@ -84,8 +84,8 @@
android:id="@+id/baseRecyclerView"
android:layout_width="0dp"
android:layout_height="88dp"
- android:layout_marginTop="24dp"
android:clipToPadding="false"
+ android:layout_marginTop="12dp"
android:overScrollMode="never"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@@ -93,238 +93,17 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/activity_spraying.xml b/app/src/main/res/layout/activity_spraying.xml
index f089fdd..ba8012f 100644
--- a/app/src/main/res/layout/activity_spraying.xml
+++ b/app/src/main/res/layout/activity_spraying.xml
@@ -103,13 +103,5 @@
android:text="单击或语音输入“开始”,进入下一步"
android:textColor="#ff40FF5E"
android:textSize="14sp"/>
-
-