Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions android/buildSrc/src/main/kotlin/BuildConfigs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ object BuildConfigs {
const val minSdk = 26
const val compileSdk = 35
const val targetSdk = 35
const val versionCode = 19
const val versionName = "2.2.2"
const val versionCode = 21
const val versionName = "2.2.3"
const val applicationId = "org.smartregister.opensrp"
const val jvmToolchain = 17
const val kotlinCompilerExtensionVersion = "1.5.14"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,34 +78,34 @@ constructor(
)

override suspend fun doWork(): Result {
kotlin
.runCatching {
saveSyncStartTimestamp()
setForeground(getForegroundInfo())
customResourceSyncService.runCustomResourceSync()
}
.onSuccess {
return super.doWork()
}
.onFailure { exception ->
when (exception) {
is HttpException -> {
val response = exception.response()
if (response != null && (400..503).contains(response.code())) {
Timber.e("HTTP exception ${response.code()} -> ${response.errorBody()}")
}
saveSyncStartTimestamp()
// setForeground can throw ForegroundServiceStartNotAllowedException on Android 12+ when the
// periodic sync fires while the app is in the background. Catch this separately so that a
// failed foreground-service start does not prevent the actual sync from running.
runCatching { setForeground(getForegroundInfo()) }
.onFailure { Timber.w(it, "Could not start foreground service for sync; proceeding anyway") }

return try {
customResourceSyncService.runCustomResourceSync()
super.doWork()
} catch (exception: Exception) {
when (exception) {
is HttpException -> {
val response = exception.response()
if (response != null && (400..503).contains(response.code())) {
Timber.e("HTTP exception ${response.code()} -> ${response.errorBody()}")
}
else -> Timber.e(exception)
}
syncListenerManager.emitSyncStatus(
SyncState(
counter = SYNC_COUNTER_1,
currentSyncJobStatus = CurrentSyncJobStatus.Failed(OffsetDateTime.now()),
),
)
return result()
else -> Timber.e(exception)
}
return Result.success()
syncListenerManager.emitSyncStatus(
SyncState(
counter = SYNC_COUNTER_1,
currentSyncJobStatus = CurrentSyncJobStatus.Failed(OffsetDateTime.now()),
),
)
result()
}
}

private fun result(): Result =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,7 @@ import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
Expand Down Expand Up @@ -112,65 +109,57 @@ private fun LoaderContent(
showBackground: Boolean,
showLineSpinIndicator: Boolean,
) {
val openDialog = remember { mutableStateOf(true) }
if (openDialog.value) {
Box(modifier.size(boxWidth, boxHeight)) {
Column(
modifier = modifier.padding(8.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
Box(modifier.size(boxWidth, boxHeight)) {
Column(
modifier = modifier.padding(8.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Surface(
modifier = modifier.size(boxWidth, boxHeight),
shape = RoundedCornerShape(8.dp),
color = if (showBackground) Color.Black.copy(alpha = 0.56f) else Color.Transparent,
) {
Surface(
modifier = modifier.size(boxWidth, boxHeight),
shape = RoundedCornerShape(8.dp),
color = if (showBackground) Color.Black.copy(alpha = 0.56f) else Color.Transparent,
Column(
modifier = modifier.padding(8.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Column(
modifier = modifier.padding(8.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
if (showLineSpinIndicator) {
LineSpinFadeLoaderProgressIndicator(
color = Color.White,
lineLength = 12f,
innerRadius = 16f,
)
} else {
CircularProgressIndicator(
color = Color.White,
strokeWidth = 3.dp,
modifier = modifier.testTag(LOADER_DIALOG_PROGRESS_BAR_TAG).size(progressBarSize),
)
}
if (showLineSpinIndicator) {
LineSpinFadeLoaderProgressIndicator(
color = Color.White,
lineLength = 12f,
innerRadius = 16f,
)
} else {
CircularProgressIndicator(
color = Color.White,
strokeWidth = 3.dp,
modifier = modifier.testTag(LOADER_DIALOG_PROGRESS_BAR_TAG).size(progressBarSize),
)
}

dialogMessage?.let {
Text(
text = it,
color = Color.White,
fontSize = 14.sp,
modifier = modifier.testTag(LOADER_DIALOG_PROGRESS_MSG_TAG).padding(top = 8.dp),
)
}
dialogMessage?.let {
Text(
text = it,
color = Color.White,
fontSize = 14.sp,
modifier = modifier.testTag(LOADER_DIALOG_PROGRESS_MSG_TAG).padding(top = 8.dp),
)
}

if (showPercentageProgress) {
Text(
fontSize = 15.sp,
color = Color.White,
text = "$currentPercentage%",
modifier = modifier.padding(top = 4.dp),
)
}
if (showPercentageProgress) {
Text(
fontSize = 15.sp,
color = Color.White,
text = "$currentPercentage%",
modifier = modifier.padding(top = 4.dp),
)
}
}
}
}
}
SideEffect {
if (currentPercentage >= 100) {
openDialog.value = false
}
}
}

@PreviewWithBackgroundExcludeGenerated
Expand Down
10 changes: 5 additions & 5 deletions android/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ androidx-test= "1.6.2"
appcompat = "1.7.0"
benchmark-junit = "1.3.3"
cardview = "1.0.0"
common-utils = "1.0.3-SNAPSHOT"
common-utils = "1.0.5"
commons-logging = "1.3.5"
compose-activity = "1.9.3"
compose-fragment = "1.8.5"
Expand All @@ -20,18 +20,18 @@ core-ktx = "1.13.1"
core-testing = "2.2.0"
coverallsGradlePlugin = "2.12.2"
cqfFhirCr = "3.8.0"
cqfFhirCql = "3.8.0-OpenSRP-2-SNAPSHOT"
cqfFhirCql = "3.8.0-opensrp-hotfix-SNAPSHOT"
dagger-hilt = "2.57.1"
datastore = "1.1.2"
desugar-jdk-libs = "2.1.5"
dokkaBase = "1.9.20"
easyRulesCore = "4.1.1-SNAPSHOT"
easyRulesCore = "4.1.1"
espresso-core = "3.6.1"
fhir-sdk-common = "0.1.0-alpha05-preview3-SNAPSHOT"
fhir-sdk-contrib-barcode = "0.1.0-beta3-preview7-SNAPSHOT"
fhir-sdk-contrib-locationwidget = "0.1.0-alpha01-preview2-SNAPSHOT"
fhir-sdk-data-capture = "1.3.0-preview1-SNAPSHOT"
fhir-sdk-engine = "1.2.0-preview-SNAPSHOT"
fhir-sdk-engine = "1.2.0-preview1-SNAPSHOT"
fhir-sdk-knowledge = "0.1.0-beta01-preview1-SNAPSHOT"
fhir-sdk-workflow = "0.1.0-beta01-preview2-SNAPSHOT"
generativeai = "0.9.0"
Expand Down Expand Up @@ -73,7 +73,7 @@ okhttp = "4.12.0"
okhttp-logging-interceptor = "4.12.0"
orchestrator = "1.5.1"
owasp = "8.2.1"
p2p-lib = "0.6.11-SNAPSHOT"
p2p-lib = "0.6.11"
playServicesLocation = "21.3.0"
playServicesTasks = "18.2.0"
preference-ktx = "1.2.1"
Expand Down
18 changes: 18 additions & 0 deletions android/quest/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import com.android.build.api.variant.FilterConfiguration.FilterType
import com.android.build.gradle.internal.api.BaseVariantOutputImpl
import io.sentry.android.gradle.extensions.InstrumentationFeature
import io.sentry.android.gradle.instrumentation.logcat.LogcatLevel
import java.io.FileReader
Expand Down Expand Up @@ -273,6 +274,13 @@ android {
manifestPlaceholders["appLabel"] = "eCHIS"
}

create("echisTraining") {
dimension = "apps"
applicationId = "ug.go.health.echis.training"
versionNameSuffix = "-echisTraining"
manifestPlaceholders["appLabel"] = "Training eCHIS"
}

create("sidBunda") {
dimension = "apps"
applicationIdSuffix = ".sidBunda"
Expand Down Expand Up @@ -433,6 +441,16 @@ android {
"app_name",
"\"${variant.mergedFlavor.manifestPlaceholders["appLabel"]}\"",
)

variant.outputs.all {
val output = this as BaseVariantOutputImpl
val buildTypeName = variant.buildType.name
output.outputFileName =
output.outputFileName.replace(
"-$buildTypeName.apk",
"-${BuildConfigs.versionName}(${BuildConfigs.versionCode})-$buildTypeName.apk",
)
}
}

applicationVariants.all {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import androidx.navigation.compose.rememberNavController
import androidx.paging.PagingData
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import com.google.android.fhir.sync.CurrentSyncJobStatus
import kotlinx.coroutines.flow.flowOf
import org.hl7.fhir.r4.model.ResourceType
import org.smartregister.fhircore.engine.R
Expand Down Expand Up @@ -172,7 +173,15 @@ fun RegisterScreen(
},
) { innerPadding ->
Box(modifier = modifier.padding(innerPadding)) {
if (!BuildConfig.SKIP_AUTHENTICATION && registerUiState.isFirstTimeSync) {
// Tear down the first-time sync dialog (and its full-screen scrim) on a single authoritative
// signal: the sync reaching a terminal state. Relying on isFirstTimeSync alone left the scrim
// composed until a fragile, async recompute (timestamp write + register count) landed, which
// could lag badly or never complete on low-resource devices, stranding a touch-blocking
// overlay. The terminal CurrentSyncJobStatus is delivered to all listeners on the same event.
val syncTerminated =
appDrawerUIState.currentSyncJobStatus is CurrentSyncJobStatus.Succeeded ||
appDrawerUIState.currentSyncJobStatus is CurrentSyncJobStatus.Failed
if (!BuildConfig.SKIP_AUTHENTICATION && registerUiState.isFirstTimeSync && !syncTerminated) {
LoaderDialog(
modifier = modifier.testTag(FIRST_TIME_SYNC_DIALOG),
percentageProgressFlow = flowOf(appDrawerUIState.percentageProgress ?: 0),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,19 @@ constructor(
if (registerId.isNotEmpty()) {
val paramsMap: Map<String, String> = params.toParamDataMap()
val currentRegisterConfiguration = retrieveRegisterConfiguration(registerId, paramsMap)

fun buildRegisterUiState() =
RegisterUiState(
screenTitle = currentRegisterConfiguration.registerTitle ?: screenTitle,
isFirstTimeSync = isFirstTimeSync(),
registerConfiguration = currentRegisterConfiguration,
registerId = registerId,
progressPercentage = _percentageProgress,
isSyncUpload = _isUploadSync,
currentSyncJobStatus = _currentSyncJobStatusFlow,
params = params?.toList() ?: emptyList(),
)

if (currentRegisterConfiguration.infiniteScroll) {
registerData.value = getPagerFlow(currentRegisterConfiguration.id, clearCache)
} else {
Expand Down Expand Up @@ -666,20 +679,12 @@ constructor(
)
.toInt(),
)

registerUiState.value = buildRegisterUiState()
}
}

registerUiState.value =
RegisterUiState(
screenTitle = currentRegisterConfiguration.registerTitle ?: screenTitle,
isFirstTimeSync = isFirstTimeSync(),
registerConfiguration = currentRegisterConfiguration,
registerId = registerId,
progressPercentage = _percentageProgress,
isSyncUpload = _isUploadSync,
currentSyncJobStatus = _currentSyncJobStatusFlow,
params = params?.toList() ?: emptyList(),
)
registerUiState.value = buildRegisterUiState()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,10 @@ class AppSettingViewModelTest : RobolectricTest() {
appSettingViewModel.configurationRegistry.fetchRemoteCompositionByAppId(any())
}
coVerify(exactly = 0) {
appSettingViewModel.configurationRegistry.fetchRemoteImplementationGuideByAppId(any(), any())
appSettingViewModel.configurationRegistry.fetchRemoteImplementationGuideByAppId(
any(),
any()
)
}
}
}
Expand Down Expand Up @@ -484,7 +487,10 @@ class AppSettingViewModelTest : RobolectricTest() {
}

coEvery {
appSettingViewModel.configurationRegistry.fetchRemoteImplementationGuideByAppId(any(), any())
appSettingViewModel.configurationRegistry.fetchRemoteImplementationGuideByAppId(
any(),
any()
)
} returns null
coEvery {
appSettingViewModel.configurationRegistry.fetchRemoteCompositionByAppId(any())
Expand Down