Skip to content

Releases: METS-Programme/UgandaEMRMobile

Release v0.0.15

21 Jul 13:17
78b3181

Choose a tag to compare

Handle sync (#14)

* Refactor: Manage Sync UI state and preferences in ViewModel

This commit refactors the Sync screen to manage its UI state, including auto-sync settings and last sync information, within the `SyncViewModel`. This centralizes state management and allows for persistence of auto-sync preferences.

Key changes:

- **Moved UI state to `SyncUiState`:**
    - Added `lastSyncTime`, `lastSyncStatus`, `lastSyncBy`, `lastSyncError`, `autoSyncEnabled`, and `autoSyncInterval` fields to `SyncUiState.kt`.
    - `SyncScreen.kt` now consumes these values directly from the `uiState` provided by the `SyncViewModel`.
- **Preference Management in ViewModel:**
    - `SyncViewModel.kt` now uses `PreferenceManager` to save and load auto-sync settings (`autoSyncEnabled`, `autoSyncInterval`).
    - Added `restoreAutoSyncSettings` function to load preferences on ViewModel initialization.
    - Added `handleToggleAutoSync` and `updateAutoSyncInterval` functions to update both UI state and preferences when these settings change.
- **New `PreferenceManager` methods:**
    - Added `saveAutoSyncEnabled`, `loadAutoSyncEnabled`, `saveAutoSyncInterval`, and `loadAutoSyncInterval` methods to `PreferenceManager.kt` and `PreferenceManagerImpl.kt` to handle storing and retrieving auto-sync settings in DataStore.
    - Defined `KEY_AUTO_SYNC_ENABLED` (boolean) and `KEY_AUTO_SYNC_INTERVAL` (string) preference keys in `PreferenceManagerImpl.kt`. Default values are `false` for enabled and "15 minutes" for interval.
- **New `SyncEvent` types:**
    - Added `UpdateLastSyncTime`, `UpdateLastSyncStatus`, `UpdateLastSyncBy`, `UpdateLastSyncError`, `ToggleAutoSync`, and `UpdateAutoSyncInterval` to `SyncEvent.kt` to handle updates to the sync-related UI state.
    - `SyncViewModel` now handles these events to update the `_uiState`.
- **Removed redundant success messages:**
    - Removed `successMessage = ""` from various `handleResult` calls in `SyncViewModel.kt` as it was not being used.
- **Simplified `saveDownloadedForms` (commented out in diff but related):**
    - The `saveDownloadedForms` function was updated to use the common `handleResult` pattern, but the provided diff comments it out. Assuming this is an intended part of the refactor.

This change improves the structure of the Sync feature by centralizing state logic in the ViewModel and leveraging the `PreferenceManager` for persistent settings.

* Refactor: Simplify encounter loading and improve patient details UI

This commit introduces the following changes:

- **Simplified Encounter Loading in `WorklistViewModel`**:
    - Removed the `getEncountersByPatientIdAndVisitId` function and its call during patient data loading. Encounters are now implicitly part of the visit data.
    - Updated the success message for loading a patient to "Patient loaded successfully".
- **Enhanced `PatientDetailsScreen`**:
    - Introduced a `patientVisits` variable to filter visits specifically for the selected patient. This ensures that only relevant visit history is displayed.
    - The "Visit History" section now uses `patientVisits` for its items.
    - Removed redundant "Vitals" label in `VitalsInfo` as the context is clear.

* Refactor: Standardize loading state handling across screens

This commit refactors the loading state management in several screens and ViewModels to use a more consistent approach.

Key changes:

- **Centralized Loading State in ViewModels:**
    - `QuestionnaireViewModel`, `WorklistViewModel`, `SyncViewModel`, and `RegisterPatientViewModel` now update their respective `isLoading` flags within their UI state (`_state.value = _state.value.copy(isLoading = true/false)` or `_uiState.update { it.copy(isLoading = true/false) }`) before and after asynchronous operations.
    - Removed redundant `showLoading()` and `hideLoading()` calls that were previously managed separately from the UI state.
- **Screen-Level Loading Observation:**
    - `WorklistScreen`, `QuestionnaireScreen`, `SyncScreen`, `PatientDetailsScreen`, and `RegisterPatientScreen` now observe the `isLoading` property from their respective ViewModel's UI state using `LaunchedEffect`.
    - The `isLoading` variable within each composable screen is updated reactively based on changes in the ViewModel's UI state.
    - The `BaseScreen` composable now consistently receives the `isLoading` state from the screen and uses its `showLoading` callback to manage the display of loading indicators.
- **Simplified Loading Logic:**
    - In `QuestionnaireViewModel`, the `isLoading` state is set to `true` at the beginning of `loadQuestionnaireByUuid` and `loadVisitByPatientId` and updated within the `handleResult` block or after the operation.
    - In `WorklistViewModel`, `isLoading` is set before operations like `searchPatients`, `getPatientById`, `saveVitals`, and `createVisit`, and then set to `false` within `handleResult` or after completion.
    - In `SyncViewModel`, `isLoading` is set before network/DB operations like `loadForms`, `filterForms`, `downloadSelectedForms`, and `createDataDefinition`, and updated to `false` in `handleResult` or upon completion.
    - In `RegisterPatientViewModel`, `isLoading` is set to `true` at the start of `loadRegisterPatientQuestionnaire` and `submit` methods and reset to `false` within the `try/catch/finally` blocks or after successful operations.
- **UI Updates for Empty States:**
    - In `PatientDetailsScreen`, when `patientVisits` is empty, an `EmptyStateView` with the message "No previous visits found for this patient." is now displayed.
    - Similarly, if `state.mostRecentVisit` is null, an `EmptyStateView` with "No active visit found." is shown.

This refactoring ensures that loading indicators are consistently shown and hidden based on the actual state of ongoing operations, leading to a more predictable and robust user experience.

* Refactor: Simplify loading state management in Auth screen

This commit refactors the loading state management in the authentication feature:

- **AuthViewModel:**
    - Removed explicit calls to `showLoading()` and `hideLoading()` within the `login` function.
    - The `isLoading` state in `_uiState` is now set to `true` at the beginning of the login process and set to `false` only upon successful login. The `handleResult` function (or its underlying mechanism) is assumed to handle the `isLoading` state for error scenarios.
- **AuthScreen:**
    - The local `isLoading` state is now directly driven by the `uiState.isLoading` collected from the `AuthViewModel`.
    - A `LaunchedEffect` observes changes in `uiState.isLoading` and updates the local `isLoading` accordingly.
    - The `isLoading` parameter of `BaseScreen` is now directly passed the local `isLoading` state.
    - The `showLoading` lambda in `BaseScreen` is updated to set the local `isLoading` state.

This change centralizes the source of truth for the loading state within the `AuthViewModel`'s `uiState` and simplifies the state management in the `AuthScreen`.

* Refactor: Improve UI and user feedback

This commit introduces the following changes:

- **Worklist Screen:**
    - Displays an `EmptyStateView` with the message "No patients found." when the patient list is empty. This provides clearer feedback to the user when there is no data to display.
- **Sync View Model:**
    - Updated the success message for downloading forms from "Successfully created data definition" to "Successfully downloaded selected forms". This message is more accurate and user-friendly.

* Refactor: Separate LaunchedEffect for isLoading state

This commit modifies the `LaunchedEffect` in `QuestionnaireScreen.kt` and `RegisterPatientScreen.kt`.

Previously, the `LaunchedEffect` responsible for loading data was also keyed by the `isLoading` state. This meant that the data loading logic would re-trigger not only when `formId` (or `patient?.id`) changed but also when `isLoading` changed, which is not the intended behavior.

The `LaunchedEffect` has been split into two:
- One `LaunchedEffect` is now keyed only by `formId` (or `patient?.id`) and is responsible for triggering the initial data load or reload when the identifier changes.
- A new, separate `LaunchedEffect` is keyed by `isLoading` (or `uiState.isLoading`) and is solely responsible for updating the local `isLoading` variable when the corresponding state changes.

This change ensures that data loading is triggered only when necessary, preventing redundant operations and aligning with the intended use of `LaunchedEffect` where effects should restart only when their key parameters change.

* Update UI state in RegisterPatientViewModel

The `isLoading` state is now set to `true` after successfully loading the patient registration questionnaire JSON. This provides visual feedback to the user while the questionnaire is being processed.

* Refactor: Enhance data handling and error management in Sync module

This commit introduces several improvements to the synchronization module, focusing on more robust data handling, refined UI state management, and better error reporting.

Key changes include:

- **Error Handling & Network Layer:**
    - Refactored API calls in `SyncRepositoryImpl` to use a `catch` block for centralized error handling and logging, replacing individual `try-catch` blocks.
    - Introduced a `RetryInterceptor` to automatically retry failed network requests up to 3 times.
    - Increased OkHttp client timeouts (connect, read, write) to 30 seconds.
    - Modified `BaseViewModel.handleResult` to include an `onError` callback, allowing more specific error handling in ViewModels.

- **Data Definition & Reporting:**
    - Updated `SyncRepository` and `SyncUseCase` for `createDataDefinition` to expect and return `List<Definition>` instead of `Any`.
    - Introduced a `Definition` data class in `core.model.reports` to represent the structure of data definition results.
    - `SyncUiState`:
        - `availableParameters` is now a computed property deriving from `customAvailableParameters` (if set) or a combination of `encounterTypes`, `orderTypes`, `identifiers`, and `personAttributeTypes`.
        - `selectedParameters` now defaults to `IndicatorRepository.defaultSelectedAttributes`.
        - Removed `selectedDateRange` as it's not directly used for the current data definition payload.
    - `SyncViewModel`:
        - `onApplyFilters` now validates for missing indicator or cohort before proceeding.
        - `buildReportRequest` no longer requires start and end dates; `type` is set to "Cohort".
        - When an indicator is selected (`onIndicatorSelected`), `customAvailableParameters` is set, and `selectedParameters` are cleared.
        - `moveRight` and `moveLeft` logic updated to manage `customAvailableParameters` and ensure distinct items in `selectedParameters`.
    - Added `defaultSelectedAttributes` to `IndicatorRepository`.

- **Model & Mapper Enhancements:**
    - Added `toAttribute()`, `toEncounterType()`, `toAttributes()`, and `toEncounterTypes()` extension functions for `EncounterType` and `Attribute` to facilitate conversion between them.
    - Added similar `toAttribute()`, `toIdentifier()`, `toAttributes()`, `toIdentifiers()` extensions for `Identifier`.
    - Added similar `toAttribute()`, `toOrderType()`, `toAttributes()`, `toOrderTypes()` extensions for `OrderType`.
    - Added similar `toAttribute()`, `toPersonAttributeType()`, `toAttributes()`, `toPersonAttributeTypes()` extensions for `PersonAttributeType`.
    - Updated `LoginResponse.privileges` to be a `List<Privilege>` (new data class) instead of `List<String>`.

- **UI & ViewModel Logic:**
    - `SyncViewModel`:
        - Asynchronously loads individual form details (`loadFormByUuid`) when downloading selected forms using `async` and `awaitAll` for improved performance.
        - `loadForms`, `filterForms`, `onDownloadSelectedForms`, `onApplyFilters` now show loading state consistently.
        - Loads patient identifiers and person attribute types, converting them to `Attribute` objects for the UI.
    - Removed the "Apply Filters" button from `PatientFilterSectionContent` as filtering is now triggered by `SyncViewModel.onApplyFilters`.
    - Removed unused `ApplyFilters` event from `SyncEvent`.

- **Logging:**
    - Improved logging in `SyncRepositoryImpl` for API call successes and failures.
    - Added logging in `SyncViewModel.onApplyFilters` for validation failures and missing data.

* Refactor: Update report indicator handling and UI

This commit refactors how report indicators, encounter types, and order types are handled and displayed in the Sync screen.

Key changes:

- **Unified Indicator List:**
    - Encounter types and order types are now converted to `Indicator` objects using new `toIndicators()` extension functions in `EncounterTypeListResponse.kt` and `OrderTypesListResponse.kt`.
    - The `indicatorOptions` in `SyncScreen.kt` now dynamically builds a list containing predefined report indicators, encounter type indicators, and order type indicators.
- **`SyncViewModel` Updates:**
    - Removed `loadReportIndicators()` as indicators are now managed within the UI state.
    - Updated `loadEncounterTypes()` and `loadOrderTypes()` to store the fetched types as `List<Indicator>` in `SyncUiState` instead of `List<Attribute>`.
    - `onIndicatorSelected()` now correctly sets `availableParameters` based on the selected indicator type (e.g., uses `identifiers` for "IDN", `personAttributeTypes` for "PAT").
    - Logic for adding and removing parameters now updates `availableParameters` instead of the deprecated `customAvailableParameters`.
- **`SyncUiState` Refactor:**
    - `encounterTypes` and `orderTypes` are now `List<Indicator>`.
    - Removed `customAvailableParameters`. The `availableParameters` field is now directly populated and used.
    - Removed the getter for `availableParameters` as it's now a direct field.
- **Model Extensions:**
    - Added `toIndicator()` and `toIndicators()` extension functions to `IdentifierListResponse.kt` and `PersonAttributeTypeListResponse.kt` to allow conversion of Identifiers and PersonAttributeTypes to `Indicator` objects.
    - Updated `toAttribute()` in `IdentifierListResponse.kt` and `PersonAttributeTypeListResponse.kt` to set a more specific `type` (e.g., "IDN", "PAT").
- **Minor UI Adjustments:**
    - `FilterSection.kt` in the `worklist` module now uses `MaterialTheme.colorScheme.surface` for its background, ensuring consistency. (This change seems unrelated to the core refactoring but was included in the diffs).
    - `PatientFilterSectionContent.kt` imports were adjusted. (This change also seems minor and potentially unrelated but was present).

This refactoring simplifies the management of different types of filterable items by treating them all as `Indicator` objects, making the code cleaner and more extensible for future filter types.

* feat: Process and save data definitions as PatientEntity

This commit updates the data definition creation process to parse the JSON response from the API into a list of `PatientEntity` objects and save them to the local database.

Key changes:

- **`SyncRepository` and `SyncRepositoryImpl`:**
    - Modified `createDataDefinition` to return `Flow<Result<List<PatientEntity>>>` instead of `Flow<Result<List<Definition>>>`.
    - In `SyncRepositoryImpl`, the `generateDataDefinition` API call now expects a `ResponseBody`.
    - Added `JsonAdapter<List<Map<String, Any>>>` (listOfMapAdapter) as a dependency to parse the JSON response.
    - The response body is parsed into a `List<Map<String, Any>>`.
    - Each map in the list is then converted to a `PatientEntity` using the new `mapToPatientEntity` function.
    - The resulting `List<PatientEntity>` is inserted into the `patientDao`.
- **`PatientEntity.kt`:**
    - Added a new extension function `mapToPatientEntity(map: Map<String, Any>): PatientEntity`. This function maps the key-value pairs from the input map to the fields of a `PatientEntity`.
    - It handles safe string extraction and converts the "status" string to the `VisitStatus` enum, defaulting to `PENDING` if parsing fails.
    - `id` is generated using `UUID.randomUUID().toString()` if not present in the map.
- **`SyncUseCase.kt`:**
    - Updated the return type of `createDataDefinition` to align with the repository changes.
- **`MoshiModule.kt`:**
    - Added providers for `JsonAdapter<Map<String, Any>>` and `JsonAdapter<List<Map<String, Any>>>` to enable Moshi to handle these generic types.
- **`SyncModule.kt`:**
    - Updated `SyncRepositoryImpl` provider to include the new `listOfMapAdapter` dependency.
- **`FormApi.kt`:**
    - Changed the return type of `generateDataDefinition` to `Response<ResponseBody>` to handle raw JSON.
- **`NetworkModule.kt`:**
    - Increased OkHttpClient timeouts (connect, read, write) from 30 seconds to 120 seconds to accommodate potentially longer data definition generation times.

* Refactor: Enhance Vitals Saving and Visit Creation Flow

This commit introduces improvements to the process of saving vitals and creating new patient visits, focusing on better error handling and UI feedback.

**Key Changes:**

- **Vitals Saving with Feedback:**
    - `PatientRepository.saveVital` and `PatientRepositoryImpl.saveVital` now return `Flow<Result<Boolean>>` instead of `Unit`.
    - `PatientRepositoryImpl.saveVital` now wraps the database operation in a `flow` and uses `try-catch` to handle `SQLiteConstraintException`, `SQLiteException`, and generic `Exception`. It emits `Result.Success(true)` or `Result.Error` with a descriptive message.
    - `PatientsUseCase.saveVitals` is updated to reflect the new return type.
    - `WorklistViewModel.onSaveVitals`:
        - Now collects the `Flow<Result<Boolean>>` from `patientsUseCase.saveVitals`.
        - Updates `_uiState.isLoading` before and after the operation.
        - Uses `handleResult` to manage success and error states.
        - On successful vital save, it reloads the patient's most recent visit and all their visits (`loadPatientMostRecentVisit`, `loadPatientVisits`).
- **Visit Creation Flow:**
    - `WorklistViewModel.onCreateNewVisit`:
        - Now sets `_uiState.isLoading` to `true` before initiating the save visit operation and sets it to `false` in both success and error scenarios within the `handleResult` block.
        - On successful visit creation, it now calls `loadPatientMostRecentVisit(patientId)` to refresh the visit details in the UI.
- **Minor UI Adjustments:**
    - `StartVisitScreen.kt`: The `clickable` modifier for visit type selection was slightly reformatted for clarity.
    - `WorklistScreen.kt`: A commented-out "Static header" line was added.

These changes provide more robust error handling during database operations for vitals and ensure the UI reflects loading states and updates correctly after saving vitals or creating a new visit.

* Fix: Update isLoading state on data definition error

This commit ensures that the `isLoading` state in `SyncUiState` is set to `false` when an error occurs during the creation of a data definition in `SyncViewModel`.

Previously, `isLoading` remained `true` even after an error, which could lead to a confusing user experience.

* feat: Integrate Paging Compose library

This commit adds the `androidx.paging:paging-compose` library as a dependency.

Key changes:
- Added `implementation(libs.androidx.paging.compose)` to `worklist/build.gradle.kts`.
- Defined `pagingComposeAndroid = "3.4.0-alpha01"` in `gradle/libs.versions.toml`.
- Updated `androidx-paging-common-android` to use `paging-common` instead of `paging-common-android` as the library name in `gradle/libs.versions.toml`.
- Added `androidx-paging-compose` to `gradle/libs.versions.toml` with the version defined above.

This integration will allow for the use of Paging 3 with Jetpack Compose for displaying large datasets in a scrollable list.

* Refactor: Add synced data counts to Sync screen and ViewModel

This commit introduces the tracking and display of synced patient and encounter counts.

Key changes:

- **`EncounterDao.kt` & `PatientDao.kt`:**
    - Added `getSyncedEncountersCount(): Flow<Int>` to `EncounterDao` to count synced encounters.
    - Added `getSyncedPatientsCount(): Flow<Int>` to `PatientDao` to count synced patients.
- **`SyncRepository.kt` & `SyncRepositoryImpl.kt`:**
    - Renamed `getUnsynced()` to `getUnsyncedEncounters()` and `markSynced()` to `markSyncedEncounter()` for clarity in the repository interface and implementation.
    - Added `getSyncedEncountersCount(): Flow<Result<Int>>` and `getSyncedPatientsCount(): Flow<Result<Int>>` to the repository, which wrap the DAO calls and handle potential errors.
- **`SyncUseCase.kt`:**
    - Updated to use the renamed repository methods for unsynced encounters and marking encounters synced.
    - Added `getSyncedEncounterCount()` and `getSyncedPatientsCount()` methods to expose the new functionality to the ViewModel.
- **`SyncUiState.kt`:**
    - Added `syncedPatientCount: Int` and `syncedEncounterCount: Int` to hold the counts of synced entities.
- **`SyncViewModel.kt`:**
    - In `init`, added calls to `updateSyncedPatientsCount()` and `updateSyncedEncounterCount()` to load the initial counts.
    - Implemented `updateSyncedEncounterCount()` and `updateSyncedPatientsCount()` to collect the counts from the `SyncUseCase` and update the `SyncUiState`.
    - Ensured `isLoading` is set to `false` in the `onError` callback of `onDownloadSelectedForms` to prevent the UI from staying in a loading state indefinitely on error.
- **`SyncScreen.kt`:**
    - Updated the "Data Synced" section to display the new `syncedPatientCount` and `syncedEncounterCount` from `uiState`.
    - Changed labels for clarity:
        - "Forms Saved:" to "Available Forms:"
        - "Visits Saved:" to "Encounters Saved:"
    - Used `Icons.Default.CheckCircle` for "Encounters Saved:" and "Encounters Synced:" for better visual representation.

These changes provide users with more detailed information about the synchronization status of their data.

* Refactor: Integrate WorkManager for background sync and update UI

This commit introduces `SyncManager` using Android's WorkManager to handle background synchronization tasks. It also updates the UI and ViewModel logic in the `settings` and `sync` modules to reflect changes in how sync intervals are managed and displayed.

**Core Changes:**

- **`SyncManager.kt` (New):**
    - Created a new `SyncManager` class in the `core.data.sync` package.
    - `syncNow()`: Enqueues a chain of `OneTimeWorkRequest` for a list of worker classes. Requires a connected network.
    - `schedulePeriodicSync()`: Enqueues `PeriodicWorkRequest` for specified worker classes with a given interval in hours. Requires an unmetered network and the device to be charging. Uses `ExistingPeriodicWorkPolicy.UPDATE`.
    - `cancelPeriodicSync()`: Cancels unique periodic work for the specified worker classes.
- **Dependency Updates (`core/build.gradle.kts`):**
    - Added Hilt WorkManager dependencies (`hilt.work`, `hilt.work.compiler`) and WorkManager KTX (`work.runtime.ktx`).
- **DI (`CoreModule.kt`):**
    - Added a provider for `SyncManager` as a singleton.

**Settings Module:**

- **`SettingsScreen.kt`:**
    - Sync interval display now shows hours (e.g., "${state.syncIntervalInHours} hrs") instead of minutes.
    - `SettingsServerConfigurationDialog` now uses `currentInterval = state.syncIntervalInHours`.
- **`SettingsViewModel.kt`:**
    - Injected `SyncManager`.
    - `updateSyncInterval()`:
        - Now takes `intervalInHours: Long`.
        - Saves the interval to `PreferenceManager`.
        - Commented out logic for re-scheduling sync workers (e.g., `PatientsSyncWorker`, `EncountersSyncWorker`) when the interval changes and auto-sync is enabled. This seems like a work in progress.
    - `updateServerUrl()`: Now saves the server URL to `PreferenceManager`.
- **`SettingsEvent.kt`:**
    - `UpdateSyncInterval` now takes `intervalInHours: Long`.
- **`SettingsUiState.kt`:**
    - Renamed `syncIntervalInMinutes` to `syncIntervalInHours` (Int).
    - Added `autoSyncEnabled: Boolean = false`.

**Sync Module:**

- **`SyncViewModel.kt`:**
    - Injected `SyncManager`.
    - `onEvent()`:
        - Handles `SyncEvent.SyncNow` by calling `syncManager.syncNow()` with `PatientsSyncWorker` and `EncountersSyncWorker`.
        - Removed handling for `SyncEvent.UpdateAutoSyncInterval`.
    - `handleToggleAutoSync()`:
        - If auto-sync is enabled, it schedules periodic sync using `syncManager.schedulePeriodicSync()` with the interval from `PreferenceManager`.
        - If auto-sync is disabled, it cancels periodic sync using `syncManager.cancelPeriodicSync()`.
    - `updateAutoSyncInterval()` (private function):
        - Takes `newInterval: Long`.
        - Updates `PreferenceManager`.
        - If auto-sync is enabled, it cancels existing periodic sync and re-schedules with the new interval.
    - Removed `saveDownloadedForms()` and `getCurrentFormattedTime()` as they are no longer used or replaced by WorkManager logic.
- **`SyncScreen.kt`:**
    - Removed `onToggleAutoSync` and `onSyncNow` parameters from the `SyncScreen` composable.
    - "Sync Now" button now triggers `SyncEvent.SyncNow`.
    - Auto-sync Switch `onCheckedChange` now triggers `SyncEvent.ToggleAutoSync`.
- **`SyncUiState.kt`:**
    - `autoSyncInterval` is now `Long` and defaults to 12 (hours).
- **Worker Renaming:**
    - `EncounterSyncWorker.kt` moved from `sync.presentation` to `sync.data.sync` and renamed to `EncountersSyncWorker.kt`.
    - `PatientsSyncWorker.kt` moved from `sync.presentation` to `sync.data.sync`.
- **`SyncEvent.kt`:**
    - Added `SyncNow` object.
    - Removed `UpdateAutoSyncInterval`.
- **DI (`SyncModule.kt`):**
    - Updated to provide `SyncManager` to `SyncRepositoryImpl` (though this might be an error as `SyncRepositoryImpl` doesn't directly use it in the diff).

**PreferenceManager:**

- **`PreferenceManagerImpl.kt`:**
    - Changed `KEY_AUTO_SYNC_INTERVAL` to `AUTO_SYNC_INTERVAL_HOURS` (intPreferencesKey).
    - `saveAutoSyncInterval` now saves an `Int`.
    - `loadAutoSyncInterval` now loads an `Int` and defaults to "12".
- **`PreferenceManager.kt`:**
    - `saveAutoSyncInterval` now takes `hours: Long`.
    - `loadAutoSyncInterval` now returns `Long`.
    - Added `loadServerUrl(): String` and `saveServerUrl(url: String)`.

* Refactor: Force Guava version and update Kotlin configuration

This commit enforces the Guava version to `32.1.3-android` across multiple modules (`auth`, `settings`, `core`, `worklist`, `form`) to ensure consistency and resolve potential compatibility issues.

Additionally, the Kotlin configuration in `core/build.gradle.kts` has been updated:
- `kotlinOptions { jvmTarget = "11" }` was removed.
- The `freeCompilerArgs` for `-XXLanguage:+PropertyParamAnnotationDefaultTargetMode` is now set within the `kotlin { compilerOptions { ... } }` block.

The `main/build.gradle.kts` file was also updated:
- Replaced `kotlinOptions { jvmTarget = "11" }` with `kotlin { jvmToolchain(11) }` for consistency with other modules.

* Refactor: Change auto-sync interval to hours and update UI

This commit refactors the auto-sync interval to be represented in hours instead of minutes across the application.

Key changes:

- **Data Types & Storage:**
    - `PreferenceManager` and `PreferenceManagerImpl`:
        - `saveAutoSyncInterval` now accepts an `Int` representing hours.
        - `loadAutoSyncInterval` now returns an `Int` representing hours, defaulting to 12 if not set.
    - `SettingsEvent.UpdateSyncInterval`: `intervalInHours` is now `Int`.
    - `SyncUiState`: `autoSyncInterval` is now `Int` and defaults to 1.
    - `SettingsUiState`: `syncIntervalInHours` is now `Int` and defaults to 1.
- **Settings Screen:**
    - `SettingsServerConfigurationDialog`:
        - The `currentIntervalHours` parameter is now used.
        - `intervalOptions` are now `listOf(1, 6, 12, 24)` representing hours.
        - The `OutlinedTextField` and `DropdownMenuItem` now display "hours" instead of "mins".
    - `SettingsScreen.kt`: Replaced `Divider` with `HorizontalDivider` for consistency.
- **Sync Module:**
    - `SyncViewModel`:
        - `updateAutoSyncInterval` now accepts an `Int`.
        - When scheduling periodic sync, `intervalHours` (Int) is converted to `Long` for `syncManager.schedulePeriodicSync`.
    - `SyncScreen.kt`: The auto-sync interval display now appends "hours".
- **ViewModel Logic:**
    - `SettingsViewModel.updateSyncInterval` now correctly handles the interval as `Int` (hours).

The default auto-sync interval is now 1 hour. This change provides a more user-friendly and consistent way to manage auto-sync frequency.

* Refactor: Enhance logging in SyncWorkers and add SyncUseCase to DI

This commit introduces more detailed logging within `PatientsSyncWorker` and `EncountersSyncWorker` to provide better visibility into their execution lifecycle and outcomes. This includes logging start, success, retry, and failure events, as well as specific errors encountered during API calls or local database operations.

Key changes:

- **`PatientsSyncWorker.kt` & `EncountersSyncWorker.kt`:**
    - Replaced `context: Context` with `appContext: Context` in the constructor for clarity.
    - Added `AppLogger.d` statements to indicate the start and end (success/retry) of the `doWork` method.
    - Improved error logging messages to be more specific, e.g., distinguishing between API rejection and local marking failures.
    - In `PatientsSyncWorker`, explicitly typed `fhirPatient` as `org.hl7.fhir.r4.model.Patient`.
    - In `EncountersSyncWorker`, corrected the `uuid` and `visitUuid` mapping in `buildEncounterPayload`. It now correctly uses `entity.id` for `uuid` and `entity.visitUuid` for `visitUuid`.

- **`SyncModule.kt`:**
    - Added a Hilt provider for `SyncUseCase` as a singleton. This ensures that the use case can be injected into the workers and other components.

* Build: Update OkHttp version to 5.1.0

This commit updates the OkHttp library version from `4.12.0` to `5.1.0` in the `gradle/libs.versions.toml` file.

* Build: Update Java version to 17 and add WorkManager dependencies

This commit updates the Java version from 11 to 17 across all modules. This involves changing `sourceCompatibility`, `targetCompatibility`, and `jvmToolchain` settings in the respective `build.gradle.kts` files.

Additionally, WorkManager related dependencies have been updated and added:
- In `sync/build.gradle.kts`:
    - `libs.hilt.work.compiler` is now a `ksp` dependency.
    - `libs.work.runtime` is added as an `implementation` dependency.
- In `app/build.gradle.kts`:
    - `libs.work.runtime` and `libs.androidx.startup.runtime` are added as `implementation` dependencies.

* Feat: Implement foreground service with notifications for sync workers

This commit introduces foreground service behavior for `PatientsSyncWorker` and `EncountersSyncWorker` to ensure reliable background execution and provide users with visual feedback on the synchronization progress via notifications.

**Key Changes:**

- **`PatientsSyncWorker.kt` & `EncountersSyncWorker.kt`:**
    - Implemented `setForeground()` using `ForegroundInfo` to run the workers as foreground services.
    - Added `ensureNotificationChannelExists()` to create a dedicated notification channel for sync updates if it doesn't exist.
    - Added `createForegroundNotification()` helper function to build and customize notifications for different sync states (starting, progress, success, retry, error).
    - Notifications now display:
        - Initial status ("Starting synchronization...").
        - Progress updates (e.g., "Syncing X of Y patients/encounters...").
        - Completion status ("Sync Complete", "No unsynced data found.").
        - Retry status ("Sync Needs Retry...").
        - Error messages if synchronization fails.
    - The notification manager is used to create and update these notifications throughout the worker's lifecycle.
    - `PATIENT_SYNC_NOTIFICATION_ID` and `ENCOUNTER_SYNC_NOTIFICATION_ID` are used to manage distinct notifications for each worker.

- **`UgandaEMRMobile.kt` (Application class):**
    - Added `createNotificationChannel()` called in `onCreate()` to ensure the `SYNC_NOTIFICATION_CHANNEL_ID` is created on app startup. This is redundant due to the worker-level check but ensures channel availability.

- **`NotificationConstants.kt` (New in `core.utils`):**
    - Introduced constants for notification channel ID, name, description, and notification IDs for patient and encounter sync. This centralizes notification-related values.
    - `SYNC_NOTIFICATION_CHANNEL_ID`: "sync_channel"
    - `SYNC_NOTIFICATION_CHANNEL_NAME`: "UgandaEMR Sync Status"
    - `SYNC_NOTIFICATION_CHANNEL_DESCRIPTION`: "Shows ongoing status of data synchronization."
    - `PATIENT_SYNC_NOTIFICATION_ID`: 1001
    - `ENCOUNTER_SYNC_NOTIFICATION_ID`: 1002

These changes improve the robustness of the sync process and enhance user experience by providing transparent feedback on background operations.

* Refactor: Remove unused UseCase providers from DI modules

This commit removes the Hilt providers for `FormsUseCase` and `PatientsUseCase` from `FormModule.kt`, and `SyncUseCase` from `SyncModule.kt`. These use cases are no longer directly injected as they are typically instantiated within ViewModels or other components that consume them, simplifying the dependency graph.

* Feat: Add notification permission request and WorkManagerInitializer

This commit introduces a permission request for notifications on Android 13+ (TIRAMISU) and configures `WorkManagerInitializer` using `androidx.startup`.

Key changes:

- **`MainActivity.kt`:**
    - Added a `requestNotificationPermissionLauncher` to handle the result of the notification permission request.
    - Implemented `askNotificationPermission()`:
        - Checks if the Android version is TIRAMISU or higher.
        - Checks if the `POST_NOTIFICATIONS` permission is already granted.
        - If not granted and rationale should be shown, it logs an info message (UI for rationale not implemented).
        - Launches the permission request.
    - `askNotificationPermission()` is called in `onCreate()`.
    - Logging is added to track permission grant/denial.

- **`app/src/main/AndroidManifest.xml`:**
    - Added `<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>`.
    - Added an `androidx.startup.InitializationProvider` to explicitly initialize `WorkManager`.
        - The `WorkManagerInitializer` is configured to be handled by `androidx.startup`, and the default node for it is removed to prevent conflicts.

- **`gradle/libs.versions.toml`:**
    - Added `startup-runtime = "1.1.1"` version.
    - Added `androidx-startup-runtime` library alias.
    - Updated `hilt-work-compiler` group to `androidx.hilt`.
    - Added `work-runtime` library alias.

* Refactor: Improve notification updates in SyncWorkers

This commit enhances the foreground notification updates within `PatientsSyncWorker` and `EncountersSyncWorker` to provide more granular feedback to the user during the synchronization process.

**Key changes:**

- **`PatientsSyncWorker.kt` & `EncountersSyncWorker.kt`:**
    - **Notification Channel:** Ensured `SYNC_NOTIFICATION_CHANNEL_ID`, `SYNC_NOTIFICATION_CHANNEL_NAME`, and `SYNC_NOTIFICATION_CHANNEL_DESCRIPTION` constants are used when creating the notification channel.
    - **Initial Notification:** Set the initial foreground notification when the worker starts.
    - **Progress Notifications:**
        - Display the number of items being synced (e.g., "Syncing 0 of X patients...").
        - Update progress dynamically as each item is successfully synced or encounters an error.
    - **Error Notifications:**
        - If reading from the local database fails, a specific error notification is shown.
        - If an API call fails or marking an item as synced fails locally, the notification is updated to reflect this, including the specific error message and current progress.
        - The notification title changes slightly (e.g., "Syncing Patient Data (with issues)") to indicate problems.
    - **Completion Notifications:**
        - If no items need syncing, a "No unsynced items found" notification is displayed.
        - On successful completion, a "Sync Complete" notification shows the total number of items synced.
        - If a retry is needed, a "Sync Needs Retry" notification is displayed.
    - **Notification Icon:** Set the small icon for notifications to `R.drawable.ic_launcher_foreground`.
    - **Clarity:** Removed redundant comments and improved logging messages.
    - In `PatientsSyncWorker.kt`, the `notificationManager` field was moved to the top of the class for better organization.

These changes provide users with clearer and more immediate feedback on the status and progress of data synchronization, including any errors encountered.

* Refactor: Remove unused `FormsDownloaded` event and simplify `SyncViewModel`

This commit removes the `FormsDownloaded` event from `SyncEvent.kt` as it was no longer in use.

Additionally, the following changes were made in `SyncViewModel.kt`:
- Removed the `updateAutoSyncInterval` private function. This logic was previously responsible for updating the auto-sync interval in preferences and re-scheduling WorkManager tasks if auto-sync was enabled. This functionality is now likely handled elsewhere or has been deprecated.
- Removed the corresponding `TODO()` block for `FormsDownloaded` in the `onEvent` handler.

* Refactor: Update Patient FHIR endpoint and notification comments

This commit updates the FHIR endpoint for saving patients from `/fhir/Patient` to `fhir2/Patient` in `FormApi.kt`.

Additionally, comments in `PatientsSyncWorker.kt` and `EncountersSyncWorker.kt` related to using constants for notification channel details and notification titles have been removed, as the constants were already in use.

* Refactor: Simplify report generation payload

This commit simplifies the `buildDataDefinitionPayload` method by removing the `indicator` parameter. The report columns are now derived from `reportRequest.reportIndicators` instead of `indicator.attributes`.

This change assumes that the necessary indicator information is already part of the `ReportRequest` object or can be accessed through it, streamlining the data flow for report generation.

* Refactor: Standardize attribute type names for consistency

This commit updates the `type` property in the `toAttribute()` extension functions for `EncounterType`, `Identifier`, and `PersonAttributeType` to use more descriptive and consistent naming conventions.

- `EncounterType`: Changed from "EncounterType" to "encounterType" (lowercase 'e').
- `Identifier`: Changed from "IDN" to "PatientIdentifier".
- `PersonAttributeType`: Changed from "PAT" to "PersonAttribute".

These changes improve clarity and align the attribute type names with their respective entities.

* Refactor: Prepend "/rest/v1/" to API endpoints and update savePatient

This commit updates the API endpoint paths across `AuthApi.kt` and `FormApi.kt` by prepending `/rest/v1/` to ensure consistency with the new API versioning scheme.

Additionally, the `savePatient` endpoint in `FormApi.kt` has been modified:
- The HTTP method changed from `POST` to `PUT`.
- The endpoint path now includes the patient's UUID: `fhir2/R4/Patient/{uuid}`.
- A new `@Path("uuid") patientId: String` parameter has been added to the method signature.

* Refactor: Improve patient data mapping and synchronization

This commit introduces several changes to enhance the mapping and synchronization of patient data, particularly focusing on identifiers and UUID handling.

Key changes:

- **`PatientEntity.kt`:**
    - Modified the `fromMap` function to use `"UUID"` as the key for retrieving the patient's ID from the map, falling back to a random UUID if not found. This standardizes the key used for patient UUID.

- **`PatientsSyncWorker.kt`:**
    - Updated `api.savePatient` call to include the patient's ID (`fhirPatient.idElement.idPart`) as part of the request. This ensures that the patient's existing ID is used when saving to the server, likely for update operations.

- **`PatientExtensions.kt` (`toMap` function):**
    - The `addIdentifier` logic now creates a more detailed `Identifier` object:
        - Sets `use` to `Identifier.IdentifierUse.OFFICIAL`.
        - Sets `type` to a `CodeableConcept` with a specific `Coding` (`code = "05a29f94-c0ed-11e2-94be-8c13b969e334"`) and `text = "OpenMRS ID"`.
        - This provides more context to the "OpenMRS ID" identifier.

- **`IndicatorRepository.kt`:**
    - Removed the `defaultSelectedAttributes` list.
    - Added a `"uuid"` attribute (`Attribute("uuid", "UUID", "Demographics", 1, false)`) to the `demographicsAttributes` list. This ensures that the patient UUID is included in the demographic attributes.

Release v0.0.14

04 Jul 08:35
f8752ef

Choose a tag to compare

UEM-27: Start Patient Visit (#13)

* UEM-27:Record Patient Vital Signs

* UEM-27:Record Patient Vital Signs

* UEM-27:Record Patient Vital Signs

Release v0.0.13

02 Jul 10:12
9263643

Choose a tag to compare

Merge pull request #10 from jabahum/refactor-questionnare-and-form-vi…

Release v0.0.12

30 Jun 17:55
ced10ea

Choose a tag to compare

Update android.yml

Release v0.0.11

30 Jun 08:38
2208238

Choose a tag to compare

Update android.yml