NTP-37254 | Ads fallback views (#14921)
Signed-off-by: Naman Khurmi <naman.khurmi@navi.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2021-2024 by Navi Technologies Limited
|
||||
* * Copyright © 2021-2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
@@ -23,4 +23,5 @@ object DeeplinkConstants {
|
||||
const val APP_UPDATE = "APP_UPDATE"
|
||||
const val INVESTMENT = "investment"
|
||||
const val NPAY_INTENT_ACTIVITY = "NPAY_INTENT_ACTIVITY"
|
||||
const val NAVI_BBPS = "naviBbps"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.common.adverse.fallbackTemplates
|
||||
|
||||
import android.app.Activity
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.navi.base.deeplink.DeepLinkManager
|
||||
import com.navi.base.deeplink.util.DeeplinkConstants
|
||||
import com.navi.base.model.CtaData
|
||||
import com.navi.common.R
|
||||
import com.navi.common.adverse.model.FallbackBannerContent
|
||||
import com.navi.common.utils.Constants.KEY_REDIRECTION_CTA
|
||||
import com.navi.elex.atoms.ElexText
|
||||
import com.navi.elex.font.FontWeightEnum
|
||||
|
||||
@Composable
|
||||
fun BbpsBannerFallbackAd(modifier: Modifier, showExploreMore: Boolean, activity: Activity) {
|
||||
Column(modifier, verticalArrangement = Arrangement.spacedBy(16.dp)) {
|
||||
ExploreMoreDivider(showExploreMore)
|
||||
FallbackBanner(
|
||||
content =
|
||||
FallbackBannerContent(
|
||||
productInfo =
|
||||
FallbackBannerContent.ProductInfo(
|
||||
icon =
|
||||
"https://public-assets.prod.navi-sa.in/adverse/bbps/bbps_loud_ad_promo_image.png",
|
||||
name = null,
|
||||
showRewardsCallout = true,
|
||||
),
|
||||
contentInfo =
|
||||
FallbackBannerContent.ContentInfo(
|
||||
headline = "₹0 platform fee on bill payments and recharges",
|
||||
description = "Recharge, credit card \n" + "and more",
|
||||
ctaText = "Pay now",
|
||||
illustration =
|
||||
"https://public-assets.prod.navi-sa.in/adverse/bbps/bbps_loud_ad_product_image.png",
|
||||
),
|
||||
bannerStyle =
|
||||
FallbackBannerContent.BannerStyle.default.copy(
|
||||
backgroundColors = listOf(Color(0xffCAFFE7), Color(0xffFAFFFC))
|
||||
),
|
||||
),
|
||||
onClick = {
|
||||
DeepLinkManager.getDeepLinkListener()
|
||||
?.navigateTo(
|
||||
activity = activity,
|
||||
ctaData =
|
||||
CtaData(type = KEY_REDIRECTION_CTA, url = DeeplinkConstants.NAVI_BBPS),
|
||||
finish = true,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ExploreMoreDivider(showExploreMore: Boolean) {
|
||||
if (showExploreMore) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
Spacer(Modifier.weight(1f).height(1.dp).background(Color(0xffF3F1F3)))
|
||||
ElexText(
|
||||
stringResource(R.string.explore_more),
|
||||
color = Color(0xff757375),
|
||||
fontSize = 12.sp,
|
||||
lineHeight = 20.sp,
|
||||
letterSpacing = 1.5.sp,
|
||||
fontWeight = FontWeightEnum.NAVI_BODY_DEMI_BOLD,
|
||||
)
|
||||
Spacer(Modifier.weight(1f).height(1.dp).background(Color(0xffF3F1F3)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.common.adverse.fallbackTemplates
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import coil.compose.AsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.navi.common.R
|
||||
import com.navi.common.adverse.model.FallbackBannerContent
|
||||
import com.navi.elex.atoms.ElexText
|
||||
import com.navi.elex.font.FontWeightEnum
|
||||
import com.navi.naviwidgets.R as NaviWidgetsR
|
||||
|
||||
private val LocalBannerTheme = staticCompositionLocalOf {
|
||||
FallbackBannerContent.BannerStyle.default
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun FallbackBanner(
|
||||
modifier: Modifier = Modifier,
|
||||
content: FallbackBannerContent,
|
||||
onClick: () -> Unit = {},
|
||||
) {
|
||||
CompositionLocalProvider(LocalBannerTheme provides content.bannerStyle) {
|
||||
Column(
|
||||
modifier =
|
||||
modifier
|
||||
.height(236.dp)
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(
|
||||
brush =
|
||||
Brush.horizontalGradient(
|
||||
colors = LocalBannerTheme.current.backgroundColors
|
||||
),
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
)
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = LocalBannerTheme.current.borderColor,
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
)
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = ripple(bounded = true),
|
||||
) {
|
||||
onClick()
|
||||
},
|
||||
horizontalAlignment = Alignment.Start,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(top = 16.dp, start = 16.dp, end = 16.dp),
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
ProductHeader(content.productInfo)
|
||||
ProductHeadline(content.contentInfo.headline)
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize().padding(top = 8.dp, start = 16.dp),
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.Top,
|
||||
) {
|
||||
BannerContent(content.contentInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ProductHeader(productInfo: FallbackBannerContent.ProductInfo) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
Row(
|
||||
modifier =
|
||||
Modifier.background(Color.White, RoundedCornerShape(4.dp))
|
||||
.padding(horizontal = 3.8.dp, vertical = 2.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
AsyncImage(
|
||||
model =
|
||||
ImageRequest.Builder(LocalContext.current)
|
||||
.allowHardware(false)
|
||||
.placeholder(NaviWidgetsR.drawable.image_placeholder_small)
|
||||
.error(NaviWidgetsR.drawable.image_placeholder_small)
|
||||
.data(productInfo.icon)
|
||||
.build(),
|
||||
contentDescription = "",
|
||||
modifier = Modifier.height(20.dp),
|
||||
)
|
||||
productInfo.name?.let { name ->
|
||||
ElexText(
|
||||
text = name,
|
||||
fontSize = 10.sp,
|
||||
lineHeight = 20.sp,
|
||||
fontWeight = FontWeightEnum.NAVI_BODY_DEMI_BOLD,
|
||||
color = LocalBannerTheme.current.productNameColor,
|
||||
letterSpacing = 1.5.sp,
|
||||
)
|
||||
}
|
||||
}
|
||||
RewardsCalloutRow(productInfo.showRewardsCallout)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RewardsCalloutRow(showRewardsCallout: Boolean) {
|
||||
if (showRewardsCallout) {
|
||||
Row(
|
||||
modifier =
|
||||
Modifier.background(
|
||||
color = LocalBannerTheme.current.rewardsCalloutBackgroundColor,
|
||||
shape = RoundedCornerShape(50),
|
||||
)
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
||||
) {
|
||||
ElexText(
|
||||
stringResource(R.string.get_upto),
|
||||
fontSize = 12.sp,
|
||||
lineHeight = 16.sp,
|
||||
color = LocalBannerTheme.current.rewardsCalloutTextColor,
|
||||
fontWeight = FontWeightEnum.NAVI_BODY_DEMI_BOLD,
|
||||
)
|
||||
Image(
|
||||
painter = painterResource(NaviWidgetsR.drawable.navi_coin_40),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(12.dp),
|
||||
)
|
||||
ElexText(
|
||||
"1,000",
|
||||
fontSize = 12.sp,
|
||||
lineHeight = 16.sp,
|
||||
color = LocalBannerTheme.current.rewardsCalloutTextColor,
|
||||
fontWeight = FontWeightEnum.NAVI_BODY_DEMI_BOLD,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ProductHeadline(productHeadline: String) {
|
||||
ElexText(
|
||||
productHeadline,
|
||||
fontSize = 16.sp,
|
||||
lineHeight = 24.sp,
|
||||
color = LocalBannerTheme.current.headlineColor,
|
||||
fontWeight = FontWeightEnum.NAVI_BODY_DEMI_BOLD,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BannerContent(content: FallbackBannerContent.ContentInfo) {
|
||||
Row(
|
||||
Modifier.fillMaxSize(),
|
||||
horizontalArrangement = Arrangement.spacedBy(9.dp),
|
||||
verticalAlignment = Alignment.Top,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.align(Alignment.Top).weight(1f),
|
||||
horizontalAlignment = Alignment.Start,
|
||||
) {
|
||||
ElexText(
|
||||
content.description,
|
||||
fontSize = 12.sp,
|
||||
lineHeight = 16.sp,
|
||||
color = LocalBannerTheme.current.descriptionColor,
|
||||
fontWeight = FontWeightEnum.NAVI_BODY_REGULAR,
|
||||
)
|
||||
Spacer(Modifier.height(20.dp))
|
||||
CtaButton(content.ctaText)
|
||||
if (content.showPoweredBy) {
|
||||
Spacer(Modifier.height(16.dp))
|
||||
ElexText(
|
||||
stringResource(R.string.powered_by_navi_finserv),
|
||||
fontSize = 6.sp,
|
||||
lineHeight = 16.sp,
|
||||
color = LocalBannerTheme.current.poweredByColor,
|
||||
fontWeight = FontWeightEnum.NAVI_BODY_REGULAR,
|
||||
)
|
||||
}
|
||||
}
|
||||
AsyncImage(
|
||||
model =
|
||||
ImageRequest.Builder(LocalContext.current)
|
||||
.allowHardware(false)
|
||||
.placeholder(NaviWidgetsR.drawable.image_placeholder_xx_large)
|
||||
.error(NaviWidgetsR.drawable.image_placeholder_xx_large)
|
||||
.data(content.illustration)
|
||||
.build(),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.align(Alignment.Bottom).size(114.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CtaButton(buttonText: String) {
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier =
|
||||
Modifier.background(
|
||||
color = LocalBannerTheme.current.ctaBackgroundColor,
|
||||
shape = RoundedCornerShape(4.dp),
|
||||
)
|
||||
.padding(vertical = 12.dp, horizontal = 8.dp),
|
||||
) {
|
||||
ElexText(
|
||||
text = buttonText,
|
||||
fontSize = 12.sp,
|
||||
lineHeight = 16.sp,
|
||||
color = LocalBannerTheme.current.ctaTextColor,
|
||||
fontWeight = FontWeightEnum.NAVI_BODY_DEMI_BOLD,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.common.adverse.model
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
data class FallbackBannerContent(
|
||||
val productInfo: ProductInfo,
|
||||
val contentInfo: ContentInfo,
|
||||
val bannerStyle: BannerStyle,
|
||||
) {
|
||||
data class ProductInfo(
|
||||
val icon: Any,
|
||||
val name: String? = null,
|
||||
val showRewardsCallout: Boolean,
|
||||
)
|
||||
|
||||
data class ContentInfo(
|
||||
val headline: String,
|
||||
val description: String,
|
||||
val ctaText: String,
|
||||
val illustration: Any,
|
||||
val showPoweredBy: Boolean = false,
|
||||
)
|
||||
|
||||
data class BannerStyle(
|
||||
val backgroundColors: List<Color>,
|
||||
val borderColor: Color,
|
||||
val productNameColor: Color,
|
||||
val rewardsCalloutBackgroundColor: Color,
|
||||
val rewardsCalloutTextColor: Color,
|
||||
val headlineColor: Color,
|
||||
val descriptionColor: Color,
|
||||
val ctaBackgroundColor: Color,
|
||||
val ctaTextColor: Color,
|
||||
val poweredByColor: Color,
|
||||
) {
|
||||
companion object {
|
||||
val default =
|
||||
BannerStyle(
|
||||
backgroundColors = listOf(Color.White, Color.White),
|
||||
borderColor = Color(0xffEBEBEB),
|
||||
ctaBackgroundColor = Color(0xff1f002a),
|
||||
ctaTextColor = Color.White,
|
||||
headlineColor = Color(0xff191919),
|
||||
descriptionColor = Color(0xff444444),
|
||||
productNameColor = Color(0xff444444),
|
||||
rewardsCalloutBackgroundColor = Color.White,
|
||||
rewardsCalloutTextColor = Color(0xff191919),
|
||||
poweredByColor = Color.Black,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -215,4 +215,8 @@
|
||||
<string name="arc_info_bbps_bottom_sheet_title">Protects bill payments</string>
|
||||
<string name="arc_info_bbps_bottom_sheet_campaign_title">If bill / recharge paid via Navi UPI is pending for %s+ days, get 100%% extra up to</string>
|
||||
|
||||
<string name="get_upto">Get upto</string>
|
||||
<string name="powered_by_navi_finserv">Powered by Navi Finserv</string>
|
||||
<string name="explore_more">EXPLORE MORE</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -60,6 +60,7 @@ import androidx.compose.ui.window.Popup
|
||||
import coil.compose.AsyncImage
|
||||
import com.navi.adverse.sdk.ui.AdverseViewRoot
|
||||
import com.navi.common.R as CommonR
|
||||
import com.navi.common.adverse.fallbackTemplates.BbpsBannerFallbackAd
|
||||
import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper
|
||||
import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper.NAVI_PAY_PPS_CROSS_SELL_AD_FALLBACK_TIMEOUT
|
||||
import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper.NAVI_PAY_PPS_CROSS_SELL_AD_RE_ID
|
||||
@@ -190,6 +191,13 @@ fun SharedTransitionScope.PaymentSummaryTransactionDetailSection(
|
||||
FirebaseRemoteConfigHelper.getLong(
|
||||
NAVI_PAY_PPS_CROSS_SELL_AD_FALLBACK_TIMEOUT
|
||||
),
|
||||
fallbackView = {
|
||||
BbpsBannerFallbackAd(
|
||||
modifier = Modifier.padding(top = 16.dp, start = 16.dp, end = 16.dp),
|
||||
activity = naviPayActivity,
|
||||
showExploreMore = true,
|
||||
)
|
||||
},
|
||||
)
|
||||
if (showRewardsPopUp) {
|
||||
Popup {
|
||||
|
||||
Reference in New Issue
Block a user