From e868d708062d846df83fc5835ffdc27e7440e52c Mon Sep 17 00:00:00 2001 From: Venkat Praneeth Reddy Date: Wed, 12 Feb 2025 20:45:34 +0530 Subject: [PATCH] NTP-24283 | Navi link changes (#14927) --- .../analytics/deeplink/DeeplinkManager.kt | 41 +++++++++--- .../analytics/deeplink/DeeplinkRepository.kt | 13 +++- .../naviapp/analytics/utils/NaviAnalytics.kt | 27 ++++++++ .../analytics/NaviDeeplinkAnalytics.kt | 22 +++++++ .../usecase/AppsFlyerDeeplinkManager.kt | 1 + .../usecase/BranchDeeplinkManager.kt | 49 ++++++++------- .../usecase/DeeplinkNavigation.kt | 1 + .../usecase/FacebookDeeplinkManager.kt | 1 + .../usecase/IDeeplinkManager.kt | 1 + .../usecase/NativeDeeplinkManager.kt | 63 ++++++++++++++----- .../usecase/NaviPayDeeplinkManager.kt | 1 + .../vm/DeeplinkManagementViewModel.kt | 43 ++++++++++++- .../home/model/InAppNotificationResponse.kt | 1 + .../home/ui/utils/NotificationUtils.kt | 17 ++++- .../java/com/naviapp/models/DeeplinkData.kt | 1 + .../java/com/naviapp/models/NaviLinkData.kt | 12 ++++ .../network/retrofit/RetrofitService.kt | 7 +++ .../firebase/NaviFirebaseMessagingService.kt | 5 ++ .../repositories/RegisterRepository.kt | 6 ++ .../LoginDeeplinkAndRedirectionHelper.kt | 62 +++++++++++------- .../registration/viewmodel/RegistrationVM.kt | 41 +++++++++++- .../main/java/com/naviapp/utils/Constants.kt | 4 ++ 22 files changed, 346 insertions(+), 73 deletions(-) create mode 100644 android/app/src/main/java/com/naviapp/models/NaviLinkData.kt diff --git a/android/app/src/main/java/com/naviapp/analytics/deeplink/DeeplinkManager.kt b/android/app/src/main/java/com/naviapp/analytics/deeplink/DeeplinkManager.kt index 4062e3d183..f0fbbc875c 100644 --- a/android/app/src/main/java/com/naviapp/analytics/deeplink/DeeplinkManager.kt +++ b/android/app/src/main/java/com/naviapp/analytics/deeplink/DeeplinkManager.kt @@ -34,6 +34,7 @@ import com.naviapp.analytics.utils.NaviAnalytics.Companion.LOGIN_FLAG import com.naviapp.app.NaviApplication import com.naviapp.common.helper.ReferralHelper import com.naviapp.common.navigator.NaviDeepLinkNavigator +import com.naviapp.common.navigator.NaviDeepLinkNavigator.HOME_SMALL import com.naviapp.common.navigator.ScreenNavigator import com.naviapp.models.request.DeeplinkRequestData import com.naviapp.models.request.OnboardingActionRequest @@ -71,6 +72,7 @@ class DeeplinkManager( private const val NAVI_PAY_HOME_PAGE_URL = "naviPayHomePageUrl" const val DEEPLINK_TYPE = "deeplinkType" const val DEEPLINK_MANAGER = "DeeplinkManager" + const val NAVILINK_IDENTIFIER = "naviLinkIdentifier" } fun handleDeeplinkData( @@ -82,6 +84,7 @@ class DeeplinkManager( originalLink: String? = null, finish: Boolean? = false, ctaData: CtaData? = null, + isDeeplinkNaviLink: Boolean? = false, onRedirectListener: (bundle: Bundle) -> Unit = {}, ) { try { @@ -118,6 +121,7 @@ class DeeplinkManager( onRedirectListener = onRedirectListener, ) } + DeeplinkType.FB.name -> { naviAnalytics.sendHandleFacebookDeeplinkEvent(deepLinkValue, originalLink) uriData?.let { uri -> @@ -161,6 +165,7 @@ class DeeplinkManager( ) } } + DeeplinkType.BRANCH_IO.name -> { naviAnalytics.sendHandleBranchDeeplinkEvent(deepLinkValue, originalLink) val customObject = jsonObject?.optJSONObject(CUSTOM_OBJECT) @@ -193,6 +198,7 @@ class DeeplinkManager( onRedirectListener = onRedirectListener, ) } + DeeplinkType.NAVI_PAY.name -> { val bundle = Bundle().apply { @@ -207,14 +213,35 @@ class DeeplinkManager( needsResult = false, ) } + DeeplinkType.NATIVE.name -> { - naviAnalytics.sendNativeDeeplinkEvent(originalLink) - navigateTo( - activity = activity, - bundle = ctaData?.bundle ?: Bundle(), - finish = originalLink == NaviDeepLinkNavigator.CHAT_ACTIVITY, - ctaData = ctaData ?: CtaData(url = originalLink), - ) + if (isDeeplinkNaviLink == true) { + naviAnalytics.sendNaviLinkDeeplinkEvent(originalLink) + if (BaseUtils.isUserLoggedIn().not()) { + onRedirectListener( + Bundle().apply { + putString(DEEPLINK_TYPE, DeeplinkType.NATIVE.name) + putString(NAVILINK_IDENTIFIER, originalLink) + } + ) + } else { + naviAnalytics.sendNaviLinkSuccessEvent(ctaData, wasUserLoggedIn = true) + navigateTo( + activity, + ctaData = ctaData ?: CtaData(url = HOME_SMALL), + bundle = ctaData?.bundle ?: Bundle(), + finish = finish, + ) + } + } else { + naviAnalytics.sendNativeDeeplinkEvent(originalLink) + navigateTo( + activity = activity, + bundle = ctaData?.bundle ?: Bundle(), + finish = originalLink == NaviDeepLinkNavigator.CHAT_ACTIVITY, + ctaData = ctaData ?: CtaData(url = originalLink), + ) + } } } } catch (ex: Exception) { diff --git a/android/app/src/main/java/com/naviapp/analytics/deeplink/DeeplinkRepository.kt b/android/app/src/main/java/com/naviapp/analytics/deeplink/DeeplinkRepository.kt index 8192816f55..85d16506e0 100644 --- a/android/app/src/main/java/com/naviapp/analytics/deeplink/DeeplinkRepository.kt +++ b/android/app/src/main/java/com/naviapp/analytics/deeplink/DeeplinkRepository.kt @@ -12,8 +12,9 @@ import com.navi.common.model.ModuleName import com.naviapp.models.request.OnboardingRequest import com.naviapp.network.retrofit.ResponseCallback import com.naviapp.utils.superAppRetrofitService +import javax.inject.Inject -class DeeplinkRepository : ResponseCallback() { +class DeeplinkRepository @Inject constructor() : ResponseCallback() { suspend fun fetchBranchSDKData(deeplink: String) = superAppRetrofitService().fetchBranchSDKData(deeplink, ModuleName.GROOT.name) @@ -37,4 +38,14 @@ class DeeplinkRepository : ResponseCallback() { isNae = { false }, ), ) + + suspend fun fetchNaviLinkData(naviLinkIdentifier: String) = + apiResponseCallback( + response = superAppRetrofitService().fetchNaviLink(naviLinkIdentifier), + metricInfo = + MetricInfo.CommonMetric( + screen = DeeplinkManager.DEEPLINK_MANAGER, + isNae = { false }, + ), + ) } diff --git a/android/app/src/main/java/com/naviapp/analytics/utils/NaviAnalytics.kt b/android/app/src/main/java/com/naviapp/analytics/utils/NaviAnalytics.kt index 93983fdfa0..912f1a46a6 100644 --- a/android/app/src/main/java/com/naviapp/analytics/utils/NaviAnalytics.kt +++ b/android/app/src/main/java/com/naviapp/analytics/utils/NaviAnalytics.kt @@ -183,6 +183,16 @@ class NaviAnalytics private constructor() { ) } + fun sendNaviLinkSuccessEvent(ctaData: CtaData?, wasUserLoggedIn: Boolean) { + NaviTrackEvent.trackEventOnClickStream( + "NaviLink_Deeplink_Success_Event", + mapOf( + Pair("ctaData", ctaData.toString()), + Pair("wasUserLoggedIn", wasUserLoggedIn.toString()), + ), + ) + } + fun noDeeplink() { NaviTrackEvent.trackEvent("Lending_Deeplink_Absent") } @@ -916,6 +926,23 @@ class NaviAnalytics private constructor() { mapOf(Pair("originalLink", originalLink.orEmpty())), ) } + + fun sendNaviLinkDeeplinkEvent(originalLink: String?) { + NaviTrackEvent.trackEventOnClickStream( + "Handle_NaviLink_Deeplink_Event", + mapOf(Pair("originalLink", originalLink.orEmpty())), + ) + } + + fun sendNaviLinkSuccessEvent(ctaData: CtaData?, wasUserLoggedIn: Boolean) { + NaviTrackEvent.trackEventOnClickStream( + "NaviLink_Deeplink_Success_Event", + mapOf( + Pair("ctaData", ctaData.toString()), + Pair("wasUserLoggedIn", wasUserLoggedIn.toString()), + ), + ) + } } inner class TopUpLoanIntro(val screenName: String? = null) { diff --git a/android/app/src/main/java/com/naviapp/deeplinkmanagement/analytics/NaviDeeplinkAnalytics.kt b/android/app/src/main/java/com/naviapp/deeplinkmanagement/analytics/NaviDeeplinkAnalytics.kt index a8e1898d4c..ad8a65742b 100644 --- a/android/app/src/main/java/com/naviapp/deeplinkmanagement/analytics/NaviDeeplinkAnalytics.kt +++ b/android/app/src/main/java/com/naviapp/deeplinkmanagement/analytics/NaviDeeplinkAnalytics.kt @@ -76,4 +76,26 @@ class NaviDeeplinkAnalytics @Inject constructor() { eventValues = mapOf("reason" to reason), ) } + + fun sendProcessNaviLinkEvent(naviLinkIdentifier: String) { + NaviTrackEvent.trackEventOnClickStream( + eventName = "Process_NaviLink_Event", + eventValues = mapOf("naviLinkIdentifier" to naviLinkIdentifier), + ) + } + + fun sendNaviLinkDataEvent(deeplinkData: DeeplinkData?, isUserLoggedIn: Boolean) { + NaviTrackEvent.trackEventOnClickStream( + eventName = "NaviLink_Data_Event", + eventValues = + mapOf( + "deeplinkData" to deeplinkData.toString(), + "isUserLoggedIn" to isUserLoggedIn.toString(), + ), + ) + } + + fun sendNaviLinkDataFailEvent() { + NaviTrackEvent.trackEventOnClickStream(eventName = "NaviLink_Data_Fail_Event") + } } diff --git a/android/app/src/main/java/com/naviapp/deeplinkmanagement/usecase/AppsFlyerDeeplinkManager.kt b/android/app/src/main/java/com/naviapp/deeplinkmanagement/usecase/AppsFlyerDeeplinkManager.kt index 91508bd7c9..ed1866667a 100644 --- a/android/app/src/main/java/com/naviapp/deeplinkmanagement/usecase/AppsFlyerDeeplinkManager.kt +++ b/android/app/src/main/java/com/naviapp/deeplinkmanagement/usecase/AppsFlyerDeeplinkManager.kt @@ -27,6 +27,7 @@ class AppsFlyerDeeplinkManager : IDeeplinkManager { isReInit: Boolean, activity: Activity?, processDeeplink: KFunction1, + processNaviLink: ((String) -> Unit)?, ) { Timber.d("DeeplinkManagement: handleAppsFlyerDeeplink subscribeForDeepLink") AppsFlyerUtil.instance.subscribeForDeepLink( diff --git a/android/app/src/main/java/com/naviapp/deeplinkmanagement/usecase/BranchDeeplinkManager.kt b/android/app/src/main/java/com/naviapp/deeplinkmanagement/usecase/BranchDeeplinkManager.kt index 22d411da40..e7899ee43c 100644 --- a/android/app/src/main/java/com/naviapp/deeplinkmanagement/usecase/BranchDeeplinkManager.kt +++ b/android/app/src/main/java/com/naviapp/deeplinkmanagement/usecase/BranchDeeplinkManager.kt @@ -68,32 +68,37 @@ constructor(private val analyticsTracker: NaviDeeplinkAnalytics) : IDeeplinkMana isReInit: Boolean, activity: Activity?, processDeeplink: KFunction1, + processNaviLink: ((String) -> Unit)?, ) { processDeeplinkCallback = processDeeplink intentData = intent.data.toString() - try { - if ( - isReInit && - intent.hasExtra("branch_force_new_session") && - intent.getBooleanExtra("branch_force_new_session", false) - ) { - Branch.sessionBuilder(activity) - .withCallback(branchReferralInitListener) - .withData(intent.data) - .reInit() - analyticsTracker.sendBranchInitEvent() - } else { - Branch.sessionBuilder(activity) - .withCallback(branchReferralInitListener) - .withData(intent.data) - .init() - analyticsTracker.sendBranchInitEvent() + val naviLinkIdentifier = getNaviLinkIdentifier(intent) + if (naviLinkIdentifier == null) { + try { + if ( + isReInit && + intent.hasExtra("branch_force_new_session") && + intent.getBooleanExtra("branch_force_new_session", false) + ) { + Branch.sessionBuilder(activity) + .withCallback(branchReferralInitListener) + .withData(intent.data) + .reInit() + analyticsTracker.sendBranchInitEvent() + } else { + Branch.sessionBuilder(activity) + .withCallback(branchReferralInitListener) + .withData(intent.data) + .init() + analyticsTracker.sendBranchInitEvent() + } + } catch (e: Exception) { + Timber.d("DeeplinkManagement: Branch init failed ${e.message}") + // DeviceId id not being populated in Crashlytics for this crash. Hence sending + // message + // in click-stream + analyticsTracker.sendBranchInitFailedEvent(e.message.orEmpty()) } - } catch (e: Exception) { - Timber.d("DeeplinkManagement: Branch init failed ${e.message}") - // DeviceId id not being populated in Crashlytics for this crash. Hence sending message - // in click-stream - analyticsTracker.sendBranchInitFailedEvent(e.message.orEmpty()) } } } diff --git a/android/app/src/main/java/com/naviapp/deeplinkmanagement/usecase/DeeplinkNavigation.kt b/android/app/src/main/java/com/naviapp/deeplinkmanagement/usecase/DeeplinkNavigation.kt index 44b12a85b1..c7973fdde8 100644 --- a/android/app/src/main/java/com/naviapp/deeplinkmanagement/usecase/DeeplinkNavigation.kt +++ b/android/app/src/main/java/com/naviapp/deeplinkmanagement/usecase/DeeplinkNavigation.kt @@ -63,6 +63,7 @@ constructor( originalLink = deeplinkData?.originalDeeplink, finish = finish, ctaData = deeplinkData?.ctaData, + isDeeplinkNaviLink = deeplinkData?.isDeeplinkNaviLink, ) } diff --git a/android/app/src/main/java/com/naviapp/deeplinkmanagement/usecase/FacebookDeeplinkManager.kt b/android/app/src/main/java/com/naviapp/deeplinkmanagement/usecase/FacebookDeeplinkManager.kt index 79f5014d1a..cc533526a0 100644 --- a/android/app/src/main/java/com/naviapp/deeplinkmanagement/usecase/FacebookDeeplinkManager.kt +++ b/android/app/src/main/java/com/naviapp/deeplinkmanagement/usecase/FacebookDeeplinkManager.kt @@ -28,6 +28,7 @@ class FacebookDeeplinkManager : IDeeplinkManager { isReInit: Boolean, activity: Activity?, processDeeplink: KFunction1, + processNaviLink: ((String) -> Unit)?, ) { Timber.d("DeeplinkManagement: in handleFacebookDeeplink") val appLinkData: Bundle? = AppLinks.getAppLinkData(intent) diff --git a/android/app/src/main/java/com/naviapp/deeplinkmanagement/usecase/IDeeplinkManager.kt b/android/app/src/main/java/com/naviapp/deeplinkmanagement/usecase/IDeeplinkManager.kt index cd2f0213f2..09ba32d5a4 100644 --- a/android/app/src/main/java/com/naviapp/deeplinkmanagement/usecase/IDeeplinkManager.kt +++ b/android/app/src/main/java/com/naviapp/deeplinkmanagement/usecase/IDeeplinkManager.kt @@ -16,5 +16,6 @@ interface IDeeplinkManager { isReInit: Boolean = false, activity: Activity? = null, processDeeplink: kotlin.reflect.KFunction1, + processNaviLink: ((String) -> Unit)? = null, ) } diff --git a/android/app/src/main/java/com/naviapp/deeplinkmanagement/usecase/NativeDeeplinkManager.kt b/android/app/src/main/java/com/naviapp/deeplinkmanagement/usecase/NativeDeeplinkManager.kt index 171e8cf8e7..a4cd9c205d 100644 --- a/android/app/src/main/java/com/naviapp/deeplinkmanagement/usecase/NativeDeeplinkManager.kt +++ b/android/app/src/main/java/com/naviapp/deeplinkmanagement/usecase/NativeDeeplinkManager.kt @@ -22,6 +22,7 @@ import com.naviapp.models.DeeplinkData import com.naviapp.registration.helper.getChatParams import com.naviapp.registration.helper.isNotificationAlive import com.naviapp.utils.Constants +import com.naviapp.utils.Constants.NAVILINK import com.naviapp.utils.isPublicPage import kotlin.reflect.KFunction1 @@ -32,29 +33,31 @@ class NativeDeeplinkManager : IDeeplinkManager { isReInit: Boolean, activity: Activity?, processDeeplink: KFunction1, + processNaviLink: ((String) -> Unit)?, ) { - var deeplink = intent.getStringExtra(Constants.DEEPLINK) - if (Regex(Constants.HTTP_REGEX).containsMatchIn(deeplink.orEmpty())) { - return - } + var deeplink = intent.getStringExtra(Constants.DEEPLINK).orEmpty() var param: ArrayList? = null - if (deeplink == NaviDeepLinkNavigator.CHAT_ACTIVITY) { - if (isNotificationAlive(intent = intent)) { - param = getChatParams(intent = intent) - } else { - deeplink = NaviDeepLinkNavigator.HOME - NaviTrackEvent.trackEventOnClickStream( - eventName = CHAT_PN_OPENED, - eventValues = mapOf(SCREEN_NAME to HOME_SCREEN), - ) + + when { + handleNaviLink(intent, processNaviLink) -> return + isHttpLink(deeplink) -> return + deeplink == NaviDeepLinkNavigator.CHAT_ACTIVITY -> { + if (isNotificationAlive(intent = intent)) { + param = getChatParams(intent = intent) + } else { + deeplink = NaviDeepLinkNavigator.HOME + NaviTrackEvent.trackEventOnClickStream( + eventName = CHAT_PN_OPENED, + eventValues = mapOf(SCREEN_NAME to HOME_SCREEN), + ) + } } } - val ctaData = CtaData(url = deeplink, bundle = intent.extras) param?.let { ctaData.parameters = it } if (BaseUtils.isUserLoggedIn() || isPublicPage(deeplink)) { - if (!deeplink.isNullOrEmpty()) { + if (deeplink.isNotEmpty()) { processDeeplink( DeeplinkData( deeplinkType = DeeplinkType.NATIVE.name, @@ -66,3 +69,33 @@ class NativeDeeplinkManager : IDeeplinkManager { } } } + +private fun isHttpLink(deeplink: String): Boolean { + return Regex(Constants.HTTP_REGEX).containsMatchIn(deeplink) +} + +private fun handleNaviLink(intent: Intent, processNaviLink: ((String) -> Unit)?): Boolean { + val naviLinkIdentifier = getNaviLinkIdentifier(intent) + if (naviLinkIdentifier != null) { + processNaviLink?.invoke(naviLinkIdentifier) + return true + } + return false +} + +fun getNaviLinkIdentifier(intent: Intent): String? { + val navilink = intent.getStringExtra(Constants.NAVILINK_SMALL).orEmpty() + val identifiers = + when { + navilink.isNotEmpty() -> navilink.split("/") + else -> emptyList() + } + + val naviLinkIdentifier = identifiers.getOrNull(1) + + return if (identifiers.firstOrNull() == NAVILINK && !naviLinkIdentifier.isNullOrEmpty()) { + naviLinkIdentifier + } else { + null + } +} diff --git a/android/app/src/main/java/com/naviapp/deeplinkmanagement/usecase/NaviPayDeeplinkManager.kt b/android/app/src/main/java/com/naviapp/deeplinkmanagement/usecase/NaviPayDeeplinkManager.kt index abe61c6b40..cb00c007fb 100644 --- a/android/app/src/main/java/com/naviapp/deeplinkmanagement/usecase/NaviPayDeeplinkManager.kt +++ b/android/app/src/main/java/com/naviapp/deeplinkmanagement/usecase/NaviPayDeeplinkManager.kt @@ -22,6 +22,7 @@ class NaviPayDeeplinkManager : IDeeplinkManager { isReInit: Boolean, activity: Activity?, processDeeplink: KFunction1, + processNaviLink: ((String) -> Unit)?, ) { if (intent.isNaviPayIntent()) { Timber.d("DeeplinkManagement: in handleNaviPayDeeplink processDeeplink ${intent.data}") diff --git a/android/app/src/main/java/com/naviapp/deeplinkmanagement/vm/DeeplinkManagementViewModel.kt b/android/app/src/main/java/com/naviapp/deeplinkmanagement/vm/DeeplinkManagementViewModel.kt index 4a38c372d7..289a6ee58c 100644 --- a/android/app/src/main/java/com/naviapp/deeplinkmanagement/vm/DeeplinkManagementViewModel.kt +++ b/android/app/src/main/java/com/naviapp/deeplinkmanagement/vm/DeeplinkManagementViewModel.kt @@ -10,7 +10,10 @@ package com.naviapp.deeplinkmanagement.vm import android.content.Intent import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.viewModelScope +import com.navi.base.utils.BaseUtils +import com.navi.common.utils.isValidResponse import com.navi.common.viewmodel.BaseVM +import com.naviapp.analytics.deeplink.DeeplinkRepository import com.naviapp.analytics.deeplink.DeeplinkType import com.naviapp.deeplinkmanagement.analytics.NaviDeeplinkAnalytics import com.naviapp.deeplinkmanagement.usecase.DeeplinkManagerFactory @@ -29,6 +32,7 @@ class DeeplinkManagementViewModel constructor( private val deeplinkManagerFactory: DeeplinkManagerFactory, private val naviDeeplinkAnalytics: NaviDeeplinkAnalytics, + private val deeplinkRepository: DeeplinkRepository, ) : BaseVM() { private val _deeplinkData = MutableSharedFlow() @@ -48,7 +52,11 @@ constructor( .handleDeeplink(intent = intent, processDeeplink = ::processDeeplink) deeplinkManagerFactory .getDeeplinkManager(DeeplinkType.NATIVE, naviDeeplinkAnalytics) - .handleDeeplink(intent = intent, processDeeplink = ::processDeeplink) + .handleDeeplink( + intent = intent, + processDeeplink = ::processDeeplink, + processNaviLink = ::processNaviLink, + ) } } @@ -60,8 +68,8 @@ constructor( deeplinkManagerFactory .getDeeplinkManager(DeeplinkType.BRANCH_IO, naviDeeplinkAnalytics) .handleDeeplink( - isReInit = isReInit, intent = intent, + isReInit = isReInit, activity = activity, processDeeplink = ::processDeeplink, ) @@ -73,4 +81,35 @@ constructor( _deeplinkData.emit(deeplinkData) } } + + private fun processNaviLink(naviLinkIdentifier: String) { + viewModelScope.safeLaunch(Dispatchers.IO) { + naviDeeplinkAnalytics.sendProcessNaviLinkEvent(naviLinkIdentifier) + if (BaseUtils.isUserLoggedIn()) { + val response = deeplinkRepository.fetchNaviLinkData(naviLinkIdentifier) + if (response.isValidResponse()) { + val data = + DeeplinkData( + deeplinkType = DeeplinkType.NATIVE.name, + originalDeeplink = naviLinkIdentifier, + ctaData = response.data?.nextCta, + isDeeplinkNaviLink = true, + ) + _deeplinkData.emit(data) + naviDeeplinkAnalytics.sendNaviLinkDataEvent(data, isUserLoggedIn = true) + } else { + naviDeeplinkAnalytics.sendNaviLinkDataFailEvent() + } + } else { + val data = + DeeplinkData( + deeplinkType = DeeplinkType.NATIVE.name, + originalDeeplink = naviLinkIdentifier, + isDeeplinkNaviLink = true, + ) + _deeplinkData.emit(data) + naviDeeplinkAnalytics.sendNaviLinkDataEvent(data, isUserLoggedIn = false) + } + } + } } diff --git a/android/app/src/main/java/com/naviapp/home/model/InAppNotificationResponse.kt b/android/app/src/main/java/com/naviapp/home/model/InAppNotificationResponse.kt index 426e37c153..737af1fd95 100644 --- a/android/app/src/main/java/com/naviapp/home/model/InAppNotificationResponse.kt +++ b/android/app/src/main/java/com/naviapp/home/model/InAppNotificationResponse.kt @@ -35,6 +35,7 @@ data class NotificationPayload( @Parcelize data class NotificationDynamicDeeplink( @SerializedName("url") val url: String? = null, + @SerializedName("naviLink") val naviLink: String? = null, @SerializedName("isDynamicDeeplink") val isDynamicDeeplink: Boolean? = null, ) : Parcelable diff --git a/android/app/src/main/java/com/naviapp/home/ui/utils/NotificationUtils.kt b/android/app/src/main/java/com/naviapp/home/ui/utils/NotificationUtils.kt index 27166ca4d1..917404ec3e 100644 --- a/android/app/src/main/java/com/naviapp/home/ui/utils/NotificationUtils.kt +++ b/android/app/src/main/java/com/naviapp/home/ui/utils/NotificationUtils.kt @@ -16,6 +16,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.withStyle import androidx.core.net.toUri import com.navi.base.model.LineItem +import com.navi.base.utils.isNotNullAndNotEmpty import com.navi.design.font.FontWeightEnum import com.navi.design.font.getFontWeight import com.navi.pay.utils.IS_FROM_IAN @@ -27,6 +28,7 @@ import com.naviapp.home.model.NotificationStatus import com.naviapp.home.model.NotificationUpdateStatus import com.naviapp.home.model.ToastBarState import com.naviapp.home.viewmodel.NotificationVM +import com.naviapp.utils.Constants.NAVILINK_SMALL fun createAnnotatedStringWithBold(text: String): AnnotatedString { val regex = "\\*\\*(.*?)\\*\\*".toRegex() @@ -110,11 +112,20 @@ fun handleNotificationClick( if (notification.notificationPayload?.dynamicDeeplink?.isDynamicDeeplink == true) { try { - notification.notificationPayload.dynamicDeeplink.url?.let { + if (notification.notificationPayload.dynamicDeeplink.naviLink.isNotNullAndNotEmpty()) { val intent = Intent(activity, DeeplinkManagementActivity::class.java) - intent.data = it.toUri() - intent.putExtra("branch_force_new_session", true) + intent.putExtra( + NAVILINK_SMALL, + notification.notificationPayload.dynamicDeeplink.naviLink, + ) activity.startActivity(intent) + } else { + notification.notificationPayload.dynamicDeeplink.url?.let { + val intent = Intent(activity, DeeplinkManagementActivity::class.java) + intent.data = it.toUri() + intent.putExtra("branch_force_new_session", true) + activity.startActivity(intent) + } } } catch (e: Exception) { NaviAnalytics.naviAnalytics diff --git a/android/app/src/main/java/com/naviapp/models/DeeplinkData.kt b/android/app/src/main/java/com/naviapp/models/DeeplinkData.kt index 8473ee17ec..f364662da5 100644 --- a/android/app/src/main/java/com/naviapp/models/DeeplinkData.kt +++ b/android/app/src/main/java/com/naviapp/models/DeeplinkData.kt @@ -19,4 +19,5 @@ data class DeeplinkData( var originalDeeplink: String? = null, var deeplinkValue: String? = null, var ctaData: CtaData? = null, + var isDeeplinkNaviLink: Boolean? = false, ) : Serializable diff --git a/android/app/src/main/java/com/naviapp/models/NaviLinkData.kt b/android/app/src/main/java/com/naviapp/models/NaviLinkData.kt new file mode 100644 index 0000000000..444a8fafb0 --- /dev/null +++ b/android/app/src/main/java/com/naviapp/models/NaviLinkData.kt @@ -0,0 +1,12 @@ +/* + * + * * Copyright © 2025 by Navi Technologies Limited + * * All rights reserved. Strictly confidential + * + */ + +package com.naviapp.models + +import com.navi.base.model.CtaData + +data class NaviLinkData(val nextCta: CtaData? = null) diff --git a/android/app/src/main/java/com/naviapp/network/retrofit/RetrofitService.kt b/android/app/src/main/java/com/naviapp/network/retrofit/RetrofitService.kt index c4e3db1b01..ea331de622 100644 --- a/android/app/src/main/java/com/naviapp/network/retrofit/RetrofitService.kt +++ b/android/app/src/main/java/com/naviapp/network/retrofit/RetrofitService.kt @@ -69,6 +69,7 @@ import com.naviapp.home.model.NotificationUpdateStatus import com.naviapp.lending_permission.model.LendingPermissionScreenResponse import com.naviapp.models.ABSettings import com.naviapp.models.LoginSettings +import com.naviapp.models.NaviLinkData import com.naviapp.models.UserInstalledApp import com.naviapp.models.request.CustomPaymentRequest import com.naviapp.models.request.EmailRequest @@ -151,6 +152,12 @@ interface RetrofitService { @Header(HEADER_X_PLATFORM) platform: String = Constants.OS_ANDROID, ): Response> + @GET("navi-link/v1/{identifier}") + suspend fun fetchNaviLink( + @Path("identifier") naviLinkIdentifier: String, + @Header(HEADER_X_PLATFORM) platform: String = Constants.OS_ANDROID, + ): Response> + @POST("/auth/v1/session") suspend fun createNewSession(): Response> diff --git a/android/app/src/main/java/com/naviapp/pushnotification/firebase/NaviFirebaseMessagingService.kt b/android/app/src/main/java/com/naviapp/pushnotification/firebase/NaviFirebaseMessagingService.kt index 37a99898b4..993cfe158c 100644 --- a/android/app/src/main/java/com/naviapp/pushnotification/firebase/NaviFirebaseMessagingService.kt +++ b/android/app/src/main/java/com/naviapp/pushnotification/firebase/NaviFirebaseMessagingService.kt @@ -57,9 +57,11 @@ import com.naviapp.pushnotification.utils.clearSharedDbWithKeys import com.naviapp.pushnotification.utils.removeSpecificImageCache import com.naviapp.pushnotification.utils.removeSpecificPreferenceKeys import com.naviapp.utils.Constants +import com.naviapp.utils.Constants.BRANCH import com.naviapp.utils.Constants.DELIVERED import com.naviapp.utils.Constants.FCM_DELIVERED_V2 import com.naviapp.utils.Constants.MESSAGE_ID +import com.naviapp.utils.Constants.NAVILINK_SMALL import com.naviapp.utils.Constants.NOTIFICATION_PERMISSION_DENIED import com.naviapp.utils.Constants.STATUS import com.naviapp.utils.Constants.TEMPLATE_NAME @@ -259,6 +261,9 @@ class NaviFirebaseMessagingService : FirebaseMessagingService() { intent.putExtra(key, value) } } + if (intent.getStringExtra(NAVILINK_SMALL).isNotNullAndNotEmpty()) { + intent.removeExtra(BRANCH) + } if (intent.getStringExtra(Constants.DEEPLINK) == CHAT_ACTIVITY) { NaviTrackEvent.trackEventOnClickStream(CHAT_PN_RECEIVED) if (NaviChatActivity.isChatActivityVisible) { diff --git a/android/app/src/main/java/com/naviapp/registration/repositories/RegisterRepository.kt b/android/app/src/main/java/com/naviapp/registration/repositories/RegisterRepository.kt index 1adeb2a33c..114751dd29 100644 --- a/android/app/src/main/java/com/naviapp/registration/repositories/RegisterRepository.kt +++ b/android/app/src/main/java/com/naviapp/registration/repositories/RegisterRepository.kt @@ -108,4 +108,10 @@ class RegisterRepository : ResponseCallback() { retrofitService().updateMarketingDataToSaphyra(request = saphyraRequestData), metricInfo = MetricInfo.AppMetric(screen = naeScreenName, isNae = { false }), ) + + suspend fun fetchNaviLinkCta(naviLinkIdentifier: String, screenName: String) = + apiResponseCallback( + response = superAppRetrofitService().fetchNaviLink(naviLinkIdentifier), + metricInfo = MetricInfo.CommonMetric(screen = screenName, isNae = { false }), + ) } diff --git a/android/app/src/main/java/com/naviapp/registration/usecase/LoginDeeplinkAndRedirectionHelper.kt b/android/app/src/main/java/com/naviapp/registration/usecase/LoginDeeplinkAndRedirectionHelper.kt index dbec1f2696..e88e8598c1 100644 --- a/android/app/src/main/java/com/naviapp/registration/usecase/LoginDeeplinkAndRedirectionHelper.kt +++ b/android/app/src/main/java/com/naviapp/registration/usecase/LoginDeeplinkAndRedirectionHelper.kt @@ -22,6 +22,7 @@ import com.navi.common.utils.Constants.LOGIN_SOURCE import com.navi.common.utils.isHomePageUrl import com.naviapp.analytics.deeplink.DeeplinkManager import com.naviapp.analytics.deeplink.DeeplinkManager.Companion.DEEPLINK_TYPE +import com.naviapp.analytics.deeplink.DeeplinkManager.Companion.NAVILINK_IDENTIFIER import com.naviapp.analytics.utils.NaviAnalytics import com.naviapp.common.navigator.NaviDeepLinkNavigator import com.naviapp.common.navigator.NaviDeepLinkNavigator.HOME_SMALL @@ -125,6 +126,7 @@ constructor( type = deeplinkData?.deeplinkType.toString(), deepLinkValue = deeplinkData?.deeplinkValue, originalLink = deeplinkData?.originalDeeplink, + isDeeplinkNaviLink = deeplinkData?.isDeeplinkNaviLink, ) { bundle -> deepLinkBundle = bundle activity.source = deepLinkBundle?.getString(LOGIN_SOURCE) @@ -163,29 +165,45 @@ constructor( val deepLinkType = deepLinkBundle?.getString(DEEPLINK_TYPE) if (deepLinkType.isNotNullAndNotEmpty()) { analyticsTracker.deeplinkSuccess(deepLinkType) - registrationVM.getOnboardingAction( - screenName = screenName, - onboardingRequest = - OnboardingRequest( - onboardingActions = - listOf( - OnboardingActionRequest( - type = OnboardingActionType.DEEP_LINK_RESOLUTION.name, - data = - DeeplinkRequestData( - data = extractDataFromBundle(deepLinkBundle), - type = deepLinkType, - ), + val naviLinkIdentifier = deepLinkBundle?.getString(NAVILINK_IDENTIFIER) + if (!naviLinkIdentifier.isNullOrEmpty()) { + registrationVM.fetchNaviLinkCta( + naviLinkIdentifier = naviLinkIdentifier, + screenName = screenName, + ) { nextCta -> + analyticsTracker.sendNaviLinkSuccessEvent(nextCta, wasUserLoggedIn = false) + asyncHomePageResponseFetchAndRedirect( + homePageResponseFetchJob, + activity, + homeVM, + nextCta, + ) + } + } else { + registrationVM.getOnboardingAction( + screenName = screenName, + onboardingRequest = + OnboardingRequest( + onboardingActions = + listOf( + OnboardingActionRequest( + type = OnboardingActionType.DEEP_LINK_RESOLUTION.name, + data = + DeeplinkRequestData( + data = extractDataFromBundle(deepLinkBundle), + type = deepLinkType, + ), + ) ) - ) - ), - ) { nextCta -> - asyncHomePageResponseFetchAndRedirect( - homePageResponseFetchJob, - activity, - homeVM, - nextCta, - ) + ), + ) { nextCta -> + asyncHomePageResponseFetchAndRedirect( + homePageResponseFetchJob, + activity, + homeVM, + nextCta, + ) + } } } else { if (deferredActionUseCase.get().isJobStarted()) { diff --git a/android/app/src/main/java/com/naviapp/registration/viewmodel/RegistrationVM.kt b/android/app/src/main/java/com/naviapp/registration/viewmodel/RegistrationVM.kt index 744e555cef..7729e3aa58 100644 --- a/android/app/src/main/java/com/naviapp/registration/viewmodel/RegistrationVM.kt +++ b/android/app/src/main/java/com/naviapp/registration/viewmodel/RegistrationVM.kt @@ -18,6 +18,7 @@ import com.navi.base.utils.FAILURE import com.navi.base.utils.orFalse import com.navi.common.network.authenticator.TokenAuthenticator import com.navi.common.utils.CommonNaviAnalytics +import com.navi.common.utils.isValidResponse import com.navi.common.viewmodel.BaseVM import com.naviapp.models.request.GenerateOtpRequest import com.naviapp.models.request.LoginType @@ -34,6 +35,7 @@ import com.naviapp.network.ApiConstants.API_TOO_MANY_REQUESTS import com.naviapp.network.ApiConstants.API_WRONG_OTP import com.naviapp.network.ApiErrorTagType import com.naviapp.registration.repositories.RegisterRepository +import com.naviapp.utils.Constants.DEEPLINK_FETCH_CTA_TIMEOUT import java.util.concurrent.atomic.AtomicBoolean import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.TimeoutCancellationException @@ -206,7 +208,7 @@ class RegistrationVM(private val registerRepository: RegisterRepository = Regist viewModelScope.launch(Dispatchers.IO) { var nextCta: CtaData? = null try { - withTimeout(5000) { + withTimeout(DEEPLINK_FETCH_CTA_TIMEOUT) { val response = registerRepository.getOnboardingAction( onboardingRequest = onboardingRequest, @@ -241,6 +243,43 @@ class RegistrationVM(private val registerRepository: RegisterRepository = Regist } } + fun fetchNaviLinkCta( + naviLinkIdentifier: String, + screenName: String, + onComplete: (nextCta: CtaData?) -> Unit, + ) { + viewModelScope.launch(Dispatchers.IO) { + var nextCta: CtaData? = null + try { + withTimeout(DEEPLINK_FETCH_CTA_TIMEOUT) { + val response = + registerRepository.fetchNaviLinkCta( + naviLinkIdentifier = naviLinkIdentifier, + screenName = screenName, + ) + if (response.isValidResponse()) { + nextCta = response.data?.nextCta + } + } + } catch (e: Exception) { + Timber.e(e) + if (e is TimeoutCancellationException) { + NaviTrackEvent.trackEvent( + eventName = "fetchNaviLinkCta", + eventValues = mapOf("error" to "Timeout"), + ) + } else { + NaviTrackEvent.trackEvent( + eventName = "fetchNaviLinkCta", + eventValues = mapOf("error" to e.toString()), + ) + } + } finally { + onComplete(nextCta) + } + } + } + fun saveTokens(sessionToken: String?, refreshToken: String?) { BaseUtils.saveSessionToken(sessionToken.orEmpty()) BaseUtils.saveRefreshToken(refreshToken.orEmpty()) 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 ca1c858299..617b0804d8 100644 --- a/android/app/src/main/java/com/naviapp/utils/Constants.kt +++ b/android/app/src/main/java/com/naviapp/utils/Constants.kt @@ -37,7 +37,11 @@ object Constants { const val HOME_LOAN = "home" const val PERSONAL_LOAN = "personal" const val MESSAGE_ID = "messageId" + const val NAVILINK = "NAVILINK" + const val NAVILINK_SMALL = "navilink" + const val BRANCH = "branch" const val DEEPLINK = "deeplink" + const val DEEPLINK_FETCH_CTA_TIMEOUT = 5000L const val WEB_URL = "webUrl" const val REPEAT_LOAN_TYPE = "repeat" const val WIDGET_ID = "WIDGET_ID"