Data: Scan History & Management
Relevant source files
The following files were used as context for generating this wiki page:
- app/src/main/java/com/musagenius/ocrapp/data/repository/ScanRepositoryImpl.kt
- app/src/main/java/com/musagenius/ocrapp/domain/repository/ScanRepository.kt
- app/src/main/java/com/musagenius/ocrapp/data/local/dao/ScanDao.kt
- app/src/main/java/com/musagenius/ocrapp/data/local/entity/ScanEntity.kt
- app/src/main/java/com/musagenius/ocrapp/domain/model/ScanResult.kt
- app/src/test/java/com/musagenius/ocrapp/data/repository/ScanRepositoryImplTest.kt
- app/src/main/java/com/musagenius/ocrapp/presentation/viewmodel/OCRViewModel.kt
The Scan History & Management system is responsible for the local persistence, retrieval, and management of all OCR scan results. It is designed as an offline-first data layer, leveraging RoomDB for storage and following Clean Architecture principles. This ensures a clear separation of concerns between the UI (Presentation), business logic (Domain), and data handling (Data) layers.
The core of this system is the ScanRepository, which provides a clean API for all data operations. It abstracts the underlying data source (Room database) and handles data mapping, error wrapping, and reactive data streams using Kotlin Flows. This allows the UI to observe data changes and update automatically.
Architecture Overview
The data management system follows the Repository pattern, a key component of Clean Architecture. The ViewModel in the presentation layer communicates with a ScanRepository interface defined in the domain layer. The actual implementation, ScanRepositoryImpl, resides in the data layer and coordinates with the ScanDao to perform database operations.
This diagram illustrates the architectural layering and dependencies.
Data Models
Two primary models represent a scan result: ScanEntity for database persistence and ScanResult for use in the domain and presentation layers.
ScanEntity: A Room entity class that defines the schema for thescanstable in the database.ScanResult: A domain model that represents a scan in a clean, implementation-agnostic way. It includes helper functions likegetTextPreview()andgetWordCount().
A mapper function, toDomain(), converts ScanEntity objects to ScanResult objects, decoupling the database structure from the application's business logic.
Sources: data/local/entity/ScanEntity.kt, domain/model/ScanResult.kt, data/mapper/ScanMapperTest.kt:18
This class diagram shows the structure of the two models and their relationship.
Database Schema (Room)
The application uses RoomDB for local, offline storage. All data is stored in a single table named scans.
Entity: scans
The scans table schema is defined by the ScanEntity class. It includes indexes on timestamp and language to optimize sorting and filtering operations.
This ER diagram visualizes the scans table structure.
The table below details the fields of the ScanEntity.
| Column Name | Data Type | Description | Default Value |
|---|---|---|---|
id | Long | Primary key, auto-generated. | 0 |
timestamp | Date | Timestamp of when the scan was created. | - |
image_uri | Uri | URI pointing to the stored image file. | - |
extracted_text | String | The full text extracted by the OCR engine. | - |
language | String | The language used for OCR (e.g., "eng"). | - |
confidence_score | Float | The confidence score from the OCR engine. | - |
title | String | A user-editable title for the scan. | "" |
tags | String | Comma-separated list of tags. | "" |
notes | String | User-editable notes. | "" |
is_favorite | Boolean | Flag to mark a scan as a favorite. | false |
modified_timestamp | Date | Timestamp of the last modification. | timestamp |
Sources: data/local/entity/ScanEntity.kt:15-49
Data Access Object (DAO): ScanDao
The ScanDao interface defines all database operations. It uses suspend functions for one-shot operations and returns Flow for queries that need to be observed for changes, enabling a reactive UI.
Key methods in ScanDao:
| Method | Return Type | Description |
|---|---|---|
insert(scan) | suspend fun ... : Long | Inserts a single scan and returns its new ID. |
update(scan) | suspend fun | Updates an existing scan. |
deleteById(scanId) | suspend fun | Deletes a scan by its primary key. |
getAllScans() | Flow<List<ScanEntity>> | Returns a flow of all scans, ordered by newest first. |
searchScans(query) | Flow<List<ScanEntity>> | Searches for a query in text, title, tags, and notes. |
getFavoriteScans() | Flow<List<ScanEntity>> | Returns a flow of all scans marked as favorites. |
getScansByLanguage(lang) | Flow<List<ScanEntity>> | Filters scans by a specific language. |
getScansByDateRange(...) | Flow<List<ScanEntity>> | Filters scans within a given start and end date. |
updateFavoriteStatus(...) | suspend fun | Updates the is_favorite flag for a specific scan. |
deleteOldScans(cutoffDate) | suspend fun ... : Int | Deletes scans older than a given timestamp. |
Sources: [data/local/dao/ScanDao.kt]
Repository Pattern
The repository pattern is used to abstract data operations and provide a clean API to the rest of the app.
ScanRepository Interface
This interface, located in the domain layer, defines the contract for all scan-related data operations. It is implementation-agnostic and uses the ScanResult domain model and a custom Result wrapper for handling success and error states.
Sources: [domain/repository/ScanRepository.kt]
ScanRepositoryImpl Implementation
This class in the data layer provides the concrete implementation of ScanRepository. It injects ScanDao to interact with the database.
Key responsibilities:
- Calling
ScanDaomethods to execute database queries. - Mapping
ScanEntityobjects from the DAO toScanResultdomain models. - Catching exceptions from the DAO and wrapping the outcome in a
Result.success()orResult.error()object. This provides robust error handling.
Sources: [data/repository/ScanRepositoryImpl.kt]
Data Flows
Saving a New Scan
When a new OCR result is saved, the call propagates from the ViewModel down to the ScanDao.
This sequence diagram shows the process of inserting a new scan.
Fetching All Scans (Reactive)
Fetching a list of scans is a reactive process. The ViewModel collects a Flow from the Repository, which in turn gets a Flow from the DAO. When data in the scans table changes, Room automatically emits a new list, which flows up to the UI for display.
This sequence diagram illustrates the reactive data flow for fetching all scans.
Summary
The data management system for the OCR app is a robust, offline-first solution built on modern Android development practices. By adhering to Clean Architecture and the Repository pattern, it achieves excellent separation of concerns. The use of RoomDB provides reliable local persistence, while Kotlin Flows enable a reactive and responsive user interface. The comprehensive ScanDao and ScanRepository APIs offer a full suite of functionalities for creating, reading, updating, deleting, and searching scan history.