feat(task): 添加复合材料铺贴任务功能

- 实现复合材料铺贴任务的完整业务流程
- 集成 OCR 识别功能用于逐层铺贴验证
- 添加任务详情查询和状态管理
- 实现拍照识别和结果处理逻辑
- 优化任务路由逻辑支持多种任务类型匹配
- 添加语音控制命令支持
- 设计专用 UI 界面和视觉反馈
- 配置网络请求接口和服务端通信
- 添加数据模型定义和状态枚举
- 实现离线命令监听和回调处理
- 完善错误处理和用户提示信息
这个提交包含在:
徐勤民 2026-04-22 16:25:36 +08:00
父节点 75e9fca5a4
当前提交 3f97ff01f4
共有 11 个文件被更改,包括 694 次插入6 次删除

查看文件

@ -0,0 +1,56 @@
package com.nova.brain.glass.model.data
data class CompositeLayupDetailItem(
val id: Long = 0,
val taskId: Long = 0,
val taskNo: String = "",
val stepSeq: Int = 0,
val detailInfo: String = "",
val detailResult: String = "",
val detailStatus: Int = 0,
val createdAt: String = "",
val updatedAt: String = "",
val createdBy: String = "",
val updatedBy: String = "",
val isDeleted: Long = 0
)
data class CompositeLayupTaskDetail(
val id: Long = 0,
val taskNo: String = "",
val taskName: String = "",
val taskType: Int = 0,
val taskStatus: Int = 0,
val taskSource: String = "",
val taskSteps: Int = 0,
val taskCurrentStep: Int = 0,
val taskFilePaths: String = "",
val centralTaskId: String = "",
val centralTaskResult: String = "",
val createdAt: String = "",
val updatedAt: String = "",
val createdBy: String = "",
val updatedBy: String = "",
val isDeleted: Long = 0,
val detailList: List<CompositeLayupDetailItem>? = null
)
data class CompositeLayupTaskDetailResponse(
val code: Int = 0,
val message: String = "",
val data: CompositeLayupTaskDetail? = null,
val success: Boolean = false
)
data class CompositeLayupRecognizeResult(
val finished: Boolean = false,
val success: Boolean = false,
val errorMessage: String = ""
)
data class CompositeLayupRecognizeResponse(
val code: Int = 0,
val message: String = "",
val data: CompositeLayupRecognizeResult? = null,
val success: Boolean = false
)

查看文件

@ -0,0 +1,26 @@
package com.nova.brain.glass.repository
import com.nova.brain.glass.model.data.CompositeLayupRecognizeResponse
import com.nova.brain.glass.model.data.CompositeLayupTaskDetailResponse
import io.reactivex.Observable
import okhttp3.MultipartBody
import okhttp3.RequestBody
import retrofit2.http.GET
import retrofit2.http.Multipart
import retrofit2.http.Part
import retrofit2.http.POST
import retrofit2.http.Query
interface Service4 {
@GET("/api/glass/workTask/queryTask")
fun queryTask(@Query("taskNo") taskNo: String): Observable<CompositeLayupTaskDetailResponse>
@Multipart
@POST("/api/glass/workTask/ocrRecognize")
fun ocrRecognize(
@Part("taskNo") taskNo: RequestBody,
@Part("stepSeq") stepSeq: RequestBody,
@Part file: MultipartBody.Part
): Observable<CompositeLayupRecognizeResponse>
}

查看文件

