diff --git a/android/app/src/main/java/com/naviapp/home/compose/home/ui/content/FrontLayerContent.kt b/android/app/src/main/java/com/naviapp/home/compose/home/ui/content/FrontLayerContent.kt index cea6fc205a..9b58686c13 100644 --- a/android/app/src/main/java/com/naviapp/home/compose/home/ui/content/FrontLayerContent.kt +++ b/android/app/src/main/java/com/naviapp/home/compose/home/ui/content/FrontLayerContent.kt @@ -64,6 +64,7 @@ import com.navi.uitron.model.UiTronResponse import com.naviapp.home.reducer.HpEffects import com.naviapp.home.reducer.HpEvents import com.naviapp.home.utils.getHomeWidgetAnimationSpec +import com.naviapp.home.utils.shouldRenderWidget import com.naviapp.home.viewmodel.HomeViewModel import com.naviapp.utils.Constants.HOME_SCREEN_IN_CAPS import kotlinx.coroutines.Dispatchers @@ -147,6 +148,13 @@ private fun MastheadWidget( mastheadWidget: MastheadWidgetData?, appBarHeight: Dp, ) { + val shouldRender = + mastheadWidget?.let { shouldRenderWidget(it.renderActions?.preRenderAction) } ?: false + + if (!shouldRender) { + Box(modifier = Modifier.padding(top = appBarHeight, bottom = 16.dp).fillMaxWidth()) + return + } mastheadWidget?.let { Box( modifier = diff --git a/android/app/src/main/java/com/naviapp/home/usecase/HomeContentProcessingUseCase.kt b/android/app/src/main/java/com/naviapp/home/usecase/HomeContentProcessingUseCase.kt index d4f0d0557f..c536dc990e 100644 --- a/android/app/src/main/java/com/naviapp/home/usecase/HomeContentProcessingUseCase.kt +++ b/android/app/src/main/java/com/naviapp/home/usecase/HomeContentProcessingUseCase.kt @@ -17,9 +17,11 @@ import com.navi.base.cache.util.NaviSharedDbKeys import com.navi.common.alchemist.model.AlchemistScreenDefinition import com.navi.common.utils.safeAsync import com.navi.common.utils.safeLaunch +import com.naviapp.home.model.HomePrioritySectionData import com.naviapp.home.model.HomeScreenContentResult import com.naviapp.home.model.SelectiveRefreshState import com.naviapp.home.reducer.HpEvents +import com.naviapp.home.utils.getFilteredWidgets import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred @@ -221,8 +223,15 @@ constructor( deserializer.setCacheEntity(cachedHomeContent.value) - val priorityContentSection = deserializer.getPrioritySection() - eventHandler(HpEvents.UpdatePrioritySectionData(priorityContentSection)) + val prioritySection = deserializer.getPrioritySection() + + val updatedPriorityContentSection = + HomePrioritySectionData( + content = getFilteredWidgets(prioritySection.content), + topNav = prioritySection.topNav, + ) + + eventHandler(HpEvents.UpdatePrioritySectionData(updatedPriorityContentSection)) trackEvent( TYPE_CACHE_PROCESS, mapOf(PARAM_CONTENT_STATUS to VALUE_PRIORITY_SECTION_UPDATED), @@ -247,7 +256,7 @@ constructor( screenLayoutDeferred.await() eventHandler( HpEvents.UpdateFrontLayerContent( - content = content, + content = getFilteredWidgets(content) ?: emptyList(), isRenderingFirstTime = true, ) ) @@ -406,7 +415,7 @@ constructor( eventHandler(HpEvents.UpdateScreenWithoutContent(screenContent)) - screenContent.screenStructure?.content?.widgets?.let { homeWidgets -> + getFilteredWidgets(screenContent.screenStructure?.content?.widgets)?.let { homeWidgets -> eventHandler(HpEvents.UpdateFrontLayerContent(homeWidgets, isFirstRender)) } } diff --git a/android/app/src/main/java/com/naviapp/home/utils/HomePageUtils.kt b/android/app/src/main/java/com/naviapp/home/utils/HomePageUtils.kt index d28a56ee62..08c25ee5f6 100644 --- a/android/app/src/main/java/com/naviapp/home/utils/HomePageUtils.kt +++ b/android/app/src/main/java/com/naviapp/home/utils/HomePageUtils.kt @@ -30,7 +30,11 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.sp import androidx.lifecycle.viewModelScope +import com.navi.base.utils.TrustedTimeAccessor +import com.navi.common.alchemist.model.AlchemistWidgetModelDefinition +import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper import com.navi.common.utils.CommonNaviAnalytics +import com.navi.common.utils.evaluateMvelExpression import com.navi.common.viewmodel.BaseVM import com.navi.design.font.FontWeightEnum import com.navi.design.font.getFontWeight @@ -40,8 +44,11 @@ import com.navi.design.theme.FF959595 import com.navi.naviwidgets.extensions.NaviText import com.navi.pay.common.theme.color.NaviPayColor import com.navi.uitron.model.UiTronResponse +import com.navi.uitron.model.action.MvelAction import com.navi.uitron.model.action.PublishEventAction import com.navi.uitron.model.action.PublishableEventData +import com.navi.uitron.model.data.UiTronActionData +import com.navi.uitron.model.data.UiTronActionType import com.navi.uitron.render.UiTronRenderer import com.navi.uitron.viewmodel.UiTronViewModel import com.naviapp.common.listeners.InAppUpdateBridge @@ -62,6 +69,8 @@ import com.naviapp.screenOverlay.nudge.domain.model.data.StaticNudgeUiState import com.naviapp.screenOverlay.nudge.domain.model.effect.NudgeEffect import com.naviapp.screenOverlay.nudge.domain.model.event.NudgeEvent import com.naviapp.screenOverlay.viewModel.ScreenOverlayVM +import com.naviapp.utils.Constants.CURRENT_TIME_IN_MILLIS +import com.naviapp.utils.Constants.FIREBASE_REMOTE_CONFIG_HELPER import com.naviapp.utils.Constants.HOME_SCREEN_IN_CAPS import com.naviapp.utils.Constants.HomePageConstants.HOME_PAGE_CTA_ACTION import com.naviapp.utils.Constants.HomePageConstants.HOME_PAGE_CTA_ACTION_STATE @@ -251,3 +260,35 @@ fun publishHomePageScrollActionState(viewModel: UiTronViewModel) { ) ) } + +// Filter widgets based on pre render action +fun getFilteredWidgets( + widgets: List>? +): List>? { + return widgets?.filter { widget -> + shouldRenderWidget(widget.widgetRenderActions?.preRenderAction) + } +} + +// Check if a widget should render based on preRenderAction +fun shouldRenderWidget(actionData: UiTronActionData?): Boolean { + val mvelActions = + actionData?.actions?.filterIsInstance()?.filter { + it.type == UiTronActionType.MvelAction.name + } + + return mvelActions?.all { isConditionSatisfied(it) } ?: true +} + +// Check if widget visibility condition is satisfied +fun isConditionSatisfied(action: MvelAction?): Boolean { + val expression = action?.inputExpression ?: return true + + val dataMap = + mapOf( + CURRENT_TIME_IN_MILLIS to TrustedTimeAccessor.getCurrentTimeMillis(), + FIREBASE_REMOTE_CONFIG_HELPER to FirebaseRemoteConfigHelper, + ) + + return evaluateMvelExpression(expression = expression, dataMap = dataMap, defaultValue = true) +} diff --git a/android/app/src/main/java/com/naviapp/utils/Constants.kt b/android/app/src/main/java/com/naviapp/utils/Constants.kt index 4599796e5d..81a548e189 100644 --- a/android/app/src/main/java/com/naviapp/utils/Constants.kt +++ b/android/app/src/main/java/com/naviapp/utils/Constants.kt @@ -232,6 +232,8 @@ object Constants { const val TOPIC = "topic" const val UNKNOWN_ERROR = "Unknown error" const val HOME_SCREEN_SCREEN_HASH = "HOME_SCREEN_SCREEN_HASH" + const val CURRENT_TIME_IN_MILLIS = "currentTimeMillis" + const val FIREBASE_REMOTE_CONFIG_HELPER = "FirebaseRemoteConfigHelper" object Notification { const val HIDE_NOTIFICATION_COUNT = "hideNotificationCount" diff --git a/android/navi-common/src/main/java/com/navi/common/model/common/MastheadWidgetData.kt b/android/navi-common/src/main/java/com/navi/common/model/common/MastheadWidgetData.kt index d65b6381ab..9446a0e848 100644 --- a/android/navi-common/src/main/java/com/navi/common/model/common/MastheadWidgetData.kt +++ b/android/navi-common/src/main/java/com/navi/common/model/common/MastheadWidgetData.kt @@ -7,10 +7,13 @@ package com.navi.common.model.common +import com.navi.common.alchemist.model.AlchemistRenderActions + data class MastheadWidgetData( val widgetData: UiTronActionAndResponse? = null, val backgroundIllustration: UiTronActionAndResponse? = null, val useLightStatusBar: Boolean = false, val updateIconsOnScroll: Boolean = false, val widgetBottomPadding: Int = 16, + val renderActions: AlchemistRenderActions? = null, ) diff --git a/android/navi-common/src/main/java/com/navi/common/utils/MvelUtils.kt b/android/navi-common/src/main/java/com/navi/common/utils/MvelUtils.kt new file mode 100644 index 0000000000..9ffe034c5f --- /dev/null +++ b/android/navi-common/src/main/java/com/navi/common/utils/MvelUtils.kt @@ -0,0 +1,30 @@ +/* + * + * * Copyright © 2025 by Navi Technologies Limited + * * All rights reserved. Strictly confidential + * + */ + +package com.navi.common.utils + +import com.google.gson.Gson +import org.mvel2.MVEL + +inline fun evaluateMvelExpression( + expression: String?, + dataMap: Map, + defaultValue: T, + onFailure: ((Throwable) -> Unit) = {}, +): T = + if (expression.isNullOrEmpty()) { + onFailure(IllegalArgumentException("Expression is null or empty")) + defaultValue + } else + try { + val result = MVEL.eval(expression, dataMap) + Gson().fromJson(result.toString(), T::class.java) + } catch (e: Exception) { + e.log() + onFailure(e) + defaultValue + }