feat(spraying): 添加喷洒完成页面和离线语音命令优化

- 实现了 SprayingFinishActivity 页面用于显示拍摄结果
- 添加了 SprayingPhotoManager 管理器用于管理喷洒照片
- 创建了 SprayingFinishVM 视图模型处理页面数据
- 设计了 activity_spraying_finish.xml 布局文件
- 优化了 OfflineCmdServiceHelper 的初始化逻辑,添加线程安全和单次初始化控制
- 实现了语音命令支持补充照片、确认提交和返回功能
- 添加了照片预览和统计功能,显示实际拍摄数量
这个提交包含在:
徐勤民 2026-04-14 22:51:57 +08:00
父节点 19997b4d54
当前提交 ff6a0c3a33
共有 5 个文件被更改,包括 251 次插入12 次删除

查看文件

@ -4,11 +4,17 @@ import com.rokid.security.glass3.open.sdk.GlassSdk
import com.rokid.security.glass3.sdk.base.data.offlineCmd.bean.VoiceAction
import com.rokid.security.glass3.sdk.base.data.offlineCmd.listener.IVoiceCallback
import com.xuqm.base.common.LogHelper
import java.util.concurrent.Executors
data class OfflineCmdBean(val text: String, val pinyin: String)
object OfflineCmdServiceHelper {
private var listenerList = mutableListOf<OfflineCmdListener>()
private val initExecutor = Executors.newSingleThreadExecutor()
@Volatile
private var initialized = false
@Volatile
private var initializing = false
private val list = mutableListOf<OfflineCmdBean>().apply {
// 首页
@ -169,11 +175,18 @@ object OfflineCmdServiceHelper {
add(OfflineCmdBean("确认", "que ren"))
//
}
@Synchronized
fun init(){
if (initialized || initializing) {
return
}
initializing = true
initExecutor.execute {
try {
val service = GlassSdk.getGlassOfflineCmdService()
if (service == null){
LogHelper.e("OfflineCmdServiceHelper","service is null")
return
return@execute
}
LogHelper.e("OfflineCmdServiceHelper","service 存在")
for (bean in list) {
@ -186,6 +199,11 @@ object OfflineCmdServiceHelper {
}
}))
}
initialized = true
} finally {
initializing = false
}
}
}
fun addOnLineListener(listener: OfflineCmdListener){
this.listenerList.add(listener)

查看文件

@ -0,0 +1,23 @@
package com.nova.brain.glass.helper
object SprayingPhotoManager {
private val photoPaths = mutableListOf<String>()
@Synchronized
fun addPhoto(path: String) {
photoPaths.add(path)
}
@Synchronized
fun getPhotoCount(): Int = photoPaths.size
@Synchronized
fun getLastPhotos(limit: Int = 3): List<String> {
return photoPaths.takeLast(limit)
}
@Synchronized
fun clear() {
photoPaths.clear()
}
}

查看文件

@ -0,0 +1,86 @@
package com.nova.brain.glass.ui
import android.app.Activity
import android.content.Intent
import android.graphics.BitmapFactory
import androidx.recyclerview.widget.RecyclerView
import com.nova.brain.glass.R
import com.nova.brain.glass.databinding.ActivitySprayingFinishBinding
import com.nova.brain.glass.helper.OfflineCmdListener
import com.nova.brain.glass.helper.OfflineCmdServiceHelper
import com.nova.brain.glass.helper.SprayingPhotoManager
import com.nova.brain.glass.model.ItemItem
import com.nova.brain.glass.viewmodel.SprayingFinishVM
import com.xuqm.base.adapter.BasePagedAdapter
import com.xuqm.base.adapter.CommonPagedAdapter
import com.xuqm.base.adapter.ViewHolder
import com.xuqm.base.ui.BaseListFormLayoutNormalActivity
class SprayingFinishActivity :
BaseListFormLayoutNormalActivity<ItemItem, SprayingFinishVM, ActivitySprayingFinishBinding>() {
override fun getLayoutId(): Int = R.layout.activity_spraying_finish
override fun fullscreen(): Boolean = true
override fun getRecyclerOrientation(): Int = RecyclerView.VERTICAL
private val listener = object : OfflineCmdListener {
override fun onOfflineCmd(cmd: String) {
runOnUiThread {
when (cmd) {
"补充照片", "继续拍摄", "继续拍照" -> finishWithAction(ACTION_SUPPLEMENT)
"确认提交", "提交", "确认" -> finishWithAction(ACTION_SUBMIT)
"取消", "返回", "退回", "退出" -> finish()
}
}
}
}
override fun initData() {
super.initData()
OfflineCmdServiceHelper.addOnLineListener(listener)
renderPhotos()
}
override fun onDestroy() {
super.onDestroy()
OfflineCmdServiceHelper.removeOnLineListener(listener)
}
private fun renderPhotos() {
val photos = SprayingPhotoManager.getLastPhotos()
binding.title.text = "本次任务应拍摄3张,实际拍摄${SprayingPhotoManager.getPhotoCount()}"
val imageViews = listOf(binding.photo1, binding.photo2, binding.photo3)
imageViews.forEach { it.setImageDrawable(null) }
photos.forEachIndexed { index, path ->
imageViews.getOrNull(index)?.setImageBitmap(BitmapFactory.decodeFile(path))
}
}
private val adapter = object : CommonPagedAdapter<ItemItem>(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) {
"补充照片" -> finishWithAction(ACTION_SUPPLEMENT)
"确认提交" -> finishWithAction(ACTION_SUBMIT)
}
}
}
}
override fun adapter(): BasePagedAdapter<ItemItem> = adapter
private fun finishWithAction(action: String) {
setResult(Activity.RESULT_OK, Intent().apply {
putExtra(EXTRA_FINISH_ACTION, action)
})
finish()
}
companion object {
const val EXTRA_FINISH_ACTION = "extra_finish_action"
const val ACTION_SUPPLEMENT = "supplement"
const val ACTION_SUBMIT = "submit"
}
}

