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 607862f1fc..49e1ab8f75 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 @@ -5940,6 +5940,9 @@ class NaviAnalytics private constructor() { const val REDIRECTION_TO_WEB_STARTED = "redirection_to_web_started" const val REDIRECTION_TO_WEB_FAILED = "redirection_to_web_failed" const val REDIRECTION_TO_WEB_SUCCESS = "redirection_to_web_success" + const val REDIRECTION_TO_WEB_SUCCESS_ON_TIME_OUT = "redirection_to_web_success_on_time_out" + const val REDIRECTION_TO_WEB_SUCCESS_ON_DATA_RECEIVED = + "redirection_to_web_success_on_data_received" const val REDIRECTION_TO_WEB_AUTH_API_FAILED = "redirection_to_web_auth_api_failed" const val REDIRECTION_TO_WEB_DATA_INGESTION_INITIATED = "redirection_to_web_data_ingestion_initiated" diff --git a/android/app/src/main/java/com/naviapp/webredirection/presentation/activity/WebRedirectionActivity.kt b/android/app/src/main/java/com/naviapp/webredirection/presentation/activity/WebRedirectionActivity.kt index 027c7b4f1a..b43cce1b6e 100644 --- a/android/app/src/main/java/com/naviapp/webredirection/presentation/activity/WebRedirectionActivity.kt +++ b/android/app/src/main/java/com/naviapp/webredirection/presentation/activity/WebRedirectionActivity.kt @@ -7,11 +7,14 @@ package com.naviapp.webredirection.presentation.activity +import android.content.ActivityNotFoundException +import android.content.Intent +import android.net.Uri import android.os.Bundle +import android.widget.Toast import androidx.activity.compose.setContent import androidx.activity.viewModels import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.lifecycle.Lifecycle @@ -26,12 +29,16 @@ import com.navi.common.model.ModuleNameV2 import com.navi.common.ui.activity.BaseActivity import com.navi.common.ui.errorview.FullScreenErrorComposeView import com.navi.common.useruploaddata.model.IngestionStatusType +import com.navi.common.useruploaddata.model.PreSignedUrlListResponse import com.navi.common.useruploaddata.model.UserDataUploadCallbackResponse import com.navi.common.useruploaddata.viewmodel.UserDataViewModel import com.navi.common.utils.Constants.STATUS import com.navi.common.utils.Constants.URL import com.navi.common.utils.Constants.VERTICAL_TYPE +import com.navi.common.utils.log +import com.navi.common.utils.observeWithTimeout import com.navi.payment.nativepayment.tribute.NaviPaymentWebBridge +import com.naviapp.R import com.naviapp.analytics.utils.NaviAnalytics import com.naviapp.analytics.utils.NaviAnalytics.Companion.REDIRECTION_TO_WEB_AUTH_API_FAILED import com.naviapp.analytics.utils.NaviAnalytics.Companion.REDIRECTION_TO_WEB_DATA_INGESTION_FAILED @@ -41,6 +48,8 @@ import com.naviapp.analytics.utils.NaviAnalytics.Companion.REDIRECTION_TO_WEB_DA import com.naviapp.analytics.utils.NaviAnalytics.Companion.REDIRECTION_TO_WEB_FAILED import com.naviapp.analytics.utils.NaviAnalytics.Companion.REDIRECTION_TO_WEB_STARTED import com.naviapp.analytics.utils.NaviAnalytics.Companion.REDIRECTION_TO_WEB_SUCCESS +import com.naviapp.analytics.utils.NaviAnalytics.Companion.REDIRECTION_TO_WEB_SUCCESS_ON_DATA_RECEIVED +import com.naviapp.analytics.utils.NaviAnalytics.Companion.REDIRECTION_TO_WEB_SUCCESS_ON_TIME_OUT import com.naviapp.common.navigator.NaviDeepLinkNavigator import com.naviapp.home.dashboard.ui.compose.loansTab.LoansTabContentShimmer import com.naviapp.home.dashboard.ui.compose.loansTab.RenderWebView @@ -97,7 +106,6 @@ class WebRedirectionActivity : BaseActivity() { initialise() setContent { InitialiseContent( - redirectToExternalWeb = ::redirectToExternalWeb, handleNavigation = ::handleNavigation, handleException = ::handleException ) @@ -105,20 +113,51 @@ class WebRedirectionActivity : BaseActivity() { initialiseDataIngestion() webRedirectionVM.fetchTemporarySessionToken() observeWebRedirectionNavigation() + observeSmsUploadDataResponse() + } + + private fun observeSmsUploadDataResponse() { + userDataViewModel.preSignedUrlList.observeWithTimeout( + lifecycleOwner = this, + timeout = + FirebaseRemoteConfigHelper.getLong( + DATA_INGESTION_TIMEOUT, + defaultValue = DATA_INGESTION_TIMEOUT_DEFAULT_VALUE + ), + onTimeout = { + NaviTrackEvent.trackEvent(eventName = REDIRECTION_TO_WEB_SUCCESS_ON_TIME_OUT) + handleRedirection(PreSignedUrlListResponse(null)) + }, + observer = { preSignedUrlResponse -> + NaviTrackEvent.trackEvent( + eventName = REDIRECTION_TO_WEB_SUCCESS_ON_DATA_RECEIVED, + eventValues = + mapOf(PRE_SIGNED_URL_LIST_RESPONSE to preSignedUrlResponse.toString()) + ) + handleRedirection(preSignedUrlResponse) + } + ) + } + + private fun handleRedirection(preSignedUrlResponse: PreSignedUrlListResponse?) { + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + webRedirectionVM.screenState.collect { + webRedirectionVM.startRedirectionProcess(preSignedUrlResponse) + } + } + } } private fun observeWebRedirectionNavigation() { lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { - webRedirectionVM.webRedirectionCtaData.collect { ctaData -> - ctaData?.let { - trackEvent(NAVIGATING_TO_WEB_BROWSER) - NaviDeepLinkNavigator.navigateTo( - activity = this@WebRedirectionActivity, - ctaData = it, - finish = true - ) - } + webRedirectionVM.webPageUrl.collect { webPageUrl -> + NaviTrackEvent.trackEvent( + eventName = REDIRECTION_TO_WEB_SUCCESS, + eventValues = mapOf(URL to webPageUrl.toString()) + ) + webPageUrl?.let { url -> launchWebPageInChrome(url) } } } } @@ -136,7 +175,7 @@ class WebRedirectionActivity : BaseActivity() { businessVertical = ModuleNameV2.PL.name, isOpLifecycleBound = false, needTohandleIngestionPolling = false, - callback = { timeTaken, userUploadDataResponse -> + callback = { timeTaken, _, userUploadDataResponse -> userUploadDataResponse.ingestionStatusList?.ingestionStatusList.let { ingestionStatusList -> if ( @@ -200,7 +239,6 @@ class WebRedirectionActivity : BaseActivity() { @Composable private fun InitialiseContent( - redirectToExternalWeb: (ctaData: CtaData) -> Unit, handleNavigation: (CtaData) -> Unit, handleException: (Throwable) -> Unit ) { @@ -209,15 +247,6 @@ class WebRedirectionActivity : BaseActivity() { webRedirectionVM.webRedirectionPlatform.collectAsStateWithLifecycle() val data by webRedirectionVM.screenState.collectAsStateWithLifecycle() val webRedirectionData by webRedirectionVM.webRedirectionData.collectAsStateWithLifecycle() - - LaunchedEffect(key1 = data) { - if ( - data is UiState.Success && webRedirectionPlatform != WebRedirectionPlatform.WEBVIEW - ) { - trackEvent(eventName = REDIRECTION_TO_WEB_SUCCESS) - redirectToExternalWeb((data as UiState.Success).ctaData) - } - } when (webRedirectionPlatform) { WebRedirectionPlatform.WEBVIEW -> InternalWebViewScreen( @@ -299,10 +328,6 @@ class WebRedirectionActivity : BaseActivity() { } } - private fun redirectToExternalWeb(ctaData: CtaData) { - webRedirectionVM.startRedirectionProcess() - } - private fun handleNavigation(ctaData: CtaData) = NaviDeepLinkNavigator.navigateTo( activity = this, @@ -340,6 +365,38 @@ class WebRedirectionActivity : BaseActivity() { ) } + /** + * Opens a web page in the default web browser, specifically targeting Google Chrome. + * + * This function attempts to launch an intent that directs the user to a specified URL. If + * Google Chrome is not installed on the device, it falls back to any available web browser. + * + * @param url The URL of the web page to be opened. It should be a valid URL format (e.g., + * "https://www.example.com"). + * @throws ActivityNotFoundException if no applications can handle the intent to view the URL. + * + * After attempting to open the URL, this function calls `finish()`, which will close the + * current activity. + */ + private fun launchWebPageInChrome(url: String) { + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)).apply { setPackage(CHROME_PACKAGE) } + + try { + startActivity(intent) + } catch (e: ActivityNotFoundException) { + intent.setPackage(null) + e.log() + try { + startActivity(intent) + } catch (e: Exception) { + Toast.makeText(this, getString(R.string.no_browser_found), Toast.LENGTH_LONG).show() + e.log() + } + } finally { + finish() + } + } + override val screenName: String get() = NaviAnalytics.WEB_REDIRECTION_SCREEN @@ -349,5 +406,9 @@ class WebRedirectionActivity : BaseActivity() { companion object { const val WEB_REDIRECTION_SESSION_ID = "WEB_REDIRECTION_SESSION_ID" const val NAVIGATING_TO_WEB_BROWSER = "NAVIGATING_TO_WEB_BROWSER" + const val DATA_INGESTION_TIMEOUT = "DATA_INGESTION_TIMEOUT" + const val DATA_INGESTION_TIMEOUT_DEFAULT_VALUE = 3000L + const val CHROME_PACKAGE = "com.android.chrome" + const val PRE_SIGNED_URL_LIST_RESPONSE = "pre_signed_url_list_response" } } diff --git a/android/app/src/main/java/com/naviapp/webredirection/presentation/viewModel/WebRedirectionVM.kt b/android/app/src/main/java/com/naviapp/webredirection/presentation/viewModel/WebRedirectionVM.kt index 3344d63777..195654356f 100644 --- a/android/app/src/main/java/com/naviapp/webredirection/presentation/viewModel/WebRedirectionVM.kt +++ b/android/app/src/main/java/com/naviapp/webredirection/presentation/viewModel/WebRedirectionVM.kt @@ -17,12 +17,16 @@ import com.navi.base.model.LineItem import com.navi.base.sharedpref.PreferenceManager import com.navi.base.utils.BaseUtils import com.navi.base.utils.isNotNullAndNotEmpty +import com.navi.base.utils.orFalse import com.navi.common.constants.APP_VERSION_CODE import com.navi.common.constants.OS_VERSION import com.navi.common.constants.OS_VERSION_NAME import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper.WEB_REDIRECTION_DEFAULT_DELAY_IN_MILLIS +import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper.WEB_REDIRECTION_SMS_UPLOADED_DELAY_IN_MILLIS import com.navi.common.network.models.GenericErrorResponse +import com.navi.common.useruploaddata.model.IngestionType +import com.navi.common.useruploaddata.model.PreSignedUrlListResponse import com.navi.common.utils.Constants.URL import com.navi.common.utils.getSessionId import com.navi.common.utils.isValidResponse @@ -31,6 +35,7 @@ import com.naviapp.analytics.utils.NaviAnalytics import com.naviapp.common.navigator.NaviDeepLinkNavigator.WEB_URL import com.naviapp.manager.usecase.UserDataUploadWorkerUseCase import com.naviapp.registration.helper.isReadSmsPermissionGranted +import com.naviapp.utils.Constants import com.naviapp.utils.buildUrlWithParameters import com.naviapp.utils.generateRandomString import com.naviapp.utils.generateSHA256Hash @@ -78,8 +83,8 @@ constructor( private val _webRedirectionData = MutableStateFlow(null) val webRedirectionData = _webRedirectionData.asStateFlow() - private val _webRedirectionCtaData = MutableStateFlow(null) - val webRedirectionCtaData = _webRedirectionCtaData.asStateFlow() + private val _webPageUrl = MutableStateFlow(null) + val webPageUrl = _webPageUrl.asStateFlow() private var businessUnit: String? = null @@ -110,26 +115,49 @@ constructor( } } - fun startRedirectionProcess() { + fun startRedirectionProcess(preSignedUrlListResponse: PreSignedUrlListResponse?) { viewModelScope.launch(Dispatchers.IO) { - val webRedirectionSessionId = "${businessUnit}_$WEB_REDIRECTION_SESSION_ID" - if (PreferenceManager.getStringPreference(webRedirectionSessionId) != getSessionId()) { - delay(getWebRedirectionDelayInMillis()) - getSessionId()?.let { - PreferenceManager.setStringPreference(webRedirectionSessionId, it) + if (isSuccessAndRedirectToNonWebView()) { + val webRedirectionDelay = + calculateRedirectDelayForSmsUpload(preSignedUrlListResponse) + val webRedirectionSessionId = "${businessUnit}_$WEB_REDIRECTION_SESSION_ID" + if ( + PreferenceManager.getStringPreference(webRedirectionSessionId) != getSessionId() + ) { + delay(webRedirectionDelay) + getSessionId()?.let { + PreferenceManager.setStringPreference(webRedirectionSessionId, it) + } } - } - if (screenState.value is UiState.Success) { - _webRedirectionCtaData.value = (screenState.value as UiState.Success).ctaData + _webPageUrl.value = extractWebPageUrl() } } } - private fun getWebRedirectionDelayInMillis(): Long { - val webRedirectionDelayInMillis = + private fun extractWebPageUrl(): String? { + val ctaData = (screenState.value as UiState.Success).ctaData + return ctaData.parameters?.firstOrNull { it.key == Constants.URL }?.value + } + + private fun isSuccessAndRedirectToNonWebView() = + screenState.value is UiState.Success && + webRedirectionPlatform.value != WebRedirectionPlatform.WEBVIEW + + private fun calculateRedirectDelayForSmsUpload( + preSignedUrlListResponse: PreSignedUrlListResponse? + ): Long { + val smsUploadStatus = + preSignedUrlListResponse + ?.preSignedUrlList + ?.find { it.ingestionType == IngestionType.SMS.name } + ?.upload + .orFalse() + return if (smsUploadStatus) { + FirebaseRemoteConfigHelper.getLong(WEB_REDIRECTION_SMS_UPLOADED_DELAY_IN_MILLIS) + } else { webRedirectionData.value?.webRedirectionDelayInMillis ?: FirebaseRemoteConfigHelper.getLong(WEB_REDIRECTION_DEFAULT_DELAY_IN_MILLIS) - return webRedirectionDelayInMillis + } } private fun generateUrlForWebRedirect(codeVerifier: String, token: String): String? { diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 575ebb39ea..c2be8431ba 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -523,4 +523,5 @@ Scan & pay For a better experience, redirecting to Navi official webpage... https://navi-finserv.com + No web browser found. Please install a browser app, such as Google Chrome or Mozilla Firefox, from the Google Play Store to continue. diff --git a/android/application-platform/navi-ap/src/main/kotlin/com/navi/ap/common/handler/UploadDataActionHandler.kt b/android/application-platform/navi-ap/src/main/kotlin/com/navi/ap/common/handler/UploadDataActionHandler.kt index e82a4e8d3d..6319bc5d3e 100644 --- a/android/application-platform/navi-ap/src/main/kotlin/com/navi/ap/common/handler/UploadDataActionHandler.kt +++ b/android/application-platform/navi-ap/src/main/kotlin/com/navi/ap/common/handler/UploadDataActionHandler.kt @@ -115,7 +115,7 @@ fun HandleUploadDataAction( uploadAppsDataToS3, businessVertical.orEmpty(), numberOfRetriesLeft, - ) { _, userDataUploadCallbackResponse -> + ) { _, _, userDataUploadCallbackResponse -> val needIngestionPolling = FirebaseRemoteConfigHelper.getBoolean( FirebaseRemoteConfigHelper.UW_INGESTION_POLLING, diff --git a/android/navi-common/src/main/java/com/navi/common/firebaseremoteconfig/FirebaseRemoteConfigHelper.kt b/android/navi-common/src/main/java/com/navi/common/firebaseremoteconfig/FirebaseRemoteConfigHelper.kt index 03ee873c8c..c9ba7483d1 100644 --- a/android/navi-common/src/main/java/com/navi/common/firebaseremoteconfig/FirebaseRemoteConfigHelper.kt +++ b/android/navi-common/src/main/java/com/navi/common/firebaseremoteconfig/FirebaseRemoteConfigHelper.kt @@ -214,6 +214,8 @@ object FirebaseRemoteConfigHelper { const val DEEPLINK_RESOLVER_WAIT_TIME = "DEEPLINK_RESOLVER_WAIT_TIME" const val WEB_REDIRECTION_DEFAULT_DELAY_IN_MILLIS = "WEB_REDIRECTION_DEFAULT_DELAY_IN_MILLIS" + const val WEB_REDIRECTION_SMS_UPLOADED_DELAY_IN_MILLIS = + "WEB_REDIRECTION_SMS_UPLOADED_DELAY_IN_MILLIS" const val RETRY_INTERCEPTOR_ENABLED = "RETRY_INTERCEPTOR_ENABLED" const val ROOT_CA_ADDITION_TARGET_SDK = "ROOT_CA_ADDITION_TARGET_SDK" diff --git a/android/navi-common/src/main/java/com/navi/common/useruploaddata/utils/PermissionUtil.kt b/android/navi-common/src/main/java/com/navi/common/useruploaddata/utils/PermissionUtil.kt index 4c2801cbda..835ab549ed 100644 --- a/android/navi-common/src/main/java/com/navi/common/useruploaddata/utils/PermissionUtil.kt +++ b/android/navi-common/src/main/java/com/navi/common/useruploaddata/utils/PermissionUtil.kt @@ -81,7 +81,9 @@ object PermissionUtil { needTohandleIngestionPolling: Boolean = false, callback: (( - timeTakenToUpload: Long, userUploadDataResponse: UserDataUploadCallbackResponse + timeTakenToUpload: Long, + preSignedUrlListResponse: PreSignedUrlListResponse?, + userUploadDataResponse: UserDataUploadCallbackResponse ) -> Unit)? = null ) { @@ -116,7 +118,9 @@ object PermissionUtil { scope: CoroutineScope, callback: ( - timeTakenToUpload: Long, userUploadDataResponse: UserDataUploadCallbackResponse + timeTakenToUpload: Long, + preSignedUrlListResponse: PreSignedUrlListResponse?, + userUploadDataResponse: UserDataUploadCallbackResponse ) -> Unit, needTohandleIngestionPolling: Boolean ) { @@ -130,7 +134,11 @@ object PermissionUtil { while ((response.error != null || !response.errors.isNullOrEmpty()) && retryCount <= 4) { if (retryCount == 4) { withContext(Dispatchers.Main) { - callback.invoke(0, UserDataUploadCallbackResponse(null, null)) + callback.invoke( + 0, + PreSignedUrlListResponse(null), + UserDataUploadCallbackResponse(null, null) + ) } return } @@ -168,7 +176,11 @@ object PermissionUtil { uploadedResponseData ) withContext(Dispatchers.Main) { - callback.invoke(timeTakenToUploadAppData, userDataUploadCallbackResponse) + callback.invoke( + timeTakenToUploadAppData, + response.data, + userDataUploadCallbackResponse + ) } if (needTohandleIngestionPolling) { UploadDataPollingUtil().handleUploadDataResponse(userDataUploadCallbackResponse) @@ -197,7 +209,7 @@ object PermissionUtil { uploadedResponse.data ) withContext(Dispatchers.Main) { - callback.invoke(-1, userDataUploadCallbackResponse) + callback.invoke(-1, response.data, userDataUploadCallbackResponse) } if (needTohandleIngestionPolling) { UploadDataPollingUtil() diff --git a/android/navi-common/src/main/java/com/navi/common/useruploaddata/viewmodel/UserDataViewModel.kt b/android/navi-common/src/main/java/com/navi/common/useruploaddata/viewmodel/UserDataViewModel.kt index 171af556b5..27b8b0669b 100644 --- a/android/navi-common/src/main/java/com/navi/common/useruploaddata/viewmodel/UserDataViewModel.kt +++ b/android/navi-common/src/main/java/com/navi/common/useruploaddata/viewmodel/UserDataViewModel.kt @@ -16,6 +16,7 @@ import com.navi.base.sharedpref.PreferenceManager import com.navi.base.utils.orFalse import com.navi.common.model.DeviceDetail import com.navi.common.useruploaddata.model.IngestionStatusType +import com.navi.common.useruploaddata.model.PreSignedUrlListResponse import com.navi.common.useruploaddata.model.UserDataUploadCallbackResponse import com.navi.common.useruploaddata.repository.UserDataRepository import com.navi.common.useruploaddata.utils.PermissionUtil @@ -36,6 +37,10 @@ class UserDataViewModel(application: Application) : AndroidViewModel(application val deviceDataSentStatus: LiveData get() = _deviceDataSentStatus + private val _preSignedUrlList = MutableLiveData() + val preSignedUrlList: LiveData + get() = _preSignedUrlList + fun sendUserDataToAws( uploadSms: Boolean = false, uploadAppUsedInfo: Boolean = false, @@ -44,7 +49,9 @@ class UserDataViewModel(application: Application) : AndroidViewModel(application needTohandleIngestionPolling: Boolean = false, callback: (( - timeTakenToUpload: Long, userUploadDataResponse: UserDataUploadCallbackResponse + timeTakenToUpload: Long, + preSignedUrlListResponse: PreSignedUrlListResponse?, + userUploadDataResponse: UserDataUploadCallbackResponse ) -> Unit)? = null ) { @@ -56,9 +63,13 @@ class UserDataViewModel(application: Application) : AndroidViewModel(application uploadAppUsedInfo, businessVertical, needTohandleIngestionPolling - ) { timeTakenToUpload, userDataUploadCallbackResponse -> + ) { timeTakenToUpload, presignedUrlResponse, userDataUploadCallbackResponse -> _userDataSentStatus.value = userDataUploadCallbackResponse - callback?.invoke(timeTakenToUpload, userDataUploadCallbackResponse) + callback?.invoke( + timeTakenToUpload, + presignedUrlResponse, + userDataUploadCallbackResponse + ) } Timber.d("SmsTask, ContactsTask and Device Details Task Done") } @@ -71,9 +82,12 @@ class UserDataViewModel(application: Application) : AndroidViewModel(application numberOfRetriesLeft: Int = 0, isOpLifecycleBound: Boolean = true, needTohandleIngestionPolling: Boolean = false, + dataUploadCallback: (PreSignedUrlListResponse?) -> Unit = {}, callback: (( - timeTakenToUpload: Long, userUploadDataResponse: UserDataUploadCallbackResponse + timeTakenToUpload: Long, + preSignedUrlListResponse: PreSignedUrlListResponse?, + userUploadDataResponse: UserDataUploadCallbackResponse ) -> Unit)? = null ) { @@ -82,8 +96,8 @@ class UserDataViewModel(application: Application) : AndroidViewModel(application uploadAppsDataToS3, businessVertical, isOpLifecycleBound, - needTohandleIngestionPolling - ) { timeTakenToUpload, userDataUploadCallbackResponse -> + needTohandleIngestionPolling, + ) { timeTakenToUpload, presignedUrlResponse, userDataUploadCallbackResponse -> if ( numberOfRetriesLeft > 0 && userDataUploadCallbackResponse.ingestionStatusList @@ -98,11 +112,17 @@ class UserDataViewModel(application: Application) : AndroidViewModel(application numberOfRetriesLeft - 1, isOpLifecycleBound, needTohandleIngestionPolling, + dataUploadCallback, callback ) } else { + _preSignedUrlList.value = presignedUrlResponse _userDataSentStatus.value = userDataUploadCallbackResponse - callback?.invoke(timeTakenToUpload, userDataUploadCallbackResponse) + callback?.invoke( + timeTakenToUpload, + presignedUrlResponse, + userDataUploadCallbackResponse + ) } } } diff --git a/android/navi-common/src/main/java/com/navi/common/utils/LiveData.kt b/android/navi-common/src/main/java/com/navi/common/utils/LiveData.kt index 63ad8c31e8..7406e4b781 100644 --- a/android/navi-common/src/main/java/com/navi/common/utils/LiveData.kt +++ b/android/navi-common/src/main/java/com/navi/common/utils/LiveData.kt @@ -1,15 +1,21 @@ /* * - * * Copyright © 2019-2022 by Navi Technologies Limited + * * Copyright © 2019-2024 by Navi Technologies Limited * * All rights reserved. Strictly confidential * */ package com.navi.common.utils +import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LiveData import androidx.lifecycle.Observer +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext fun LiveData.observeNullable(owner: LifecycleOwner, observer: (T?) -> Unit) = observe(owner, Observer { v -> observer.invoke(v) }) @@ -17,3 +23,38 @@ fun LiveData.observeNullable(owner: LifecycleOwner, observer: (T?) -> Uni fun LiveData.observeNonNull(owner: LifecycleOwner, observer: (T) -> Unit) { this.observe(owner, Observer { it?.let { observer(it) } }) } + +fun LiveData.observeWithTimeout( + lifecycleOwner: LifecycleOwner, + timeout: Long, + onTimeout: () -> Unit, + observer: (T) -> Unit +) { + var localOnTimeOut: (() -> Unit)? = onTimeout + + val localObserver = + Observer { value -> + value?.let { + observer(it) + localOnTimeOut = null + } + } + this.observe(lifecycleOwner, localObserver) + + val job = + lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { + delay(timeout) + withContext(Dispatchers.Main.immediate) { + this@observeWithTimeout.removeObserver(localObserver) + localOnTimeOut?.invoke() + } + } + lifecycleOwner.lifecycle.addObserver( + object : DefaultLifecycleObserver { + override fun onDestroy(owner: LifecycleOwner) { + job.cancel() + removeObserver(localObserver) + } + } + ) +}