feat(ocr): 添加喷涂任务OCR识别功能

- 新增BatchUploadResponse、RecognizeByPathRequest、OcrResultData和RecognizeByPathResponse数据类
- 在Service中添加batchUpload接口返回类型修改和recognizeByPath新接口
- 修改SprayingOCRActivity从观察uploadSuccessPath改为观察ocrResult并传递识别结果参数
- 移除SprayingResultActivity中图片解码相关代码和依赖,新增OCR结果显示逻辑
- 修改SprayingOCRVM中上传成功后的处理流程,增加OCR识别步骤和结果处理
- 在MyApplication中新增appComponent3用于OCR服务组件管理
这个提交包含在:
徐勤民 2026-04-21 14:40:49 +08:00
父节点 3dfd8494e9
当前提交 931dd43fe5
共有 6 个文件被更改,包括 87 次插入36 次删除

查看文件

@ -22,6 +22,7 @@ public class MyApplication extends App {
public static AppComponent appComponent1; public static AppComponent appComponent1;
// 喷涂 // 喷涂
public static AppComponent appComponent2; public static AppComponent appComponent2;
public static AppComponent appComponent3;
@Override @Override
public void onCreate() { public void onCreate() {
@ -29,9 +30,11 @@ public class MyApplication extends App {
appComponent = HttpManager.getAppComponent(baseUrl, new HeaderInterceptor(getApplicationContext())); appComponent = HttpManager.getAppComponent(baseUrl, new HeaderInterceptor(getApplicationContext()));
appComponent1 = HttpManager.getAppComponent("https://22v1322u01.vicp.fun", new HeaderInterceptor(getApplicationContext())); appComponent1 = HttpManager.getAppComponent("https://22v1322u01.vicp.fun", new HeaderInterceptor(getApplicationContext()));
appComponent2 = HttpManager.getAppComponent("https://22v1322u01.vicp.fun", new HeaderInterceptor(getApplicationContext())); appComponent2 = HttpManager.getAppComponent("https://22v1322u01.vicp.fun", new HeaderInterceptor(getApplicationContext()));
appComponent3 = HttpManager.getAppComponent("https://22v1322u01.vicp.fun", new HeaderInterceptor(getApplicationContext()));
// appComponent1 = HttpManager.getAppComponent("http://192.168.6.20:12119", new HeaderInterceptor(getApplicationContext())); // appComponent1 = HttpManager.getAppComponent("http://192.168.6.20:12119", new HeaderInterceptor(getApplicationContext()));
// appComponent2 = HttpManager.getAppComponent("http://192.168.6.156:10085", new HeaderInterceptor(getApplicationContext())); // appComponent2 = HttpManager.getAppComponent("http://192.168.6.156:10085", new HeaderInterceptor(getApplicationContext()));
// appComponent3 = HttpManager.getAppComponent("http://192.168.22.125:8820/", new HeaderInterceptor(getApplicationContext()));
initSdk(); initSdk();

查看文件

@ -10,3 +10,25 @@ data class SubmitTaskResponse(
val data: Any? = null val data: Any? = null
) )
data class BatchUploadResponse(
val code: Int,
val message: String,
val data: String? = null
)
data class RecognizeByPathRequest(
val filePath: String
)
data class OcrResultData(
val judgment: String = "",
val one: String = "",
val two: String = ""
)
data class RecognizeByPathResponse(
val code: Int,
val message: String,
val data: OcrResultData? = null
)

查看文件

@ -8,6 +8,10 @@ import com.nova.brain.glass.model.data.BackToRequest
import com.nova.brain.glass.model.data.GetTaskInfoRequest import com.nova.brain.glass.model.data.GetTaskInfoRequest
import com.nova.brain.glass.model.data.GetTaskInfoResponse import com.nova.brain.glass.model.data.GetTaskInfoResponse
import com.nova.brain.glass.model.data.RecognizeData import com.nova.brain.glass.model.data.RecognizeData
import com.nova.brain.glass.model.data.BatchUploadResponse
import com.nova.brain.glass.model.data.OcrResultData
import com.nova.brain.glass.model.data.RecognizeByPathRequest
import com.nova.brain.glass.model.data.RecognizeByPathResponse
import com.nova.brain.glass.model.data.SubmitTaskRequest import com.nova.brain.glass.model.data.SubmitTaskRequest
import com.nova.brain.glass.model.data.SubmitTaskResponse import com.nova.brain.glass.model.data.SubmitTaskResponse
import com.nova.brain.glass.model.data.PushToNextData import com.nova.brain.glass.model.data.PushToNextData
@ -70,7 +74,10 @@ interface Service {
@Multipart @Multipart
@POST("/skyscopicsecond-api/api/aiGlasses/batchUpload") @POST("/skyscopicsecond-api/api/aiGlasses/batchUpload")
fun batchUpload(@Part multipartFile: MultipartBody.Part): Observable<ResponseBody> fun batchUpload(@Part multipartFile: MultipartBody.Part): Observable<BatchUploadResponse>
@POST("/skyscopicsecond-api/api/aiGlasses/recognizeByPath")
fun recognizeByPath(@Body body: RecognizeByPathRequest): Observable<RecognizeByPathResponse>
@POST("/skyscopicsecond-api/api/aiGlasses/submitTask") @POST("/skyscopicsecond-api/api/aiGlasses/submitTask")
fun submitTask(@Body body: SubmitTaskRequest): Observable<SubmitTaskResponse> fun submitTask(@Body body: SubmitTaskRequest): Observable<SubmitTaskResponse>

查看文件

@ -115,12 +115,14 @@ class SprayingOCRActivity :
message.showMessage() message.showMessage()
} }
} }
viewModel.uploadSuccessPath.observe(this) { path -> viewModel.ocrResult.observe(this) { result ->
if (path.isNullOrBlank()) return@observe if (result == null) return@observe
viewModel.clearUploadSuccessPath() viewModel.clearOcrResult()
startActivity(Intent(this@SprayingOCRActivity, SprayingResultActivity::class.java).apply { startActivity(Intent(this@SprayingOCRActivity, SprayingResultActivity::class.java).apply {
putExtra("path", path)
putExtra("taskId", taskId) putExtra("taskId", taskId)
putExtra("judgment", result.judgment)
putExtra("one", result.one)
putExtra("two", result.two)
}) })
finish() finish()
} }

