diff --git a/.gradle-home/daemon/7.3.3/registry.bin b/.gradle-home/daemon/7.3.3/registry.bin new file mode 100644 index 0000000..d999452 Binary files /dev/null and b/.gradle-home/daemon/7.3.3/registry.bin differ diff --git a/.gradle-home/daemon/7.3.3/registry.bin.lock b/.gradle-home/daemon/7.3.3/registry.bin.lock new file mode 100644 index 0000000..fa72fa9 Binary files /dev/null and b/.gradle-home/daemon/7.3.3/registry.bin.lock differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fda696a..3da7ec8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -78,6 +78,15 @@ + + + diff --git a/app/src/main/java/com/nova/brain/glass/helper/OfflineCmdServiceHelper.kt b/app/src/main/java/com/nova/brain/glass/helper/OfflineCmdServiceHelper.kt index 274bdf7..1c8da07 100644 --- a/app/src/main/java/com/nova/brain/glass/helper/OfflineCmdServiceHelper.kt +++ b/app/src/main/java/com/nova/brain/glass/helper/OfflineCmdServiceHelper.kt @@ -277,4 +277,48 @@ object OfflineCmdServiceHelper { )) } + // ---- Inspection 页面关键词 ---- + + fun addListenerInspectionResult(){ + registerBeans(listOf( + OfflineCmdBean("重拍照", "chong pai zhao"), + OfflineCmdBean("结束任务", "jie shu ren wu"), + OfflineCmdBean("继续任务", "ji xu ren wu") + )) + } + + fun removeListenerInspectionResult(){ + removeBeans(listOf( + OfflineCmdBean("重拍照", "chong pai zhao"), + OfflineCmdBean("结束任务", "jie shu ren wu"), + OfflineCmdBean("继续任务", "ji xu ren wu") + )) + } + + fun addListenerInspectionMissing(){ + registerBeans(listOf( + OfflineCmdBean("补充单证", "bu chong dan zheng"), + OfflineCmdBean("继续提交", "ji xu ti jiao") + )) + } + + fun removeListenerInspectionMissing(){ + removeBeans(listOf( + OfflineCmdBean("补充单证", "bu chong dan zheng"), + OfflineCmdBean("继续提交", "ji xu ti jiao") + )) + } + + fun addListenerInspectionComplete(){ + registerBeans(listOf( + OfflineCmdBean("完成任务", "wan cheng ren wu") + )) + } + + fun removeListenerInspectionComplete(){ + removeBeans(listOf( + OfflineCmdBean("完成任务", "wan cheng ren wu") + )) + } + } diff --git a/app/src/main/java/com/nova/brain/glass/ui/InspectionActivity.kt b/app/src/main/java/com/nova/brain/glass/ui/InspectionActivity.kt index 3f0eb0a..87e0dbd 100644 --- a/app/src/main/java/com/nova/brain/glass/ui/InspectionActivity.kt +++ b/app/src/main/java/com/nova/brain/glass/ui/InspectionActivity.kt @@ -1,73 +1,102 @@ package com.nova.brain.glass.ui -import androidx.recyclerview.widget.RecyclerView +import android.content.Intent +import android.os.Environment +import android.view.WindowManager import com.nova.brain.glass.R import com.nova.brain.glass.databinding.ActivityInspectionBinding +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.ItemListVM -import com.xuqm.base.adapter.BasePagedAdapter -import com.xuqm.base.adapter.CommonPagedAdapter -import com.xuqm.base.adapter.ViewHolder -import com.xuqm.base.ui.BaseListFormLayoutNormalActivity +import com.nova.brain.glass.helper.SprayingPhotoManager +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 +import kotlin.jvm.java -class InspectionActivity : BaseListFormLayoutNormalActivity() { - override fun getLayoutId(): Int =R.layout.activity_inspection +class InspectionActivity : BaseActivity() { + override fun getLayoutId(): Int = R.layout.activity_inspection override fun fullscreen(): Boolean = true - override fun getRecyclerOrientation(): Int = RecyclerView.HORIZONTAL - private val listener = object : OfflineCmdListener { override fun onOfflineCmd(cmd: String) { runOnUiThread { - when( cmd){ - "退出","返回","退回"->{ - finish() - } - "驳回","拒绝","不同意"->{ - finish() - } - "同意","通过"->{ - finish() - } + when (cmd) { + "退出", "返回", "退回" -> finish() + "开始", "开始任务", "拍照" -> startCapture() } } } } + override fun initData() { super.initData() - window.addFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + SprayingPhotoManager.clear() + binding.btnCamera.setOnClickListener { startCapture() } + binding.btnPrev.setOnClickListener { /* 切换上一任务(待实现) */ } + binding.btnNext.setOnClickListener { /* 切换下一任务(待实现) */ } + } + + private fun startCapture() { + binding.hint.text = "拍照中,请稍后..." + SprayingPhotoManager.clear() + takePhoto() + } + + private fun takePhoto() { + val fileName = "inspection_${System.currentTimeMillis()}.png" + val file = File( + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), + fileName + ) + GlassMediaServiceHelper.takePhoto(PhotoResolution.RESOLUTION_480P, file.absolutePath) + } + + private val photoCallbackId = UUID.randomUUID().toString() + + private val mPhotoFileCallback = object : PhotoFileCallback.Stub() { + override fun onTakePhoto(path: String) { + LogHelper.d("onTakePhoto: $path") + } + + override fun getCallbackId(): String = photoCallbackId + + override fun onTakePhotoV2(path: String?, width: Int, height: Int) { + if (path == null) { + runOnUiThread { + binding.hint.text = "单击或语音输入\u201C开始\u201D,进入下一步\n滑动切换上/下一个任务" + } + "相机异常".showMessage() + return + } + SprayingPhotoManager.addPhoto(path) + runOnUiThread { + startActivity(Intent(this@InspectionActivity, InspectionResultActivity::class.java).apply { + putExtra(InspectionResultActivity.EXTRA_PHOTO_PATH, path) + }) + } + } } override fun onResume() { super.onResume() - OfflineCmdServiceHelper.addListenerInspection() + GlassMediaServiceHelper.addPhotoCallback(mPhotoFileCallback) OfflineCmdServiceHelper.addOnLineListener(listener) } override fun onPause() { super.onPause() - OfflineCmdServiceHelper.removeListenerInspection() + GlassMediaServiceHelper.removePhotoCallback(mPhotoFileCallback) OfflineCmdServiceHelper.removeOnLineListener(listener) } override fun onDestroy() { super.onDestroy() - window.clearFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) } - private val adapter = object : CommonPagedAdapter(R.layout.item_item) { - override fun convert(holder: ViewHolder, item: ItemItem, position: Int) { - holder - .setText(R.id.text, item.text) - .setClickListener(R.id.text) { - when (item.text) { - "同意" -> finish() - "驳回" -> finish() - } - } - } - } - - override fun adapter(): BasePagedAdapter = adapter -} +} \ No newline at end of file diff --git a/app/src/main/java/com/nova/brain/glass/ui/InspectionCompleteActivity.kt b/app/src/main/java/com/nova/brain/glass/ui/InspectionCompleteActivity.kt new file mode 100644 index 0000000..21ba72e --- /dev/null +++ b/app/src/main/java/com/nova/brain/glass/ui/InspectionCompleteActivity.kt @@ -0,0 +1,53 @@ +package com.nova.brain.glass.ui + +import android.content.Intent +import com.nova.brain.glass.R +import com.nova.brain.glass.databinding.ActivityInspectionCompleteBinding +import com.nova.brain.glass.helper.OfflineCmdListener +import com.nova.brain.glass.helper.OfflineCmdServiceHelper +import com.xuqm.base.ui.BaseActivity + +class InspectionCompleteActivity : BaseActivity() { + override fun getLayoutId(): Int = R.layout.activity_inspection_complete + override fun fullscreen(): Boolean = true + + companion object { + const val EXTRA_PHOTO_COUNT = "extra_photo_count" + } + + private val listener = object : OfflineCmdListener { + override fun onOfflineCmd(cmd: String) { + runOnUiThread { + when (cmd) { + "退出", "返回", "退回", "完成任务" -> finishTask() + } + } + } + } + + override fun initData() { + super.initData() + val count = intent.getIntExtra(EXTRA_PHOTO_COUNT, 0) + binding.subtitle.text = "本次成功提交并归档${count}张单证!" + binding.btnFinish.setOnClickListener { finishTask() } + } + + private fun finishTask() { + startActivity(Intent(this, TaskListActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP) + }) + finish() + } + + override fun onResume() { + super.onResume() + OfflineCmdServiceHelper.addListenerInspectionComplete() + OfflineCmdServiceHelper.addOnLineListener(listener) + } + + override fun onPause() { + super.onPause() + OfflineCmdServiceHelper.removeListenerInspectionComplete() + OfflineCmdServiceHelper.removeOnLineListener(listener) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/nova/brain/glass/ui/InspectionMissingActivity.kt b/app/src/main/java/com/nova/brain/glass/ui/InspectionMissingActivity.kt new file mode 100644 index 0000000..ead6738 --- /dev/null +++ b/app/src/main/java/com/nova/brain/glass/ui/InspectionMissingActivity.kt @@ -0,0 +1,77 @@ +package com.nova.brain.glass.ui + +import android.app.Activity +import android.content.Intent +import androidx.recyclerview.widget.RecyclerView +import com.nova.brain.glass.R +import com.nova.brain.glass.databinding.ActivityInspectionMissingBinding +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.InspectionMissingVM +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 InspectionMissingActivity : + BaseListFormLayoutNormalActivity() { + + override fun getLayoutId(): Int = R.layout.activity_inspection_missing + override fun fullscreen(): Boolean = true + override fun getRecyclerOrientation(): Int = RecyclerView.VERTICAL + + companion object { + const val EXTRA_ACTION = "extra_action" + const val ACTION_SUPPLEMENT = "supplement" + const val ACTION_SUBMIT = "submit" + } + + private val listener = object : OfflineCmdListener { + override fun onOfflineCmd(cmd: String) { + runOnUiThread { + when (cmd) { + "退出", "返回", "退回" -> finish() + "补充单证" -> supplement() + "继续提交" -> submit() + } + } + } + } + + private fun supplement() { + setResult(Activity.RESULT_OK, Intent().putExtra(EXTRA_ACTION, ACTION_SUPPLEMENT)) + finish() + } + + private fun submit() { + setResult(Activity.RESULT_OK, Intent().putExtra(EXTRA_ACTION, ACTION_SUBMIT)) + finish() + } + + override fun onResume() { + super.onResume() + OfflineCmdServiceHelper.addListenerInspectionMissing() + OfflineCmdServiceHelper.addOnLineListener(listener) + } + + override fun onPause() { + super.onPause() + OfflineCmdServiceHelper.removeListenerInspectionMissing() + OfflineCmdServiceHelper.removeOnLineListener(listener) + } + + private val adapter = object : CommonPagedAdapter(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) { + "补充单证" -> supplement() + "继续提交" -> submit() + } + } + } + } + + override fun adapter(): BasePagedAdapter = adapter +} \ No newline at end of file diff --git a/app/src/main/java/com/nova/brain/glass/ui/InspectionResultActivity.kt b/app/src/main/java/com/nova/brain/glass/ui/InspectionResultActivity.kt new file mode 100644 index 0000000..58446b4 --- /dev/null +++ b/app/src/main/java/com/nova/brain/glass/ui/InspectionResultActivity.kt @@ -0,0 +1,224 @@ +package com.nova.brain.glass.ui + +import android.app.Activity +import android.content.Intent +import android.os.CountDownTimer +import android.os.Environment +import android.view.WindowManager +import androidx.activity.result.contract.ActivityResultContracts +import androidx.core.view.doOnLayout +import androidx.recyclerview.widget.RecyclerView +import com.nova.brain.glass.R +import com.nova.brain.glass.databinding.ActivityInspectionResultBinding +import com.nova.brain.glass.helper.BitmapDecodeHelper +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.helper.SprayingPhotoManager +import com.nova.brain.glass.model.ItemItem +import com.nova.brain.glass.viewmodel.InspectionResultVM +import com.rokid.security.glass3.sdk.base.data.media.PhotoResolution +import com.rokid.security.system.server.media.callback.PhotoFileCallback +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.BaseListFormLayoutNormalActivity +import java.io.File +import java.util.UUID +import java.util.concurrent.Executors +import kotlin.random.Random + +class InspectionResultActivity : + BaseListFormLayoutNormalActivity() { + + override fun getLayoutId(): Int = R.layout.activity_inspection_result + override fun fullscreen(): Boolean = true + override fun getRecyclerOrientation(): Int = RecyclerView.HORIZONTAL + + private val imageDecodeExecutor = Executors.newSingleThreadExecutor() + private var status = true // true=合格, false=不合格 + private var resultCountdown: CountDownTimer? = null + + companion object { + const val EXTRA_PHOTO_PATH = "extra_photo_path" + } + + private val missingLauncher = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode != Activity.RESULT_OK) return@registerForActivityResult + when (result.data?.getStringExtra(InspectionMissingActivity.EXTRA_ACTION)) { + InspectionMissingActivity.ACTION_SUPPLEMENT -> { + // 补充单证:重新拍照 + binding.tvTaskHeader.text = "拍照中,请稍后..." + takePhoto() + } + InspectionMissingActivity.ACTION_SUBMIT -> { + goComplete() + } + } + } + + private val listener = object : OfflineCmdListener { + override fun onOfflineCmd(cmd: String) { + runOnUiThread { + when (cmd) { + "退出", "返回", "退回" -> finish() + "重拍照" -> retake() + "继续任务" -> continueTask() + "结束任务" -> endTask() + } + } + } + } + + override fun initData() { + super.initData() + window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + val path = intent.getStringExtra(EXTRA_PHOTO_PATH) + if (path != null) { + showPhoto(path) + } + // 拍完照等待5秒后显示结果 + startResultCountdown() + } + + /** 等待 5 秒后随机显示合格/不合格结果 */ + private fun startResultCountdown(seconds: Long = 5) { + binding.tvTaskHeader.text = "识别中,请稍后..." + resultCountdown?.cancel() + resultCountdown = object : CountDownTimer(seconds * 1000, 1000) { + override fun onTick(millisUntilFinished: Long) {} + override fun onFinish() { + status = Random.nextBoolean() + runOnUiThread { updateResultDisplay() } + } + }.start() + } + + private fun updateResultDisplay() { + binding.tvTaskHeader.text = if (status) "检验结果:合格!" else "检验结果:不合格!" + binding.status.setImageResource(if (status) R.mipmap.ocr_true else R.mipmap.ocr_false) + } + + private fun retake() { + binding.tvTaskHeader.text = "重新拍照中,请稍后..." + takePhoto() + } + + private fun continueTask() { + binding.tvTaskHeader.text = "拍照中,请稍后..." + takePhoto() + } + + private fun endTask() { + // 随机进入单证缺失或单证齐全 + if (Random.nextBoolean()) { + missingLauncher.launch(Intent(this, InspectionMissingActivity::class.java)) + } else { + goComplete() + } + } + + private fun goComplete() { + val photoCount = SprayingPhotoManager.getPhotoCount() + SprayingPhotoManager.clear() + startActivity(Intent(this, InspectionCompleteActivity::class.java).apply { + putExtra(InspectionCompleteActivity.EXTRA_PHOTO_COUNT, photoCount) + }) + finish() + } + + // ---- 拍照 ---- + + private fun takePhoto() { + val fileName = "inspection_${System.currentTimeMillis()}.png" + val file = File( + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), + fileName + ) + GlassMediaServiceHelper.takePhoto(PhotoResolution.RESOLUTION_480P, file.absolutePath) + } + + private val photoCallbackId = UUID.randomUUID().toString() + + private val mPhotoFileCallback = object : PhotoFileCallback.Stub() { + override fun onTakePhoto(path: String) { + LogHelper.d("onTakePhoto: $path") + } + + override fun getCallbackId(): String = photoCallbackId + + override fun onTakePhotoV2(path: String?, width: Int, height: Int) { + if (path == null) { + runOnUiThread { + binding.tvTaskHeader.text = "相机异常,请重试" + } + "相机异常".showMessage() + return + } + SprayingPhotoManager.addPhoto(path) + runOnUiThread { + showPhoto(path) + // 重拍/继续任务:等待3秒后刷新结果 + startResultCountdown(3) + } + } + } + + private fun showPhoto(path: String) { + binding.iv.doOnLayout { + val w = it.width.coerceAtLeast(1) + val h = it.height.coerceAtLeast(1) + imageDecodeExecutor.execute { + val bitmap = BitmapDecodeHelper.decodeSampledBitmap(path, w, h) + runOnUiThread { + if (!isFinishing && !isDestroyed) { + binding.iv.setImageBitmap(bitmap) + } + } + } + } + } + + // ---- 生命周期 ---- + + override fun onResume() { + super.onResume() + OfflineCmdServiceHelper.addListenerInspectionResult() + OfflineCmdServiceHelper.addOnLineListener(listener) + GlassMediaServiceHelper.addPhotoCallback(mPhotoFileCallback) + } + + override fun onPause() { + super.onPause() + resultCountdown?.cancel() + OfflineCmdServiceHelper.removeListenerInspectionResult() + OfflineCmdServiceHelper.removeOnLineListener(listener) + GlassMediaServiceHelper.removePhotoCallback(mPhotoFileCallback) + } + + override fun onDestroy() { + super.onDestroy() + binding.iv.setImageDrawable(null) + window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + } + + // ---- Adapter ---- + + private val adapter = object : CommonPagedAdapter(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() + "结束任务" -> endTask() + "继续任务" -> continueTask() + } + } + } + } + + override fun adapter(): BasePagedAdapter = adapter +} \ No newline at end of file diff --git a/app/src/main/java/com/nova/brain/glass/viewmodel/InspectionMissingVM.kt b/app/src/main/java/com/nova/brain/glass/viewmodel/InspectionMissingVM.kt new file mode 100644 index 0000000..a09d5f7 --- /dev/null +++ b/app/src/main/java/com/nova/brain/glass/viewmodel/InspectionMissingVM.kt @@ -0,0 +1,14 @@ +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 InspectionMissingVM : BaseListViewModel() { + override fun loadData(page: Int, onResponse: Response) { + onResponse.onResponse(arrayListOf().apply { + add(ItemItem("补充单证")) + add(ItemItem("继续提交")) + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/nova/brain/glass/viewmodel/InspectionResultVM.kt b/app/src/main/java/com/nova/brain/glass/viewmodel/InspectionResultVM.kt new file mode 100644 index 0000000..b11c915 --- /dev/null +++ b/app/src/main/java/com/nova/brain/glass/viewmodel/InspectionResultVM.kt @@ -0,0 +1,15 @@ +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 InspectionResultVM : BaseListViewModel() { + override fun loadData(page: Int, onResponse: Response) { + onResponse.onResponse(arrayListOf().apply { + add(ItemItem("重拍照")) + add(ItemItem("结束任务")) + add(ItemItem("继续任务")) + }) + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_inspection.xml b/app/src/main/res/layout/activity_inspection.xml index 2742c21..6a5b812 100644 --- a/app/src/main/res/layout/activity_inspection.xml +++ b/app/src/main/res/layout/activity_inspection.xml @@ -1,6 +1,5 @@ - @@ -57,49 +57,98 @@ android:layout_marginTop="4dp" android:text="任务编号:20293989-001" android:textColor="#ff40FF5E" - android:textSize="14sp"/> + android:textSize="14sp" /> + android:textSize="14sp" /> - + android:textSize="14sp" /> - + + app:layout_constraintTop_toBottomOf="@+id/content"> + + + + + + + + + + + + + + android:textSize="14sp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/cameraRow" /> - + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_inspection_complete.xml b/app/src/main/res/layout/activity_inspection_complete.xml new file mode 100644 index 0000000..10073ed --- /dev/null +++ b/app/src/main/res/layout/activity_inspection_complete.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_inspection_missing.xml b/app/src/main/res/layout/activity_inspection_missing.xml new file mode 100644 index 0000000..94e038f --- /dev/null +++ b/app/src/main/res/layout/activity_inspection_missing.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_inspection_result.xml b/app/src/main/res/layout/activity_inspection_result.xml new file mode 100644 index 0000000..08bf8c0 --- /dev/null +++ b/app/src/main/res/layout/activity_inspection_result.xml @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file