Data: State Management
Relevant source files
The following files were used as context for generating this wiki page:
- app/src/main/java/com/musagenius/ocrapp/presentation/viewmodel/OCRViewModel.kt
- app/src/main/java/com/musagenius/ocrapp/presentation/ui/camera/CameraState.kt
- app/src/main/java/com/musagenius/ocrapp/domain/model/ScanResult.kt
- TODO.md
- app/src/main/java/com/musagenius/ocrapp/data/utils/StorageManager.kt
- app/src/main/java/com/musagenius/ocrapp/data/ocr/OCRServiceImpl.kt
State management in the OCR Android App follows the modern Android architecture guidelines, employing a Unidirectional Data Flow (UDF) pattern within an MVVM (Model-View-ViewModel) structure. This approach is designed for Jetpack Compose, ensuring that the UI is a direct function of the application state, leading to a more predictable, testable, and maintainable codebase.
The core principle is that state flows down from ViewModels to the UI, and events flow up from the UI to the ViewModels. State is encapsulated in immutable data classes, and updates are exposed to the UI using Kotlin's StateFlow. This ensures that the UI automatically recomposes when the state changes, but only the ViewModel can modify the state.
Architecture and Data Flow
The application's state management is centered around ViewModels, which are responsible for holding and processing UI-related data. They interact with the domain and data layers to fetch and update data, and then expose the UI state to the composable functions.
The following diagram illustrates the Unidirectional Data Flow implemented in the app.
This diagram shows the flow of data and events. The UI sends an event to the ViewModel, which updates its state and may call use cases or repositories. The updated state is then observed by the UI, which recomposes to reflect the changes.
Sources: app/src/main/java/com/musagenius/ocrapp/presentation/viewmodel/OCRViewModel.kt:47-56, TODO.md
Core Components
ViewModel State Management
The OCRViewModel is a primary example of state management in the app. It holds the state for the OCR processing screen, handles business logic, and updates the UI state in response to events.
- State Holder: A private
MutableStateFlowis used to hold the current UI state. This is exposed to the UI as an immutableStateFlow. - Event Handling: The
onEventfunction receives events from the UI (defined in asealed classlikeOCREvent) and triggers the appropriate logic. - Asynchronous Operations:
viewModelScopeis used to launch coroutines for long-running tasks like image processing, ensuring the UI thread is not blocked. - Job Management: A
Jobinstance (currentJob) is maintained to track the active OCR process. This allows for cancellation of ongoing tasks to prevent race conditions or resource leaks, for example, when a new image is submitted for processing before the previous one is finished.
The following class diagram shows the relationships between the OCRViewModel and its key dependencies.
This diagram illustrates that OCRViewModel holds a StateFlow of OCRUiState, handles different OCREvent types, and uses ProcessImageUseCase, ScanRepository, and OCRService to perform its tasks.
Sources: app/srcs/main/java/com/musagenius/ocrapp/presentation/viewmodel/OCRViewModel.kt:29-38
State and Event Data Structures
State and events are modeled using immutable Kotlin data and sealed classes. This ensures type safety and predictability.
OCR Screen State (OCRUiState)
This data class represents the entire state for the OCR processing screen. While the file OCRUiState.kt is not provided, its structure can be inferred from its usage in OCRViewModel.
| Property | Type | Description |
|---|---|---|
imageUri | Uri? | The URI of the image being processed. |
isProcessing | Boolean | True if OCR processing is currently active. |
error | String? | Holds an error message if processing fails. |
language | String | The language used for OCR. |
extractedText | String | The text extracted from the image. |
confidenceScore | Float | The confidence score of the OCR result. |
processingTimeMs | Long | The time taken for the OCR process. |
isSaved | Boolean | True if the scan has been saved to history. |
Sources: app/src/main/java/com/musagenius/ocrapp/presentation/viewmodel/OCRViewModel.kt:81-88, 103-111
Camera Screen State (CameraUiState)
This data class holds all state-related information for the camera screen, from flash mode to zoom ratio and detected document corners.
| Property | Type | Description |
|---|---|---|
isLoading | Boolean | Indicates if the camera is being initialized. |
capturedImageUri | Uri? | The URI of the most recently captured image. |
error | String? | An error message related to camera operations. |
flashMode | FlashMode | The current state of the camera flash (ON, OFF, AUTO). |
isProcessing | Boolean | True if a captured image is being processed. |
zoomRatio | Float | The current camera zoom level. |
cameraFacing | CameraFacing | The direction the camera is facing (BACK or FRONT). |
documentCorners | DocumentEdgeDetector.DocumentCorners? | Coordinates of detected document edges. |
lightingCondition | LowLightDetector.LightingCondition | The detected ambient lighting condition. |
Sources: app/src/main/java/com/musagenius/ocrapp/presentation/ui/camera/CameraState.kt:9-28
Events (CameraEvent and OCREvent)
Events are modeled as a sealed class, allowing for a well-defined and exhaustive set of actions that the UI can send to the ViewModel.
// Example structure for CameraEvent
sealed class CameraEvent {
data object CaptureImage : CameraEvent()
data object ToggleFlash : CameraEvent()
data class SetZoom(val ratio: Float) : CameraEvent()
// ... other events
}
Sources: app/src/main/java/com/musagenius/ocrapp/presentation/ui/camera/CameraState.kt:111-129, app/src/main/java/com/musagenius/ocrapp/presentation/viewmodel/OCRViewModel.kt:48-56
State Update Flow in Practice
The following sequence diagram shows the interactions when a user captures an image and initiates OCR processing.
This diagram details the sequence from a user action to the final UI update, highlighting the asynchronous nature of the process and the role of each component.
Sources: app/src/main/java/com/musagenius/ocrapp/presentation/viewmodel/OCRViewModel.kt:70-125, app/src/main/java/com/musagenius/ocrapp/data/ocr/OCRServiceImpl.kt:149-237
Domain and Storage Models
While UI state is ephemeral, the result of a successful OCR scan is persisted using domain and storage models.
ScanResult: A domain-layer data class representing a completed scan. It is decoupled from the database implementation and contains all relevant information, such as the extracted text, confidence score, and image URI. It also includes helper functions likegetTextPreview()andhasHighConfidence().StorageInfo: A simple data class fromStorageManagerthat represents the state of the application's storage usage, broken down by images and cache.
Sources: app/src/main/java/com/musagenius/ocrapp/domain/model/ScanResult.kt, app/src/main/java/com/musagenius/ocrapp/data/utils/StorageManager.kt:159-167
Summary
The application employs a robust and modern state management strategy based on UDF and MVVM. State is centralized in ViewModels, represented by immutable data classes, and exposed via StateFlow. This pattern, combined with sealed classes for events, ensures a clear and predictable data flow, simplifies handling of asynchronous operations, and results in a reactive UI that is easy to debug and maintain.