查看文件

@ -10,11 +10,9 @@ import android.os.Looper
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.WindowManager import android.view.WindowManager
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.doOnLayout
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.nova.brain.glass.R import com.nova.brain.glass.R
import com.nova.brain.glass.databinding.ActivitySprayingResultBinding import com.nova.brain.glass.databinding.ActivitySprayingResultBinding
import com.nova.brain.glass.helper.BitmapDecodeHelper
import com.nova.brain.glass.helper.GlassMediaServiceHelper import com.nova.brain.glass.helper.GlassMediaServiceHelper
import com.nova.brain.glass.helper.OfflineCmdListener import com.nova.brain.glass.helper.OfflineCmdListener
import com.nova.brain.glass.helper.OfflineCmdServiceHelper import com.nova.brain.glass.helper.OfflineCmdServiceHelper
@ -31,7 +29,6 @@ import com.xuqm.base.common.LogHelper
import com.xuqm.base.extensions.showMessage import com.xuqm.base.extensions.showMessage
import com.xuqm.base.ui.BaseListFormLayoutNormalActivity import com.xuqm.base.ui.BaseListFormLayoutNormalActivity
import java.io.File import java.io.File
import java.util.concurrent.Executors
import java.util.UUID import java.util.UUID
class SprayingResultActivity : class SprayingResultActivity :
@ -45,7 +42,8 @@ class SprayingResultActivity :
.orEmpty() .orEmpty()
.ifBlank { "1493291302287048704" } .ifBlank { "1493291302287048704" }
} }
private val imageDecodeExecutor = Executors.newSingleThreadExecutor() private var ocrOne: String = ""
private var ocrTwo: String = ""
private var status = true private var status = true
private var successDialog: AlertDialog? = null private var successDialog: AlertDialog? = null
private val uiHandler = Handler(Looper.getMainLooper()) private val uiHandler = Handler(Looper.getMainLooper())
@ -191,15 +189,19 @@ class SprayingResultActivity :
message.showMessage() message.showMessage()
} }
} }
intent.getStringExtra("path")?.apply { val judgment = intent.getStringExtra("judgment").orEmpty()
showResultImage(this) ocrOne = intent.getStringExtra("one").orEmpty()
ocrTwo = intent.getStringExtra("two").orEmpty()
status = judgment != "不合格"
setStatusImage() setStatusImage()
} }
}
fun setStatusImage() { fun setStatusImage() {
binding.tvTaskHeader.text = if (status) "OCR识别结果合格" else "OCR识别结果不合格" binding.tvTaskHeader.text = if (status) "OCR识别结果合格" else "OCR识别结果不合格"
binding.status.setImageResource(if (status) R.mipmap.ocr_true else R.mipmap.ocr_false) binding.status.setImageResource(if (status) R.mipmap.ocr_true else R.mipmap.ocr_false)
binding.value1.text = "识码一:$ocrOne"
binding.value2.text = "识码二:$ocrTwo"
binding.value3.text = if (status) "两码一致,符合要求!" else "两码不一致,不符合要求!"
if (status) { if (status) {
binding.value1.paintFlags = binding.value1.paintFlags =
binding.value1.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv() binding.value1.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
@ -272,21 +274,7 @@ class SprayingResultActivity :
override fun adapter(): BasePagedAdapter<ItemItem> = adapter override fun adapter(): BasePagedAdapter<ItemItem> = adapter
private fun showResultImage(path: String) {
binding.iv.doOnLayout {
val targetWidth = it.width.coerceAtLeast(1)
val targetHeight = it.height.coerceAtLeast(1)
imageDecodeExecutor.execute {
val bitmap = BitmapDecodeHelper.decodeSampledBitmap(path, targetWidth, targetHeight)
runOnUiThread {
if (isFinishing || isDestroyed) {
return@runOnUiThread
}
binding.iv.setImageBitmap(bitmap)
}
}
}
}
private fun showSuccessDialogThenBackToTaskList() { private fun showSuccessDialogThenBackToTaskList() {
successDialog?.dismiss() successDialog?.dismiss()

查看文件

@ -3,6 +3,8 @@ package com.nova.brain.glass.viewmodel
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import com.nova.brain.glass.MyApplication import com.nova.brain.glass.MyApplication
import com.nova.brain.glass.model.ItemItem import com.nova.brain.glass.model.ItemItem
import com.nova.brain.glass.model.data.OcrResultData
import com.nova.brain.glass.model.data.RecognizeByPathRequest
import com.nova.brain.glass.repository.Service import com.nova.brain.glass.repository.Service
import com.xuqm.base.di.manager.HttpManager import com.xuqm.base.di.manager.HttpManager
import com.xuqm.base.viewmodel.BaseListViewModel import com.xuqm.base.viewmodel.BaseListViewModel
@ -22,10 +24,10 @@ enum class UploadState {
FAILED FAILED
} }
class SprayingOCRVM: BaseListViewModel<ItemItem>() { class SprayingOCRVM : BaseListViewModel<ItemItem>() {
val uploadState = MutableLiveData(UploadState.IDLE) val uploadState = MutableLiveData(UploadState.IDLE)
val uploadError = MutableLiveData<String>() val uploadError = MutableLiveData<String>()
val uploadSuccessPath = MutableLiveData<String?>() val ocrResult = MutableLiveData<OcrResultData?>()
private var uploadingPath: String? = null private var uploadingPath: String? = null
private var uploadDisposable: Disposable? = null private var uploadDisposable: Disposable? = null
@ -55,10 +57,15 @@ class SprayingOCRVM: BaseListViewModel<ItemItem>() {
.batchUpload(multipartFile) .batchUpload(multipartFile)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe({ .subscribe({ response ->
if (uploadingPath != path) return@subscribe if (uploadingPath != path) return@subscribe
uploadState.value = UploadState.SUCCESS if (response.code == 200 && !response.data.isNullOrBlank()) {
uploadSuccessPath.value = path uploadState.value = UploadState.IDLE
startRecognize(path, response.data)
} else {
uploadState.value = UploadState.FAILED
uploadError.value = if (response.message.isBlank()) "附件上传失败" else response.message
}
}, { e -> }, { e ->
if (uploadingPath != path) return@subscribe if (uploadingPath != path) return@subscribe
uploadState.value = UploadState.FAILED uploadState.value = UploadState.FAILED
@ -68,6 +75,28 @@ class SprayingOCRVM: BaseListViewModel<ItemItem>() {
add(disposable) add(disposable)
} }
private fun startRecognize(localPath: String, serverPath: String) {
val disposable = HttpManager.getApi(MyApplication.appComponent2, Service::class.java)
.recognizeByPath(RecognizeByPathRequest(filePath = serverPath))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ response ->
if (uploadingPath != localPath) return@subscribe
if (response.code == 200) {
uploadState.value = UploadState.SUCCESS
ocrResult.value = response.data ?: OcrResultData()
} else {
uploadState.value = UploadState.FAILED
uploadError.value = if (response.message.isBlank()) "OCR识别失败" else response.message
}
}, { e ->
if (uploadingPath != localPath) return@subscribe
uploadState.value = UploadState.FAILED
uploadError.value = e.message ?: "OCR识别失败"
})
add(disposable)
}
fun cancelUpload() { fun cancelUpload() {
uploadingPath = null uploadingPath = null
uploadDisposable?.dispose() uploadDisposable?.dispose()
@ -77,7 +106,7 @@ class SprayingOCRVM: BaseListViewModel<ItemItem>() {
} }
} }
fun clearUploadSuccessPath() { fun clearOcrResult() {
uploadSuccessPath.value = null ocrResult.value = null
} }
} }