查看文件

@ -0,0 +1,19 @@
package com.nova.brain.glass.viewmodel
import com.nova.brain.glass.model.ItemItem
import com.xuqm.base.viewmodel.BaseListViewModel
import com.xuqm.base.viewmodel.callback.Response
class SprayingFinishVM : BaseListViewModel<ItemItem>() {
override fun loadData(
page: Int,
onResponse: Response<ItemItem>
) {
onResponse.onResponse(
arrayListOf<ItemItem>().apply {
add(ItemItem("补充照片"))
add(ItemItem("确认提交"))
}
)
}
}

查看文件

@ -0,0 +1,93 @@
<?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/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="58dp"
android:gravity="center"
android:text="本次任务应拍摄3张,实际拍摄0张"
android:textColor="#4AFE59"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/photoContainer"
android:layout_width="0dp"
android:layout_height="170dp"
android:layout_marginStart="24dp"
android:layout_marginTop="28dp"
android:layout_marginEnd="24dp"
android:background="@drawable/bg_item"
android:gravity="center"
android:orientation="horizontal"
android:paddingHorizontal="18dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/title">
<ImageView
android:id="@+id/photo1"
android:layout_width="0dp"
android:layout_height="130dp"
android:layout_weight="1"
android:background="#FF3F3F3F"
android:scaleType="centerCrop" />
<ImageView
android:id="@+id/photo2"
android:layout_width="0dp"
android:layout_height="130dp"
android:layout_marginStart="14dp"
android:layout_weight="1"
android:background="#FF3F3F3F"
android:scaleType="centerCrop" />
<ImageView
android:id="@+id/photo3"
android:layout_width="0dp"
android:layout_height="130dp"
android:layout_marginStart="14dp"
android:layout_weight="1"
android:background="#FF3F3F3F"
android:scaleType="centerCrop" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/baseRecyclerView"
android:layout_width="210dp"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:overScrollMode="never"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/photoContainer" />
<TextView
android:id="@+id/hint"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="24dp"
android:gravity="center"
android:lineSpacingExtra="6dp"
android:text="单击或语音输入“补充照片”,继续拍摄图片&#10;单击或语音输入“确认提交”,提交任务结果至天镜系统"
android:textColor="#4AFE59"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/baseRecyclerView" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>