diff --git a/navi-uitron/src/main/java/com/navi/uitron/model/ui/ScratchCardProperty.kt b/navi-uitron/src/main/java/com/navi/uitron/model/ui/ScratchCardProperty.kt index dc4eb49..ee3e0ad 100644 --- a/navi-uitron/src/main/java/com/navi/uitron/model/ui/ScratchCardProperty.kt +++ b/navi-uitron/src/main/java/com/navi/uitron/model/ui/ScratchCardProperty.kt @@ -1,6 +1,6 @@ /* * - * * Copyright © 2024 by Navi Technologies Limited + * * Copyright © 2024-2025 by Navi Technologies Limited * * All rights reserved. Strictly confidential * */ @@ -12,7 +12,9 @@ data class ScratchCardProperty( var threshold: Float? = DEFAULT_THRESHOLD_AREA_SCRATCHED, var imageScale: Float? = DEFAULT_SCRATCH_CARD_SCALE, var pathThickness: Float? = DEFAULT_PATH_THICKNESS, + val placeHolderImage: String?, ) : BaseProperty() { + companion object { const val DEFAULT_THRESHOLD_AREA_SCRATCHED = 0.2f const val DEFAULT_SCRATCH_CARD_SCALE = 1.4f diff --git a/navi-uitron/src/main/java/com/navi/uitron/render/ScratchCardRenderer.kt b/navi-uitron/src/main/java/com/navi/uitron/render/ScratchCardRenderer.kt index 7e35247..134c026 100644 --- a/navi-uitron/src/main/java/com/navi/uitron/render/ScratchCardRenderer.kt +++ b/navi-uitron/src/main/java/com/navi/uitron/render/ScratchCardRenderer.kt @@ -27,7 +27,6 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.scale import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Rect @@ -39,6 +38,7 @@ import androidx.compose.ui.graphics.Path import androidx.compose.ui.graphics.asAndroidBitmap import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.drawscope.clipPath +import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.pointerInteropFilter import androidx.compose.ui.layout.layoutId import androidx.compose.ui.layout.onGloballyPositioned @@ -62,13 +62,19 @@ import com.navi.uitron.utils.KEY_MVEL_ACTION import com.navi.uitron.utils.KEY_PROPERTY import com.navi.uitron.utils.KEY_UI_TRON_DATA import com.navi.uitron.utils.UI_TRON_VM +import com.navi.uitron.utils.dpToPx import com.navi.uitron.utils.orFalse import com.navi.uitron.utils.orTrue import com.navi.uitron.utils.orValue +import com.navi.uitron.utils.orZero import com.navi.uitron.utils.setHeight import com.navi.uitron.utils.setTag import com.navi.uitron.utils.setWidth import com.navi.uitron.viewmodel.UiTronViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch class ScratchCardRenderer( private val childrenComposeViews: List, @@ -130,36 +136,80 @@ class ScratchCardRenderer( uiTronScratchCardData: ScratchCardData?, uiTronViewModel: UiTronViewModel, ) { - val uiTronProvider = remember { UiTronSdkManager.getDependencyProvider() } - val isImagePresentLocally by - remember(property.iconUrl) { - mutableStateOf(uiTronProvider.getIconResourceId(property.iconUrl)) + val placeHolderImageResource = + remember(property.placeHolderImage) { + uiTronProvider.getIconResourceId(property.placeHolderImage) } + val iconResourceId = + remember(property.iconUrl) { uiTronProvider.getIconResourceId(property.iconUrl) } + + val coroutineScope = rememberCoroutineScope() val context = LocalContext.current val localImageBitmap by remember { mutableStateOf( - AppCompatResources.getDrawable(context, isImagePresentLocally ?: 0) - ?.toBitmap() - ?.copy(Bitmap.Config.ARGB_8888, true) - ?.asImageBitmap() + iconResourceId?.let { iconResourceId -> + AppCompatResources.getDrawable(context, iconResourceId) + ?.toBitmap() + ?.copy(Bitmap.Config.ARGB_8888, true) + ?.asImageBitmap() + } + ) + } + val placeholderImageBitmap by remember { + mutableStateOf( + placeHolderImageResource?.let { defaultImageResource -> + AppCompatResources.getDrawable(context, defaultImageResource) + ?.toBitmap() + ?.copy(Bitmap.Config.ARGB_8888, true) + ?.asImageBitmap() + } ) } var overlayImage by remember { mutableStateOf(localImageBitmap) } - + var showingPlaceHolderImage by remember { mutableStateOf(false) } + var job by remember { mutableStateOf(null) } fun updateOuterImageBitmap(state: AsyncImagePainter.State) { when (state) { is AsyncImagePainter.State.Success -> { - overlayImage = - state.result.drawable - .toBitmap() - .copy(Bitmap.Config.ARGB_8888, true) - .asImageBitmap() + job?.cancel() + if (showingPlaceHolderImage.not()) { + overlayImage = + state.result.drawable + .toBitmap() + .copy(Bitmap.Config.ARGB_8888, true) + .asImageBitmap() + } + } + is AsyncImagePainter.State.Error -> { + overlayImage = placeholderImageBitmap + } + is AsyncImagePainter.State.Loading -> { + overlayImage = placeholderImageBitmap + job = + coroutineScope.launch(Dispatchers.IO) { + delay(500) + showingPlaceHolderImage = true + } + } + else -> { + overlayImage = placeholderImageBitmap } - else -> Unit } } - if (overlayImage == null) { + + val height = dpToPx(property.height?.toIntOrNull().orZero()).toInt() + val width = dpToPx(property.width?.toIntOrNull().orZero()).toInt() + if (localImageBitmap == null && width > 0 && height > 0) { + rememberAsyncImagePainter( + model = + ImageRequest.Builder(LocalContext.current) + .data(property.iconUrl) + .size(height = dpToPx(height).toInt(), width = dpToPx(width).toInt()) + .build(), + onState = ::updateOuterImageBitmap, + ) + } else if (localImageBitmap == null) { rememberAsyncImagePainter( model = ImageRequest.Builder(LocalContext.current) @@ -189,7 +239,10 @@ class ScratchCardRenderer( modifier = Modifier.setWidth(property.width, property.screenWidthFraction) .setHeight(property.height) - .scale(imageScaleAnimation) + .graphicsLayer { + scaleX = imageScaleAnimation + scaleY = imageScaleAnimation + } .setTag(property) .layoutId(property.layoutId.orEmpty()), currentPathThickness = property.pathThickness ?: 150f,