@ -0,0 +1,256 @@
package com.nova.brain.glass.ui
import android.os.Environment
import android.view.View
import androidx.lifecycle.ViewModelProvider
import com.nova.brain.glass.R
import com.nova.brain.glass.databinding.ActivityCompositeLayupTaskBinding
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.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.common.LogHelper
import com.xuqm.base.extensions.showMessage
import com.xuqm.base.ui.BaseActivity
import java.io.File
import java.util.UUID
class CompositeLayupTaskActivity : BaseActivity<ActivityCompositeLayupTaskBinding>() {
override fun getLayoutId(): Int = R.layout.activity_composite_layup_task
override fun fullscreen(): Boolean = true
private enum class ScreenMode {
START,
STEP_SUCCESS,
FAILED,
FINISHED
}
private val viewModel: CompositeLayupTaskVM by lazy {
ViewModelProvider(this)[CompositeLayupTaskVM::class.java]
}
private val taskNoFromIntent: String by lazy {
intent.getStringExtra(EXTRA_TASK_NO).orEmpty()
}
private var isPhotoFallback = false
private var isCaptureInFlight = false
private var screenMode = ScreenMode.START
private val listener = object : OfflineCmdListener {
override fun onOfflineCmd(cmd: String) {
runOnUiThread {
when (cmd) {
"退出", "返回", "退回" -> finish()
"开始", "开始任务" -> if (screenMode == ScreenMode.START) triggerCapture()
"下一步", "继续识别", "继续任务" -> {
if (screenMode == ScreenMode.STEP_SUCCESS) triggerCapture()
}
"重拍", "重新拍照", "重新拍摄" -> {
if (screenMode == ScreenMode.FAILED) triggerCapture()
}
"完成任务" -> if (screenMode == ScreenMode.FINISHED) finish()
}
}
}
}
private val photoCallbackId = UUID.randomUUID().toString()
private val photoCallback = object : PhotoFileCallback.Stub() {
override fun onTakePhoto(path: String) {
LogHelper.d("CompositeLayupTask onTakePhoto: $path")
}
override fun getCallbackId(): String = photoCallbackId
override fun onTakePhotoV2(path: String?, width: Int, height: Int) {
LogHelper.d("CompositeLayupTask onTakePhotoV2 width=$width height=$height path=$path")
if (path == null) {
if (isPhotoFallback) {
isPhotoFallback = false
takePhoto()
} else {
isCaptureInFlight = false
runOnUiThread {
updateHint("相机异常,请重试")
}
"相机异常".showMessage()
}
return
}
isCaptureInFlight = false
runOnUiThread {
updateHint("OCR识别中,请稍后...")
viewModel.recognize(path)
}
}
}
override fun initData() {
super.initData()
window.addFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
bindClicks()
applyScreenMode(ScreenMode.START)
observeViewModel()
if (taskNoFromIntent.isBlank()) {
updateHint("任务编号缺失")
"任务编号缺失".showMessage()
} else {
viewModel.loadTaskDetail(taskNoFromIntent)
}
}
private fun bindClicks() {
binding.btnAction.setOnClickListener {
when (screenMode) {
ScreenMode.START, ScreenMode.STEP_SUCCESS, ScreenMode.FAILED -> triggerCapture()
ScreenMode.FINISHED -> finish()
}
}
binding.btnSecondary.setOnClickListener {
if (screenMode == ScreenMode.FINISHED) {
finish()
}
}
}
private fun observeViewModel() {
viewModel.taskDetail.observe(this) { detail ->
if (detail != null) {
binding.tvTaskNo.text = "工号:${detail.taskNo.ifBlank { taskNoFromIntent }}"
binding.tvTaskName.text = detail.taskName.ifBlank { "铺贴任务列表" }
updateProgressText()
}
}
viewModel.taskDetailError.observe(this) { msg ->
if (msg.isNotBlank()) {
updateHint(msg)
msg.showMessage()
}
}
viewModel.recognizeState.observe(this) { state ->
when (state) {
CompositeLayupRecognizeState.LOADING -> {
binding.btnAction.isClickable = false
updateHint("OCR识别中,请稍后...")
}
CompositeLayupRecognizeState.SUCCESS -> {
binding.btnAction.isClickable = true
handleRecognizeResult()
}
CompositeLayupRecognizeState.FAILED -> {
binding.btnAction.isClickable = true
val error = viewModel.recognizeError.value?.ifBlank { "OCR识别失败" } ?: "OCR识别失败"
applyScreenMode(ScreenMode.FAILED, error)
}
else -> binding.btnAction.isClickable = true
}
}
}
private fun handleRecognizeResult() {
val result = viewModel.recognizeResult.value ?: return
when {
result.success && result.finished -> applyScreenMode(ScreenMode.FINISHED)
result.success -> applyScreenMode(ScreenMode.STEP_SUCCESS)
else -> applyScreenMode(ScreenMode.FAILED, result.errorMessage.ifBlank { "识别铺贴错误,请重新拍取" })
}
viewModel.resetRecognizeState()
}
private fun triggerCapture() {
if (isCaptureInFlight || taskNoFromIntent.isBlank()) {
return
}
isCaptureInFlight = true
binding.btnAction.isClickable = false
updateHint("拍照中,请稍后...")
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 updateProgressText() {
binding.tvProgress.text = "任务进度:${viewModel.currentStepSeq}/${viewModel.totalSteps}"
binding.tvStepHint.text = "识别到当前层第${viewModel.currentStepSeq}层 / 共${viewModel.totalSteps}"
}
private fun applyScreenMode(mode: ScreenMode, errorMessage: String = "") {
screenMode = mode
updateProgressText()
binding.groupStart.visibility = if (mode == ScreenMode.START) View.VISIBLE else View.GONE
binding.groupResult.visibility = if (mode == ScreenMode.START) View.GONE else View.VISIBLE
binding.btnSecondary.visibility = if (mode == ScreenMode.FINISHED) View.VISIBLE else View.GONE
when (mode) {
ScreenMode.START -> {
binding.resultIcon.setImageResource(R.mipmap.ocr_photo)
binding.resultTitle.text = "请开始本层铺贴工作"
binding.resultSubtitle.text = "语音输入“开始任务”或点击按钮后拍照识别"
binding.btnAction.text = "开工"
updateHint("左滑右滑选择任务,单击开工,开始该任务。")
}
ScreenMode.STEP_SUCCESS -> {
binding.resultIcon.setImageResource(R.mipmap.ocr_true)
binding.resultTitle.text = "识别铺贴层与料号信息正确"
binding.resultSubtitle.text = "请开始下一层铺贴工作"
binding.btnAction.text = "下一步"
updateHint("当前层识别通过,可进入下一层识别。")
}
ScreenMode.FAILED -> {
binding.resultIcon.setImageResource(R.mipmap.ocr_false)
binding.resultTitle.text = errorMessage.ifBlank { "识别铺贴错误,请重新拍取" }
binding.resultSubtitle.text = "请重新拍照识别"
binding.btnAction.text = "重拍"
updateHint(errorMessage.ifBlank { "识别失败,请重试" })
}
ScreenMode.FINISHED -> {
binding.resultIcon.setImageResource(R.mipmap.ocr_true)
binding.resultTitle.text = "恭喜完成全部铺贴逐层任务!"
binding.resultSubtitle.text = "请点击返回,回到铺贴任务列表开启下一任务"
binding.btnAction.text = "返回"
binding.btnSecondary.text = "完成任务"
updateHint("当前任务已全部完成。")
}
}
}
private fun updateHint(text: String) {
binding.hint.text = text
}
override fun onResume() {
super.onResume()
isCaptureInFlight = false
binding.btnAction.isClickable = true
GlassMediaServiceHelper.addPhotoCallback(photoCallback)
OfflineCmdServiceHelper.addListenerCompositeLayup()
OfflineCmdServiceHelper.addOnLineListener(listener)
}
override fun onPause() {
super.onPause()
GlassMediaServiceHelper.removePhotoCallback(photoCallback)
OfflineCmdServiceHelper.removeListenerCompositeLayup()
OfflineCmdServiceHelper.removeOnLineListener(listener)
}
override fun onDestroy() {
super.onDestroy()
window.clearFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
companion object {
const val EXTRA_TASK_NO = "extra_task_no"
}
}

查看文件

@ -124,13 +124,13 @@ class TaskListActivity :
} }
private fun routeToTask(item: TaskItem) { private fun routeToTask(item: TaskItem) {
when (item.taskType) { when {
"复材MES任务" -> startActivity( item.taskType == "复材MES任务" -> startActivity(
Intent(this, FoActivity::class.java) Intent(this, FoActivity::class.java)
.putExtra("aiDescription", item.displayDesc()) .putExtra("aiDescription", item.displayDesc())
.putExtra("taskType", item.taskType) .putExtra("taskType", item.taskType)
) )
"审核任务" -> startActivity( item.taskType == "审核任务" -> startActivity(
Intent(this, ReviewActivity::class.java) Intent(this, ReviewActivity::class.java)
.putExtra("taskId", item.id) .putExtra("taskId", item.id)
.putExtra("taskType", item.taskType) .putExtra("taskType", item.taskType)
@ -149,19 +149,19 @@ class TaskListActivity :
) )
) )
) )
"天镜检验任务" -> startActivity( item.taskType == "天镜检验任务" -> startActivity(
Intent(this, SprayingActivity::class.java) Intent(this, SprayingActivity::class.java)
.putExtra("taskId", item.params.firstNotBlank("taskId", "task_id", "id")) .putExtra("taskId", item.params.firstNotBlank("taskId", "task_id", "id"))
.putExtra("aiDescription", item.displayDesc()) .putExtra("aiDescription", item.displayDesc())
.putExtra("taskType", item.taskType) .putExtra("taskType", item.taskType)
) )
"检验任务", "产业大脑检验任务" -> startActivity( item.taskType == "检验任务" || item.taskType == "产业大脑检验任务" -> startActivity(
Intent(this, InspectionActivity::class.java) Intent(this, InspectionActivity::class.java)
.putExtra("glassTaskId", item.params.firstNotBlank("taskId", "task_id", "id")) .putExtra("glassTaskId", item.params.firstNotBlank("taskId", "task_id", "id"))
.putExtra("taskName", item.params?.get("taskName").orEmpty()) .putExtra("taskName", item.params?.get("taskName").orEmpty())
.putExtra("taskNumber", item.params?.get("taskNumber").orEmpty()) .putExtra("taskNumber", item.params?.get("taskNumber").orEmpty())
) )
"复材铺贴任务" -> startActivity( item.taskType.contains("复材铺贴任务") -> startActivity(
Intent(this, CompositeLayupTaskActivity::class.java) Intent(this, CompositeLayupTaskActivity::class.java)
.putExtra( .putExtra(
CompositeLayupTaskActivity.EXTRA_TASK_NO, CompositeLayupTaskActivity.EXTRA_TASK_NO,

查看文件

@ -0,0 +1,118 @@
package com.nova.brain.glass.viewmodel
import androidx.lifecycle.MutableLiveData
import com.nova.brain.glass.MyApplication
import com.nova.brain.glass.model.data.CompositeLayupRecognizeResult
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 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 CompositeLayupRecognizeState { IDLE, LOADING, SUCCESS, FAILED }
class CompositeLayupTaskVM : ViewModel() {
val taskDetail = MutableLiveData<CompositeLayupTaskDetail?>()
val taskDetailError = MutableLiveData<String>()
val recognizeState = MutableLiveData(CompositeLayupRecognizeState.IDLE)
val recognizeResult = MutableLiveData<CompositeLayupRecognizeResult?>()
val recognizeError = MutableLiveData<String>()
private val disposables = CompositeDisposable()
var taskNo: String = ""
var currentStepSeq: Int = 1
private set
var totalSteps: Int = 1
private set
fun loadTaskDetail(taskNo: String) {
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)
} else {
taskDetailError.value = response.message.ifBlank { "获取任务详情失败" }
}
}, { e ->
taskDetailError.value = e.message ?: "获取任务详情失败"
})
disposables.add(disposable)
}
fun recognize(photoPath: String) {
val file = File(photoPath)
if (!file.exists()) {
recognizeState.value = CompositeLayupRecognizeState.FAILED
recognizeError.value = "图片不存在"
return
}
if (taskNo.isBlank()) {
recognizeState.value = CompositeLayupRecognizeState.FAILED
recognizeError.value = "任务编号为空"
return
}
recognizeState.value = CompositeLayupRecognizeState.LOADING
val taskNoBody = taskNo.toRequestBody("text/plain".toMediaTypeOrNull())
val stepSeqBody = currentStepSeq.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 ->
val result = response.data
if (response.success && result != null) {
recognizeResult.value = result
recognizeState.value = CompositeLayupRecognizeState.SUCCESS
if (result.success && !result.finished) {
currentStepSeq = (currentStepSeq + 1).coerceAtMost(totalSteps.coerceAtLeast(1))
}
} else {
recognizeState.value = CompositeLayupRecognizeState.FAILED
recognizeError.value = response.message.ifBlank { "OCR识别失败" }
}
}, { e ->
recognizeState.value = CompositeLayupRecognizeState.FAILED
recognizeError.value = e.message ?: "OCR识别失败"
})
disposables.add(disposable)
}
fun resetRecognizeState() {
recognizeState.value = CompositeLayupRecognizeState.IDLE
recognizeResult.value = null
recognizeError.value = ""
}
private fun bindTaskDetail(detail: CompositeLayupTaskDetail) {
taskDetail.value = detail
this.taskNo = detail.taskNo.ifBlank { taskNo }
totalSteps = detail.taskSteps.coerceAtLeast(detail.detailList?.size ?: 1).coerceAtLeast(1)
currentStepSeq = when {
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)
}
override fun onCleared() {
super.onCleared()
disposables.clear()
}
}

