Bläddra i källkod

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

- 实现了 SprayingFinishActivity 页面用于显示拍摄结果
- 添加了 SprayingPhotoManager 管理器用于管理喷洒照片
- 创建了 SprayingFinishVM 视图模型处理页面数据
- 设计了 activity_spraying_finish.xml 布局文件
- 优化了 OfflineCmdServiceHelper 的初始化逻辑,添加线程安全和单次初始化控制
- 实现了语音命令支持补充照片、确认提交和返回功能
- 添加了照片预览和统计功能,显示实际拍摄数量
徐勤民 2 dagar sedan
förälder
incheckning
ff6a0c3a33

+ 30 - 12
app/src/main/java/com/nova/brain/glass/helper/OfflineCmdServiceHelper.kt

@@ -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,22 +175,34 @@ object OfflineCmdServiceHelper {
         add(OfflineCmdBean("确认", "que ren"))
         //
     }
+    @Synchronized
     fun init(){
-        val service = GlassSdk.getGlassOfflineCmdService()
-        if (service == null){
-            LogHelper.e("OfflineCmdServiceHelper","service is null")
+        if (initialized || initializing) {
             return
         }
-        LogHelper.e("OfflineCmdServiceHelper","service 存在")
-        for (bean in list) {
-            service.add(VoiceAction(bean.text, bean.pinyin, object : IVoiceCallback.Stub() {
-                override fun onVoiceTriggered() {
-                    LogHelper.d("onOfflineCmd: ${bean.text}")
-                    for (l in listenerList) {
-                        l.onOfflineCmd(bean.text)
-                    }
+        initializing = true
+        initExecutor.execute {
+            try {
+                val service = GlassSdk.getGlassOfflineCmdService()
+                if (service == null){
+                    LogHelper.e("OfflineCmdServiceHelper","service is null")
+                    return@execute
                 }
-            }))
+                LogHelper.e("OfflineCmdServiceHelper","service 存在")
+                for (bean in list) {
+                    service.add(VoiceAction(bean.text, bean.pinyin, object : IVoiceCallback.Stub() {
+                        override fun onVoiceTriggered() {
+                            LogHelper.d("onOfflineCmd: ${bean.text}")
+                            for (l in listenerList) {
+                                l.onOfflineCmd(bean.text)
+                            }
+                        }
+                    }))
+                }
+                initialized = true
+            } finally {
+                initializing = false
+            }
         }
     }
     fun addOnLineListener(listener: OfflineCmdListener){

+ 23 - 0
app/src/main/java/com/nova/brain/glass/helper/SprayingPhotoManager.kt

@@ -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()
+    }
+}

+ 86 - 0
app/src/main/java/com/nova/brain/glass/ui/SprayingFinishActivity.kt

@@ -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"
+    }
+}

+ 19 - 0
app/src/main/java/com/nova/brain/glass/viewmodel/SprayingFinishVM.kt

@@ -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("确认提交"))
+            }
+        )
+    }
+}

+ 93 - 0
app/src/main/res/layout/activity_spraying_finish.xml

@@ -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>