Skip to content

Commit

Permalink
Include stack widget when decoding images and handle null binary exce…
Browse files Browse the repository at this point in the history
…ptions. (#3356)

* Include stack widget when decoding images and handle null binary exceptions.

Signed-off-by: Lentumunai-Mark <[email protected]>

* Update test name to match the description.

Signed-off-by: Lentumunai-Mark <[email protected]>

* Include map to store bitmaps.

Signed-off-by: Lentumunai-Mark <[email protected]>

* Update LocationService.kt

* Update LocationService.kt

* Improve test coverage for decode image bit.

Signed-off-by: Lentumunai-Mark <[email protected]>

* Remove commented pieces.

Signed-off-by: Lentumunai-Mark <[email protected]>

* Update Image.kt

* Resolve failing test.

Signed-off-by: Lentumunai-Mark <[email protected]>

* remove unused comments.

Signed-off-by: Lentumunai-Mark <[email protected]>

* Refactor exception test.

Signed-off-by: Lentumunai-Mark <[email protected]>

* Fix spotless check fail.

Signed-off-by: Lentumunai-Mark <[email protected]>

* remove decoded bitmap from the image config.

Signed-off-by: Lentumunai-Mark <[email protected]>

* Run spotless check.

Signed-off-by: Lentumunai-Mark <[email protected]>

* Change map type for decoded images.

Signed-off-by: Lentumunai-Mark <[email protected]>

* Resolve failing build on CI

Signed-off-by: Lentumunai-Mark <[email protected]>

* Include keys for map test instances.

Signed-off-by: Lentumunai-Mark <[email protected]>

---------

Signed-off-by: Lentumunai-Mark <[email protected]>
  • Loading branch information
Lentumunai-Mark authored Jul 18, 2024
1 parent 9ef20df commit de2c397
Show file tree
Hide file tree
Showing 20 changed files with 281 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ package org.smartregister.fhircore.engine.configuration

import android.content.Context
import android.database.SQLException
import android.graphics.Bitmap
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.ui.state.ToggleableState
import ca.uhn.fhir.context.ConfigurationException
import ca.uhn.fhir.context.FhirContext
Expand Down Expand Up @@ -102,6 +104,7 @@ constructor(

val configsJsonMap = mutableMapOf<String, String>()
val configCacheMap = mutableMapOf<String, Configuration>()
val decodedImageMap = mutableStateMapOf<String, Bitmap>()
val localizationHelper: LocalizationHelper by lazy { LocalizationHelper(this) }
private val supportedFileExtensions = listOf("json", "properties")
private var _isNonProxy = BuildConfig.IS_NON_PROXY_APK
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,8 @@

package org.smartregister.fhircore.engine.configuration.navigation

import android.graphics.Bitmap
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable
import org.smartregister.fhircore.engine.domain.model.ActionConfig
import org.smartregister.fhircore.engine.util.extension.interpolate
Expand All @@ -46,7 +44,6 @@ data class ImageConfig(
val alpha: Float = 1.0f,
val imageType: ImageType = ImageType.SVG,
val contentScale: ContentScaleType = ContentScaleType.FIT,
@Contextual var decodedBitmap: Bitmap? = null,
) : Parcelable, java.io.Serializable {
fun interpolate(computedValuesMap: Map<String, Any>): ImageConfig {
return this.copy(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.smartregister.fhircore.quest.integration.ui.shared.components

import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertHasClickAction
import androidx.compose.ui.test.assertIsDisplayed
Expand Down Expand Up @@ -44,6 +45,7 @@ class CardViewTest {
viewProperties = viewProperties,
resourceData = resourceData,
navController = TestNavHostController(LocalContext.current),
decodedImageMap = mutableStateMapOf(),
)
}
composeTestRule.onNodeWithText("IMMUNIZATIONS").assertIsDisplayed()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.smartregister.fhircore.quest.integration.ui.shared.components

import android.graphics.Bitmap
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
Expand Down Expand Up @@ -369,17 +370,20 @@ class ViewGeneratorTest {
@Test
fun testImageIsRenderedFromDecodedBitmap() {
composeRule.setContent {
val decodedImageMap = mutableStateMapOf<String, Bitmap>()
decodedImageMap["testImageReference"] = Bitmap.createBitmap(100, 16, Bitmap.Config.ARGB_8888)
GenerateView(
properties =
ImageProperties(
imageConfig =
ImageConfig(
ICON_TYPE_REMOTE,
decodedBitmap = Bitmap.createBitmap(100, 16, Bitmap.Config.ARGB_8888),
reference = "testImageReference",
),
),
resourceData = resourceData,
navController = TestNavHostController(LocalContext.current),
decodedImageMap = decodedImageMap,
)
}
composeRule
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package org.smartregister.fhircore.quest.ui.bottomsheet

import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.remember
import androidx.navigation.NavController
import org.smartregister.fhircore.engine.configuration.view.ViewProperties
import org.smartregister.fhircore.engine.domain.model.ResourceData
Expand All @@ -32,5 +34,6 @@ fun SummaryBottomSheetView(
viewProperties = properties,
resourceData = resourceData,
navController = navController,
decodedImageMap = remember { mutableStateMapOf() },
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,11 @@ constructor(
it.menuIconConfig?.type == ICON_TYPE_REMOTE &&
!it.menuIconConfig!!.reference.isNullOrEmpty()
}
.decodeBinaryResourcesToBitmap(viewModelScope, registerRepository)
.decodeBinaryResourcesToBitmap(
viewModelScope,
registerRepository,
configurationRegistry.decodedImageMap,
)
}

fun retrieveAppMainUiState(refreshAll: Boolean = true) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ class ProfileFragment : Fragment() {
profileUiState = profileViewModel.profileUiState.value,
onEvent = profileViewModel::onEvent,
snackStateFlow = profileViewModel.snackBarStateFlow,
decodedImageMap = configurationRegistry.decodedImageMap,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.smartregister.fhircore.quest.ui.profile

import android.graphics.Bitmap
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
Expand Down Expand Up @@ -88,6 +89,7 @@ fun ProfileScreen(
navController: NavController,
profileUiState: ProfileUiState,
snackStateFlow: SharedFlow<SnackBarMessageConfig>,
decodedImageMap: MutableMap<String, Bitmap> = mutableMapOf(),
onEvent: (ProfileEvent) -> Unit,
) {
val scaffoldState = rememberScaffoldState()
Expand Down Expand Up @@ -170,6 +172,7 @@ fun ProfileScreen(
resourceData =
profileUiState.resourceData ?: ResourceData("", ResourceType.Patient, emptyMap()),
navController = navController,
decodedImageMap = profileUiState.decodedImageMap,
)
}
}
Expand Down Expand Up @@ -244,6 +247,7 @@ private fun RenderSimpleAppTopBar(
resourceData =
profileUiState.resourceData ?: ResourceData("", ResourceType.Patient, emptyMap()),
navController = navController,
decodedImageMap = profileUiState.decodedImageMap,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

package org.smartregister.fhircore.quest.ui.profile

import android.graphics.Bitmap
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.snapshots.SnapshotStateMap
import org.smartregister.fhircore.engine.configuration.app.SnackBarThemeConfig
import org.smartregister.fhircore.engine.configuration.profile.ProfileConfiguration
import org.smartregister.fhircore.engine.domain.model.ResourceData
Expand All @@ -25,4 +28,5 @@ data class ProfileUiState(
val profileConfiguration: ProfileConfiguration? = null,
val snackBarTheme: SnackBarThemeConfig = SnackBarThemeConfig(),
val showDataLoadProgressIndicator: Boolean = true,
val decodedImageMap: SnapshotStateMap<String, Bitmap> = mutableStateMapOf(),
)
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,11 @@ constructor(
)
profileConfig.overFlowMenuItems
.filter { it.icon != null && !it.icon!!.reference.isNullOrEmpty() }
.decodeBinaryResourcesToBitmap(viewModelScope, registerRepository)
.decodeBinaryResourcesToBitmap(
viewModelScope,
registerRepository,
configurationRegistry.decodedImageMap,
)
}

suspend fun retrieveProfileUiState(
Expand Down Expand Up @@ -127,6 +131,7 @@ constructor(
profileConfiguration = profileConfigs,
snackBarTheme = applicationConfiguration.snackBarTheme,
showDataLoadProgressIndicator = false,
decodedImageMap = configurationRegistry.decodedImageMap,
)

profileConfigs.views.retrieveListProperties().forEach { listProperties ->
Expand All @@ -140,15 +145,13 @@ constructor(
listResourceDataStateMap[listProperties.id] != null &&
listResourceDataStateMap[listProperties.id]?.size!! > 0
) {
val computedMap = listResourceDataStateMap[listProperties.id]?.get(0)?.computedValuesMap
viewModelScope.launch(dispatcherProvider.io()) {
if (computedMap != null) {
loadRemoteImagesBitmaps(
profileConfiguration.views,
registerRepository,
computedMap,
)
}
listResourceDataStateMap[listProperties.id]?.forEach { resourceData ->
loadRemoteImagesBitmaps(
profileConfiguration.views,
registerRepository,
resourceData.computedValuesMap,
configurationRegistry.decodedImageMap,
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.material.Divider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.dp
Expand Down Expand Up @@ -68,11 +70,14 @@ fun RegisterCardList(
contentType = pagingItems.itemContentType(),
) { index ->
// Register card UI rendered dynamically should be wrapped in a column
Column(modifier = modifier.fillMaxWidth().padding(horizontal = 16.dp)) {
Column(
modifier = modifier.fillMaxWidth().padding(horizontal = 16.dp),
) {
ViewRenderer(
viewProperties = registerCardConfig.views,
resourceData = pagingItems[index]!!,
navController = navController,
decodedImageMap = remember { mutableStateMapOf() },
)
}
Divider(color = DividerColor, thickness = 1.dp)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.smartregister.fhircore.quest.ui.shared.components

import android.graphics.Bitmap
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
Expand All @@ -26,6 +27,9 @@ import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Card
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshots.SnapshotStateMap
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
Expand Down Expand Up @@ -58,6 +62,7 @@ fun CardView(
viewProperties: CardViewProperties,
resourceData: ResourceData,
navController: NavController,
decodedImageMap: SnapshotStateMap<String, Bitmap>,
) {
// Check if card is visible
if (viewProperties.visible.toBoolean()) {
Expand Down Expand Up @@ -109,6 +114,7 @@ fun CardView(
viewProperties = viewProperties.content,
resourceData = resourceData,
navController = navController,
decodedImageMap = decodedImageMap,
)
}
}
Expand Down Expand Up @@ -147,6 +153,7 @@ private fun CardViewWithoutPaddingPreview() {
),
resourceData = ResourceData("id", ResourceType.Patient, emptyMap()),
navController = rememberNavController(),
decodedImageMap = remember { mutableStateMapOf() },
)
}
}
Expand Down Expand Up @@ -183,6 +190,7 @@ private fun CardViewWithPaddingPreview() {
),
resourceData = ResourceData("id", ResourceType.Patient, emptyMap()),
navController = rememberNavController(),
decodedImageMap = remember { mutableStateMapOf() },
)
}
}
Expand All @@ -205,6 +213,7 @@ private fun CardViewWithoutPaddingAndHeaderPreview() {
),
resourceData = ResourceData("id", ResourceType.Patient, emptyMap()),
navController = rememberNavController(),
decodedImageMap = remember { mutableStateMapOf() },
)
}
}
Expand Down Expand Up @@ -272,6 +281,7 @@ private fun CardViewImageWithItems() {
),
resourceData = ResourceData("id", ResourceType.Patient, emptyMap()),
navController = rememberNavController(),
decodedImageMap = remember { mutableStateMapOf() },
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.smartregister.fhircore.quest.ui.shared.components

import android.content.Context
import android.graphics.Bitmap
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
Expand Down Expand Up @@ -56,6 +57,8 @@ import org.smartregister.fhircore.engine.domain.model.ResourceData
import org.smartregister.fhircore.engine.domain.model.ViewType
import org.smartregister.fhircore.engine.ui.theme.DangerColor
import org.smartregister.fhircore.engine.util.annotation.PreviewWithBackgroundExcludeGenerated
import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid
import org.smartregister.fhircore.engine.util.extension.interpolate
import org.smartregister.fhircore.engine.util.extension.parseColor
import org.smartregister.fhircore.engine.util.extension.retrieveResourceId
import org.smartregister.fhircore.quest.ui.main.components.SIDE_MENU_ICON
Expand All @@ -73,6 +76,7 @@ fun Image(
imageProperties: ImageProperties = ImageProperties(viewType = ViewType.IMAGE, size = 24),
navController: NavController,
resourceData: ResourceData? = null,
decodedImageMap: MutableMap<String, Bitmap> = mutableMapOf(),
) {
val imageConfig = imageProperties.imageConfig
val colorTint = tint ?: imageProperties.imageConfig?.color.parseColor()
Expand All @@ -98,6 +102,7 @@ fun Image(
resourceData = resourceData,
modifier = modifier,
context = cotext,
decodedImageMap = decodedImageMap,
)
}
} else {
Expand All @@ -110,6 +115,7 @@ fun Image(
resourceData = resourceData,
modifier = modifier,
context = cotext,
decodedImageMap = decodedImageMap,
)
}
}
Expand All @@ -125,6 +131,7 @@ fun ClickableImageIcon(
navController: NavController,
resourceData: ResourceData? = null,
context: Context? = null,
decodedImageMap: MutableMap<String, Bitmap> = mutableMapOf(),
) {
Box(
contentAlignment = Alignment.Center,
Expand Down Expand Up @@ -176,24 +183,32 @@ fun ClickableImageIcon(
}
}
ICON_TYPE_REMOTE ->
if (imageConfig.decodedBitmap != null) {
if (decodedImageMap.isNotEmpty()) {
val imageType = imageProperties.imageConfig?.imageType
val colorFilter =
if (imageType == ImageType.SVG || imageType == ImageType.PNG) tint else null
val contentScale =
convertContentScaleTypeToContentScale(imageProperties.imageConfig!!.contentScale)
Image(
modifier =
Modifier.testTag(SIDE_MENU_ITEM_REMOTE_ICON_TEST_TAG)
.conditional(paddingEnd != null, { padding(end = paddingEnd?.dp!!) })
.align(Alignment.Center)
.fillMaxSize(0.9f),
bitmap = imageConfig.decodedBitmap!!.asImageBitmap(),
contentDescription = null,
alpha = imageProperties.imageConfig!!.alpha,
contentScale = contentScale,
colorFilter = colorFilter?.let { ColorFilter.tint(it) },
)
val decodedImage =
decodedImageMap[
imageConfig.reference
?.interpolate(resourceData!!.computedValuesMap)
?.extractLogicalIdUuid(),
]
if (decodedImage != null) {
Image(
modifier =
Modifier.testTag(SIDE_MENU_ITEM_REMOTE_ICON_TEST_TAG)
.conditional(paddingEnd != null, { padding(end = paddingEnd?.dp!!) })
.align(Alignment.Center)
.fillMaxSize(0.9f),
bitmap = decodedImage!!.asImageBitmap(),
contentDescription = null,
alpha = imageProperties.imageConfig!!.alpha,
contentScale = contentScale,
colorFilter = colorFilter?.let { ColorFilter.tint(it) },
)
}
}
}
}
Expand Down
Loading

0 comments on commit de2c397

Please sign in to comment.