查看文件

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#00000000" />
<stroke
android:width="2dp"
android:color="#40FF5E" />
<corners android:radius="12dp" />
</shape>

查看文件

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#4B7F5A" />
<corners android:radius="10dp" />
</shape>

查看文件

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#00000000" />
<stroke
android:width="2dp"
android:color="#40FF5E" />
</shape>

查看文件

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#050505" />
<stroke
android:width="2dp"
android:color="#16A5FF" />
<corners android:radius="8dp" />
</shape>

查看文件

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#4B7F5A" />
<corners android:radius="4dp" />
</shape>

查看文件

@ -0,0 +1,194 @@
<?xml version="1.0" encoding="utf-8"?>
<layout>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/app_color_black">
<TextView
android:id="@+id/tvTaskHeader"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="@drawable/bg_item"
android:gravity="center"
android:text="复材铺贴任务:逐层识别作业"
android:textColor="#ff40FF5E"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:background="@drawable/bg_task_title_selected"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvTaskHeader">
<TextView
android:id="@+id/tvTaskName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginVertical="3dp"
android:gravity="center"
android:text="铺贴任务1"
android:textColor="#ff40FF5E"
android:textSize="18sp"
android:textStyle="bold" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#ff40FF5E" />
<TextView
android:id="@+id/tvTaskNo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginTop="4dp"
android:text="任务编号PT20260422001"
android:textColor="#ff40FF5E"
android:textSize="14sp" />
<TextView
android:id="@+id/tvProgress"
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_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginVertical="4dp"
android:text="当前识别层第1层 / 共12层"
android:textColor="#ff40FF5E"
android:textSize="14sp" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/baseRecyclerView"
android:layout_width="0dp"
android:layout_height="88dp"
android:layout_marginTop="24dp"
android:clipToPadding="false"
android:overScrollMode="never"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/content" />
<TextView
android:id="@+id/hint"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:gravity="center"
android:text="单击或语音输入&quot;开始&quot;,进入下一步"
android:textColor="#ff40FF5E"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/baseRecyclerView" />
<LinearLayout
android:id="@+id/groupStart"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="12dp"
android:gravity="center"
android:orientation="vertical"
app:layout_constraintBottom_toTopOf="@+id/btnAction"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/hint">
<ImageView
android:layout_width="120dp"
android:layout_height="150dp"
android:src="@mipmap/ocr_photo" />
</LinearLayout>
<LinearLayout
android:id="@+id/groupResult"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="12dp"
android:gravity="center_horizontal"
android:orientation="vertical"
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
android:id="@+id/resultIcon"
android:layout_width="120dp"
android:layout_height="150dp"
android:src="@mipmap/ocr_true" />
<TextView
android:id="@+id/resultTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:gravity="center"
android:text="识别铺贴层与料号信息正确"
android:textColor="#ff40FF5E"
android:textSize="22sp"
android:textStyle="bold" />
<TextView
android:id="@+id/resultSubtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="center"
android:text="请开始下一层铺贴工作"
android:textColor="#ff40FF5E"
android:textSize="15sp" />
<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>
<Button
android:id="@+id/btnAction"
android:layout_width="148dp"
android:layout_height="48dp"
android:layout_marginBottom="28dp"
android:background="@drawable/bg_composite_button_outline"
android:backgroundTint="@null"
android:text="开工"
android:textAllCaps="false"
android:textColor="#ff40FF5E"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>