11package com.ninecraft.booket.feature.record.ocr
22
3+ import android.util.Base64
34import androidx.compose.runtime.Composable
4- import androidx.compose.runtime.DisposableEffect
55import androidx.compose.runtime.getValue
66import androidx.compose.runtime.mutableStateOf
7+ import androidx.compose.runtime.rememberCoroutineScope
78import androidx.compose.runtime.setValue
8- import com.ninecraft.booket.core.ocr.analyzer.LiveTextAnalyzer
9+ import com.ninecraft.booket.core.ocr.analyzer.CloudOcrRecognizer
910import com.ninecraft.booket.feature.screens.OcrScreen
11+ import com.orhanobut.logger.Logger
1012import com.slack.circuit.codegen.annotations.CircuitInject
1113import com.slack.circuit.retained.rememberRetained
1214import com.slack.circuit.runtime.Navigator
@@ -17,14 +19,16 @@ import dagger.assisted.AssistedInject
1719import dagger.hilt.android.components.ActivityRetainedComponent
1820import kotlinx.collections.immutable.persistentListOf
1921import kotlinx.collections.immutable.toPersistentList
22+ import kotlinx.coroutines.launch
2023
2124class OcrPresenter @AssistedInject constructor(
2225 @Assisted private val navigator : Navigator ,
23- private val liveTextAnalyzer : LiveTextAnalyzer . Factory ,
26+ private val recognizer : CloudOcrRecognizer ,
2427) : Presenter<OcrUiState> {
2528
2629 @Composable
2730 override fun present (): OcrUiState {
31+ val scope = rememberCoroutineScope()
2832 var currentUi by rememberRetained { mutableStateOf(OcrUi .CAMERA ) }
2933 var isPermissionDialogVisible by rememberRetained { mutableStateOf(false ) }
3034 var sentenceList by rememberRetained { mutableStateOf(emptyList<String >().toPersistentList()) }
@@ -33,18 +37,37 @@ class OcrPresenter @AssistedInject constructor(
3337 var mergedSentence by rememberRetained { mutableStateOf(" " ) }
3438 var isTextDetectionFailed by rememberRetained { mutableStateOf(false ) }
3539 var isRecaptureDialogVisible by rememberRetained { mutableStateOf(false ) }
36-
37- val analyzer = rememberRetained {
38- liveTextAnalyzer.create(
39- onTextDetected = { text ->
40- recognizedText = text
41- },
42- )
43- }
44-
45- DisposableEffect (Unit ) {
46- onDispose {
47- analyzer.cancel()
40+ var isLoading by rememberRetained { mutableStateOf(false ) }
41+
42+ fun recognizeText (base64Image : String ) {
43+ scope.launch {
44+ try {
45+ isLoading = true
46+ recognizer.recognizeText(base64Image)
47+ .onSuccess {
48+ val text = it.responses.firstOrNull()?.fullTextAnnotation?.text.orEmpty()
49+ recognizedText = text
50+
51+ if (text.isNotBlank()) {
52+ isTextDetectionFailed = false
53+ val sentences = text
54+ .split(" \n " )
55+ .map { it.trim() }
56+ .filter { it.isNotEmpty() }
57+
58+ sentenceList = persistentListOf(* sentences.toTypedArray())
59+ currentUi = OcrUi .RESULT
60+ } else {
61+ isTextDetectionFailed = true
62+ }
63+ }
64+ .onFailure {
65+ isTextDetectionFailed = true
66+ Logger .e(" Cloud Vision API Error: ${it.message} " )
67+ }
68+ } finally {
69+ isLoading = false
70+ }
4871 }
4972 }
5073
@@ -62,24 +85,9 @@ class OcrPresenter @AssistedInject constructor(
6285 isPermissionDialogVisible = false
6386 }
6487
65- is OcrUiEvent .OnFrameReceived -> {
66- analyzer.analyze(event.imageProxy)
67- }
68-
6988 is OcrUiEvent .OnCaptureButtonClick -> {
70- if (recognizedText.isEmpty()) {
71- isTextDetectionFailed = true
72- } else {
73- isTextDetectionFailed = false
74-
75- val sentences = recognizedText
76- .split(" \n " )
77- .map { it.trim() }
78- .filter { it.isNotEmpty() }
79- sentenceList = persistentListOf(* sentences.toTypedArray())
80-
81- currentUi = OcrUi .RESULT
82- }
89+ val base64Image = Base64 .encodeToString(event.imageData, Base64 .NO_WRAP )
90+ recognizeText(base64Image)
8391 }
8492
8593 is OcrUiEvent .OnReCaptureButtonClick -> {
@@ -119,6 +127,7 @@ class OcrPresenter @AssistedInject constructor(
119127 selectedIndices = selectedIndices,
120128 isTextDetectionFailed = isTextDetectionFailed,
121129 isRecaptureDialogVisible = isRecaptureDialogVisible,
130+ isLoading = isLoading,
122131 eventSink = ::handleEvent,
123132 )
124133 }
0 commit comments