TP-48780 | Refill journey Revamp to AP | PSEC-1136 (#8894)
Co-authored-by: kishan kumar <kishan.kumar@navi.com> Co-authored-by: Jegatheeswaran M <jegatheeswaran.m@navi.com> Co-authored-by: Prakhar Saxena <prakhar.saxena@navi.com> Co-authored-by: rahul bhat <rahul.bhat@navi.com> Co-authored-by: Shaurya Rehan <shaurya.rehan@navi.com> Co-authored-by: Aditya Narayan Malik <aditya.narayan@navi.com>
This commit is contained in:
@@ -73,6 +73,9 @@ dependencies {
|
||||
testImplementation libs.kotlinx.coroutines.test
|
||||
|
||||
testImplementation libs.mockk
|
||||
|
||||
implementation libs.accompanist.systemuicontroller
|
||||
|
||||
}
|
||||
|
||||
android.buildFeatures.buildConfig true
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import com.navi.ap.common.viewmodel.LambdaVM
|
||||
import com.navi.common.uitron.model.action.SystemUiAction
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
|
||||
|
||||
@Composable
|
||||
fun SystemUiActionHandler(viewModel: LambdaVM) {
|
||||
|
||||
val systemUiController = rememberSystemUiController()
|
||||
|
||||
|
||||
fun setSystemUiColors(systemUiAction: SystemUiAction) {
|
||||
systemUiController.isStatusBarVisible = systemUiAction.isStatusBarVisible.orTrue()
|
||||
systemUiController.isNavigationBarVisible = systemUiAction.isNavigationBarVisible.orTrue()
|
||||
|
||||
systemUiAction.statusBarColor?.color?.hexToComposeColor?.let { statusBarColor ->
|
||||
systemUiController.setStatusBarColor(color = statusBarColor)
|
||||
}
|
||||
|
||||
systemUiAction.navigationBarColor?.color?.hexToComposeColor?.let { navigationBarColour ->
|
||||
systemUiController.setNavigationBarColor(color = navigationBarColour)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.getActionCallback().distinctUntilChanged().collect { action ->
|
||||
if (action is SystemUiAction) {
|
||||
setSystemUiColors(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,10 @@ package com.navi.ap.common.deserializer
|
||||
|
||||
import com.google.gson.JsonDeserializationContext
|
||||
import com.google.gson.JsonElement
|
||||
import com.navi.common.uitron.model.action.DownloadAction
|
||||
import com.navi.ap.common.models.actions.UpdateDataViaHandleAction
|
||||
import com.navi.common.uitron.deserializer.UiTronActionDeserializer
|
||||
import com.navi.common.uitron.model.action.SystemUiAction
|
||||
import com.navi.common.uitron.util.ApActionType
|
||||
import com.navi.uitron.model.data.UiTronAction
|
||||
import java.lang.reflect.Type
|
||||
@@ -22,6 +24,12 @@ class ApUiTronActionDeserializer : UiTronActionDeserializer() {
|
||||
ApActionType.UpdateDataViaHandleAction.name ->
|
||||
context?.deserialize(jsonObject, UpdateDataViaHandleAction::class.java)
|
||||
|
||||
ApActionType.DownloadAction.name ->
|
||||
context?.deserialize(jsonObject, DownloadAction::class.java)
|
||||
|
||||
ApActionType.SystemUiAction.name ->
|
||||
context?.deserialize(jsonObject, SystemUiAction::class.java)
|
||||
|
||||
else -> super.deserialize(json, typeOfT, context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ package com.navi.ap.common.deserializer
|
||||
|
||||
import com.google.gson.JsonDeserializationContext
|
||||
import com.google.gson.JsonElement
|
||||
import com.navi.ap.common.models.CardWithHeaderFooterAndLazyColumnWidgetData
|
||||
import com.navi.ap.common.models.CollapsableItemsWithTitleWidgetData
|
||||
import com.navi.ap.common.models.CustomWidgets
|
||||
import com.navi.ap.common.models.DynamicColumnWidgetData
|
||||
@@ -36,6 +37,8 @@ class CustomUiTronDataDeserializer : UiTronDataDeserializer() {
|
||||
context?.deserialize(json, DynamicColumnWidgetData::class.java)
|
||||
CustomWidgets.DYNAMIC_ROW_WIDGET.name ->
|
||||
context?.deserialize(json, DynamicRowWidgetData::class.java)
|
||||
CustomWidgets.CARD_WITH_HEADER_FOOTER_AND_LAZY_COLUMN_WIDGET.name ->
|
||||
context?.deserialize(json, CardWithHeaderFooterAndLazyColumnWidgetData::class.java)
|
||||
else -> super.deserialize(json, typeOfT, context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
package com.navi.ap.common.handler
|
||||
|
||||
import SystemUiActionHandler
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.navi.ap.common.ui.ApplicationPlatformActivity
|
||||
@@ -27,4 +28,6 @@ fun InitActionsHandler(viewModel: LambdaVM, activity: ApplicationPlatformActivit
|
||||
HandleSdkAction(viewModel = viewModel, activity = activity)
|
||||
HandleApiAction(viewModel = viewModel, activity = activity)
|
||||
HandleLaunchIntentAction(viewModel = viewModel, activity = activity)
|
||||
DownloadActionHandler(viewModel = viewModel, activity = activity)
|
||||
SystemUiActionHandler(viewModel = viewModel)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.navi.ap.common.handler
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import com.navi.common.uitron.model.action.DownloadAction
|
||||
import com.navi.ap.common.ui.ApplicationPlatformActivity
|
||||
import com.navi.ap.common.viewmodel.LambdaVM
|
||||
import com.navi.ap.utils.downloader.DownloadCallback
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
@Composable
|
||||
fun DownloadActionHandler(viewModel: LambdaVM, activity: ApplicationPlatformActivity) {
|
||||
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
DisposableEffect(Unit) {
|
||||
val job = coroutineScope.launch(Dispatchers.IO) {
|
||||
|
||||
viewModel.getActionCallback().distinctUntilChanged().collect { action ->
|
||||
if (action is DownloadAction) {
|
||||
viewModel.handleActions(action.downloadAction?.onStart)
|
||||
|
||||
activity.taskDownloadManager.startDownload(
|
||||
downloadAction = action,
|
||||
downloadCallback = object : DownloadCallback {
|
||||
override fun onDownloadSuccess(downloadId: Long) {
|
||||
viewModel.handleActions(action.downloadAction?.onCompleted)
|
||||
}
|
||||
|
||||
override fun onDownloadFailure(downloadId: Long) {
|
||||
viewModel.handleActions(action.downloadAction?.onFailed)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
onDispose {
|
||||
job.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,10 @@ fun lambdaApiActionHandler(
|
||||
val map = getResolvedFieldValue(fields = lambdaApiAction.fields, handle = viewModel.handle)
|
||||
viewModel.fetchOfferDetails(lambdaApiAction = lambdaApiAction, resolvedValues = map)
|
||||
}
|
||||
LambdaType.REFILL_LOAN_DETAILS.name -> {
|
||||
val map = getResolvedFieldValue(fields = lambdaApiAction.fields, handle = viewModel.handle)
|
||||
viewModel.fetchRefillOfferDetails(map,lambdaApiAction)
|
||||
}
|
||||
LambdaType.APPLY_LOAN.name -> {
|
||||
val map = getResolvedFieldValue(fields = lambdaApiAction.fields, handle = viewModel.handle)
|
||||
viewModel.applyLoan(map)
|
||||
@@ -89,5 +93,19 @@ fun lambdaApiActionHandler(
|
||||
resolvedValues = map
|
||||
)
|
||||
}
|
||||
LambdaType.FETCH_EMI_CALENDER_DETAILS.name -> {
|
||||
val map =
|
||||
getResolvedFieldValue(fields = lambdaApiAction.fields, handle = viewModel.handle)
|
||||
viewModel.fetchEmiInstallments(resolvedValues = map, lambdaApiAction = lambdaApiAction)
|
||||
}
|
||||
|
||||
LambdaType.DOWNLOAD_LOAN_AGREEMENT_AND_SANCTION_LETTER.name -> {
|
||||
val map =
|
||||
getResolvedFieldValue(fields = lambdaApiAction.fields, handle = viewModel.handle)
|
||||
viewModel.initiateAgreementAndSanctionLetterDownload(
|
||||
resolvedValues = map,
|
||||
lambdaApiAction = lambdaApiAction
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.navi.ap.common.models
|
||||
|
||||
import com.navi.ap.common.models.lambdamodels.EmiCalendarResponse
|
||||
import com.navi.uitron.model.UiTronResponse
|
||||
import com.navi.uitron.model.data.UiTronData
|
||||
import com.navi.uitron.model.ui.BorderStrokeData
|
||||
|
||||
data class CardWithHeaderFooterAndLazyColumnWidgetData(
|
||||
val header : UiTronResponse? = null,
|
||||
val footer : UiTronResponse? = null,
|
||||
val stickyHeaderItem: UiTronResponse? = null,
|
||||
val titleItem: UiTronResponse? = null,
|
||||
val containerProperty: ContainerProperty? = null,
|
||||
val listData: EmiCalendarResponse? = null,
|
||||
) : UiTronData(){
|
||||
data class ContainerProperty(
|
||||
val verticalOffset : Int? = null,
|
||||
val backGroundColor: String? = null,
|
||||
val padding: Int? = null,
|
||||
val containerHeight : Int? = null,
|
||||
val borderStrokeData: BorderStrokeData? = null
|
||||
)
|
||||
}
|
||||
|
||||
data class UiTronResponseWithType(
|
||||
val isSticky:Boolean = false,
|
||||
val uiTronResponse: UiTronResponse?
|
||||
)
|
||||
@@ -43,5 +43,6 @@ enum class CustomWidgets {
|
||||
COLLAPSABLE_ITEMS_WITH_TITLE_WIDGET,
|
||||
DYNAMIC_COLUMN_WIDGET,
|
||||
DYNAMIC_ROW_WIDGET,
|
||||
DYNAMIC_GRID_WIDGET
|
||||
DYNAMIC_GRID_WIDGET,
|
||||
CARD_WITH_HEADER_FOOTER_AND_LAZY_COLUMN_WIDGET
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.navi.ap.common.models.lambdamodels
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
|
||||
@Parcelize
|
||||
data class AgreementLetterDownloadResponse(
|
||||
@SerializedName("requestId") val requestId: String? = null,
|
||||
@SerializedName("status") val status: String? = null,
|
||||
@SerializedName("loanAgreement") val loanAgreement: DocumentDetail? = null,
|
||||
@SerializedName("sanctionLetter") val sanctionLetter: DocumentDetail? = null,
|
||||
) : Parcelable {
|
||||
@Parcelize
|
||||
data class DocumentDetail(
|
||||
@SerializedName("referenceId") val referenceId: String? = null,
|
||||
@SerializedName("uri") val uri: String? = null,
|
||||
@SerializedName("rawCopyUri") val rawCopyUri: String? = null,
|
||||
) : Parcelable
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.navi.ap.common.models.lambdamodels
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
|
||||
@Parcelize
|
||||
data class EmiCalendarResponse(
|
||||
@SerializedName("dataList") val dataList: List<EmiResponse>? = null,
|
||||
@SerializedName("lastEmiData") val lastEmiData: EmiDetails? = null,
|
||||
@SerializedName("placeHolderKey") val placeHolderKey: String? = null,
|
||||
) : Parcelable {
|
||||
|
||||
@Parcelize
|
||||
data class EmiResponse(
|
||||
@SerializedName("year") val year: String? = null,
|
||||
@SerializedName("emiDataList") val emiDataList: List<EmiData>? = null,
|
||||
) : Parcelable
|
||||
|
||||
@Parcelize
|
||||
data class EmiData(
|
||||
@SerializedName("emiDetails") val emiDetails: EmiDetails? = null,
|
||||
) : Parcelable
|
||||
|
||||
@Parcelize
|
||||
data class EmiDetails(
|
||||
@SerializedName("month") val month: Int? = null,
|
||||
@SerializedName("date") val date: String? = null,
|
||||
@SerializedName("amount") val amount: String? = null,
|
||||
) : Parcelable
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.navi.ap.common.models.lambdamodels
|
||||
|
||||
data class EmiModel(
|
||||
val amount: AmountModel? = null,
|
||||
val firstEmiDueDate: String? = null,
|
||||
val rateOfInterest: String? = null,
|
||||
val tenureInMonths: String? = null,
|
||||
) {
|
||||
data class AmountModel(
|
||||
val amount: String? = null,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.navi.ap.common.renderer
|
||||
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.navi.ap.common.models.CardWithHeaderFooterAndLazyColumnWidgetData
|
||||
import com.navi.ap.common.models.UiTronResponseWithType
|
||||
import com.navi.ap.common.models.WidgetModelDefinition
|
||||
import com.navi.ap.common.viewmodel.CardWithHeaderFooterAndLazyColumnVM
|
||||
import com.navi.ap.common.viewmodel.LambdaVM
|
||||
import com.navi.ap.utils.constants.WIDGET_DATA
|
||||
import com.navi.base.utils.orFalse
|
||||
import com.navi.uitron.model.UiTronResponse
|
||||
import com.navi.uitron.render.UiTronRenderer
|
||||
import com.navi.uitron.utils.ShapeUtil
|
||||
import com.navi.uitron.viewmodel.UiTronViewModel
|
||||
import hexToComposeColor
|
||||
|
||||
|
||||
class CardWithHeaderFooterAndLazyColumnWidget {
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun Render(
|
||||
widget: WidgetModelDefinition<UiTronResponse>,
|
||||
lambdaVM: LambdaVM,
|
||||
viewModel: CardWithHeaderFooterAndLazyColumnVM = hiltViewModel(),
|
||||
) {
|
||||
val widgetData = remember {
|
||||
widget.widgetData?.data?.get("${WIDGET_DATA}${widget.widgetId}") as? CardWithHeaderFooterAndLazyColumnWidgetData
|
||||
}
|
||||
val containerProperty = widgetData?.containerProperty
|
||||
|
||||
val widgetListState = remember {
|
||||
mutableStateListOf<UiTronResponseWithType>()
|
||||
}
|
||||
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.getLazyColumnWidgetList(
|
||||
widgetData, widgetData?.listData, widgetListState
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(containerProperty?.backGroundColor?.hexToComposeColor ?: Color.White)
|
||||
.padding(containerProperty?.padding?.dp ?: 16.dp)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.border(
|
||||
border = BorderStroke(
|
||||
containerProperty?.borderStrokeData?.width?.dp ?: 1.dp,
|
||||
containerProperty?.borderStrokeData?.color?.hexToComposeColor
|
||||
?: Color.Gray
|
||||
),
|
||||
shape = ShapeUtil.getShape(shape = containerProperty?.borderStrokeData?.shape)
|
||||
)
|
||||
|
||||
) {
|
||||
UiRenderer(uiTronResponse = widgetData?.header, viewModel = lambdaVM)
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.heightIn(Dp.Unspecified, 400.dp) //mention max height here
|
||||
.offset(
|
||||
y = containerProperty?.verticalOffset?.dp ?: 0.dp
|
||||
)
|
||||
) {
|
||||
widgetListState.forEach { widgetViewWithType ->
|
||||
val widgetView = widgetViewWithType.uiTronResponse
|
||||
when {
|
||||
widgetViewWithType.isSticky.orFalse() -> {
|
||||
stickyHeader {
|
||||
UiRenderer(uiTronResponse = widgetView, viewModel = lambdaVM)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
item {
|
||||
UiRenderer(uiTronResponse = widgetView, viewModel = lambdaVM)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UiRenderer(uiTronResponse = widgetData?.footer, viewModel = lambdaVM)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UiRenderer(uiTronResponse: UiTronResponse?, viewModel: UiTronViewModel) {
|
||||
uiTronResponse?.parentComposeView?.let {
|
||||
UiTronRenderer(
|
||||
uiTronResponse.data, viewModel
|
||||
).Render(composeViews = it)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,12 @@
|
||||
package com.navi.ap.common.repository
|
||||
|
||||
import com.navi.ap.common.models.TelcoResendOtpRequest
|
||||
import com.navi.ap.common.models.lambdamodels.AgreementLetterDownloadResponse
|
||||
import com.navi.ap.common.models.lambdamodels.ApplyLoanResponse
|
||||
import com.navi.ap.common.models.lambdamodels.BankDataResponse
|
||||
import com.navi.ap.common.models.lambdamodels.ConsentRequest
|
||||
import com.navi.ap.common.models.lambdamodels.ConsentResponse
|
||||
import com.navi.ap.common.models.lambdamodels.EmiModel
|
||||
import com.navi.ap.common.models.lambdamodels.IfscBranchResponse
|
||||
import com.navi.ap.common.models.lambdamodels.FetchPaymentMethodsRequest
|
||||
import com.navi.ap.common.models.lambdamodels.OfferDetails
|
||||
@@ -33,7 +35,6 @@ import com.navi.common.network.models.SuccessResponse
|
||||
import com.navi.common.uitron.model.action.ApiType
|
||||
import com.navi.payment.model.SignalPaymentData
|
||||
import com.navi.payment.utils.Constants
|
||||
import org.json.JSONObject
|
||||
|
||||
open class LambdaRepository(val retrofitService: RetrofitService = retrofitService()) :
|
||||
ApplicationPlatformRepository(retrofitService) {
|
||||
@@ -82,6 +83,27 @@ open class LambdaRepository(val retrofitService: RetrofitService = retrofitServi
|
||||
return apiResponseCallback(response = response, apiTag = ApiType.GetScreenDefinition.name)
|
||||
}
|
||||
|
||||
suspend fun fetchRefillOfferDetails(
|
||||
amount: String? = null,
|
||||
tenureInMonths: String? = null,
|
||||
emiPlansSelected: String? = null,
|
||||
): ApRepoResult<Any> {
|
||||
val response =
|
||||
retrofitService.getRefillOfferDetails(
|
||||
amount = amount,
|
||||
tenureInMonths = tenureInMonths,
|
||||
emiPlansSelected = emiPlansSelected
|
||||
)
|
||||
logApEvent(
|
||||
Pair(
|
||||
RESULT,
|
||||
"${response.body()?.errors} | ${response.body()?.genericErrorBottomSheetFields}"
|
||||
),
|
||||
Pair(METHOD_NAME, ::fetchOfferDetails.name),
|
||||
Pair(STATUS_CODE, response.code().toString())
|
||||
)
|
||||
return apiResponseCallback(response = response, apiTag = ApiType.GetScreenDefinition.name)
|
||||
}
|
||||
suspend fun applyLoan(
|
||||
offerReferenceId: String? = null,
|
||||
offerDetails: OfferDetails? = null,
|
||||
@@ -324,4 +346,49 @@ open class LambdaRepository(val retrofitService: RetrofitService = retrofitServi
|
||||
)
|
||||
return apiResponseCallback(response = response, apiTag = ApiType.LambdaApiAction.name)
|
||||
}
|
||||
|
||||
suspend fun fetchEmiInstallments(emiModel: EmiModel): ApRepoResult<Any> {
|
||||
val response = retrofitService.fetchEmiInstallments(data = emiModel)
|
||||
logApEvent(
|
||||
Pair(
|
||||
RESULT,
|
||||
"${response.body()?.errors} | ${response.body()?.genericErrorBottomSheetFields}"
|
||||
),
|
||||
Pair(METHOD_NAME, ::fetchEmiInstallments.name),
|
||||
Pair(STATUS_CODE, response.code().toString())
|
||||
)
|
||||
return apiResponseCallback(response = response, apiTag = ApiType.LambdaApiAction.name)
|
||||
}
|
||||
|
||||
suspend fun initiateAgreementAndSanctionLetterDownload(
|
||||
loanApplicationId: String,
|
||||
documentType: String,
|
||||
): ApRepoResult<AgreementLetterDownloadResponse> {
|
||||
val response = retrofitService.initiateAgreementAndSanctionLetterDownload(
|
||||
loanApplicationId = loanApplicationId,
|
||||
documentType = documentType
|
||||
)
|
||||
logApEvent(
|
||||
Pair(
|
||||
RESULT,
|
||||
"${response.body()?.errors} | ${response.body()?.genericErrorBottomSheetFields}"
|
||||
),
|
||||
Pair(METHOD_NAME, ::initiateAgreementAndSanctionLetterDownload.name),
|
||||
Pair(STATUS_CODE, response.code().toString())
|
||||
)
|
||||
return apiResponseCallback(response = response, apiTag = ApiType.LambdaApiAction.name)
|
||||
}
|
||||
|
||||
suspend fun agreementGenerationStatus(loanApplicationId : String) : ApRepoResult<AgreementLetterDownloadResponse> {
|
||||
val response = retrofitService.agreementGenerationStatus(loanApplicationId)
|
||||
logApEvent(
|
||||
Pair(
|
||||
RESULT,
|
||||
"${response.body()?.errors} | ${response.body()?.genericErrorBottomSheetFields}"
|
||||
),
|
||||
Pair(METHOD_NAME, ::agreementGenerationStatus.name),
|
||||
Pair(STATUS_CODE, response.code().toString())
|
||||
)
|
||||
return apiResponseCallback(response = response, apiTag = ApiType.LambdaApiAction.name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.navi.ap.common.serializer
|
||||
|
||||
import com.navi.uitron.serializer.UiTronDataSerializer
|
||||
|
||||
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonSerializationContext
|
||||
import com.navi.ap.common.models.CardWithHeaderFooterAndLazyColumnWidgetData
|
||||
import com.navi.ap.common.models.CollapsableItemsWithTitleWidgetData
|
||||
import com.navi.ap.common.models.CustomWidgets
|
||||
import com.navi.ap.common.models.DynamicColumnWidgetData
|
||||
import com.navi.ap.common.models.DynamicGridWidgetData
|
||||
import com.navi.ap.common.models.DynamicRowWidgetData
|
||||
import com.navi.uitron.model.data.UiTronData
|
||||
import java.lang.reflect.Type
|
||||
|
||||
class CustomUiTronDataSerializer : UiTronDataSerializer() {
|
||||
override fun serialize(
|
||||
src: UiTronData?,
|
||||
typeOfSrc: Type?,
|
||||
context: JsonSerializationContext?,
|
||||
): JsonElement? {
|
||||
return when (src?.viewType) {
|
||||
CustomWidgets.COLLAPSABLE_ITEMS_WITH_TITLE_WIDGET.name -> {
|
||||
context?.serialize(
|
||||
src as CollapsableItemsWithTitleWidgetData,
|
||||
CollapsableItemsWithTitleWidgetData::class.java
|
||||
)
|
||||
}
|
||||
|
||||
CustomWidgets.DYNAMIC_GRID_WIDGET.name -> {
|
||||
context?.serialize(src as DynamicGridWidgetData, DynamicGridWidgetData::class.java)
|
||||
}
|
||||
|
||||
CustomWidgets.DYNAMIC_COLUMN_WIDGET.name -> {
|
||||
context?.serialize(
|
||||
src as DynamicColumnWidgetData,
|
||||
DynamicColumnWidgetData::class.java
|
||||
)
|
||||
}
|
||||
|
||||
CustomWidgets.DYNAMIC_ROW_WIDGET.name -> {
|
||||
context?.serialize(src as DynamicRowWidgetData, DynamicRowWidgetData::class.java)
|
||||
}
|
||||
|
||||
CustomWidgets.CARD_WITH_HEADER_FOOTER_AND_LAZY_COLUMN_WIDGET.name -> {
|
||||
context?.serialize(
|
||||
src as CardWithHeaderFooterAndLazyColumnWidgetData,
|
||||
CardWithHeaderFooterAndLazyColumnWidgetData::class.java
|
||||
)
|
||||
}
|
||||
|
||||
else -> super.serialize(src, typeOfSrc, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -161,10 +161,8 @@ fun ObserveToastVisibilityState(viewModel: LambdaVM?) {
|
||||
if (viewModel == null) return
|
||||
val toast = viewModel.toastStructure.collectAsState(WidgetModelDefinition())
|
||||
toast.value.let {
|
||||
if (it.widgetData?.data != null) {
|
||||
UiTronRenderer(it.widgetData.data, viewModel)
|
||||
.Render(composeViews = it.widgetData.parentComposeView.orEmpty())
|
||||
}
|
||||
UiTronRenderer(it.widgetData?.data, viewModel)
|
||||
.Render(composeViews = it.widgetData?.parentComposeView.orEmpty())
|
||||
viewModel.setToast(WidgetModelDefinition())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import com.navi.ap.utils.constants.FAILURE
|
||||
import com.navi.ap.utils.constants.RAZORPAY_DATA
|
||||
import com.navi.ap.utils.constants.SUCCESS
|
||||
import com.navi.ap.utils.constants.SUCCESS_CODE
|
||||
import com.navi.ap.utils.downloader.TaskDownloadManager
|
||||
import com.navi.common.ui.activity.BaseActivity
|
||||
import com.razorpay.PaymentData
|
||||
import com.razorpay.PaymentResultWithDataListener
|
||||
@@ -33,6 +34,9 @@ abstract class SdkHandlingActivity :
|
||||
|
||||
protected var apScreenVM: WeakReference<LambdaVM>? = null
|
||||
|
||||
@Inject
|
||||
lateinit var taskDownloadManager : TaskDownloadManager
|
||||
|
||||
override fun onDigioSuccess(digioResponse: DigioResponse) {
|
||||
sdkHandler.digioHandler.handleDigioSdkResult(
|
||||
digioStatus = SUCCESS,
|
||||
|
||||
@@ -50,6 +50,7 @@ import com.navi.ap.utils.constants.REASON
|
||||
import com.navi.ap.utils.constants.SCREEN_TYPE
|
||||
import com.navi.ap.utils.constants.SUBMIT_EVENT_HASH_EVENT
|
||||
import com.navi.ap.utils.helper.BottomSheetHelper
|
||||
import com.navi.ap.utils.helper.PeriodicTaskSchedulerFacade
|
||||
import com.navi.ap.utils.toMap
|
||||
import com.navi.ap.utils.toMutableMap
|
||||
import com.navi.base.model.CtaData
|
||||
@@ -83,7 +84,7 @@ constructor(private val repository: ApplicationPlatformRepository) : BaseVM() {
|
||||
@Inject
|
||||
lateinit var shouldPollStrategyUseCase: UpdateShouldPollStrategyUseCase
|
||||
|
||||
private var periodicTaskScheduler: PeriodicTaskScheduler? = null
|
||||
var periodicTaskScheduler: PeriodicTaskScheduler? = null
|
||||
protected var systemBackAction: UiTronActionData? = null
|
||||
private var queryMap: MutableMap<String, String?> = mutableMapOf()
|
||||
private var bottomSheetQueryMap: MutableMap<String, String?> = mutableMapOf()
|
||||
@@ -121,6 +122,9 @@ constructor(private val repository: ApplicationPlatformRepository) : BaseVM() {
|
||||
)
|
||||
val bottomSheetStateHolder = _bottomSheetStateHolder.asStateFlow()
|
||||
|
||||
protected val periodicTaskSchedulerFacade by lazy { PeriodicTaskSchedulerFacade() }
|
||||
|
||||
|
||||
fun updateBottomSheetUIState(
|
||||
bottomSheetState: ApBottomSheetState,
|
||||
bottomSheetStateChange: Boolean? = null,
|
||||
@@ -618,7 +622,7 @@ constructor(private val repository: ApplicationPlatformRepository) : BaseVM() {
|
||||
)
|
||||
}
|
||||
|
||||
protected fun stopApiPolling() {
|
||||
fun stopApiPolling() {
|
||||
periodicTaskScheduler?.stopTask()
|
||||
periodicTaskScheduler = null
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.navi.ap.common.viewmodel
|
||||
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.navi.ap.common.models.CardWithHeaderFooterAndLazyColumnWidgetData
|
||||
import com.navi.ap.common.models.UiTronResponseWithType
|
||||
import com.navi.ap.common.models.lambdamodels.EmiCalendarResponse
|
||||
import com.navi.ap.utils.appendIndexToDataKeys
|
||||
import com.navi.ap.utils.appendIndexToWidgetData
|
||||
import com.navi.ap.utils.getGsonBuilders
|
||||
import com.navi.ap.utils.injector.BasePathInjector
|
||||
import com.navi.ap.utils.toJson
|
||||
import com.navi.base.utils.isNotNullAndNotEmpty
|
||||
import com.navi.common.model.common.UiTronData
|
||||
import com.navi.uitron.model.UiTronResponse
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.launch
|
||||
import org.json.JSONObject
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class CardWithHeaderFooterAndLazyColumnVM @Inject constructor() : ViewModel() {
|
||||
|
||||
private var widgetIndex = 0
|
||||
|
||||
fun getLazyColumnWidgetList(
|
||||
widgetData: CardWithHeaderFooterAndLazyColumnWidgetData?,
|
||||
listData: EmiCalendarResponse?,
|
||||
widgetListState: SnapshotStateList<UiTronResponseWithType>,
|
||||
) {
|
||||
viewModelScope.launch(Dispatchers.Default) {
|
||||
val taskList = mutableListOf<Deferred<UiTronResponseWithType>>()
|
||||
listData?.dataList?.forEach { emiResponse ->
|
||||
if (emiResponse.year.isNotNullAndNotEmpty()) {
|
||||
val stickyHeaderTask = async {
|
||||
UiTronResponseWithType(
|
||||
true,
|
||||
getWidget(widgetData?.stickyHeaderItem, emiResponse, widgetIndex)
|
||||
)
|
||||
}
|
||||
widgetIndex.inc()
|
||||
taskList.add(stickyHeaderTask)
|
||||
}
|
||||
|
||||
emiResponse.emiDataList?.forEach { emiData ->
|
||||
val emiDataListTask = async {
|
||||
UiTronResponseWithType(
|
||||
false,
|
||||
getWidget(widgetData?.titleItem, emiData.emiDetails, widgetIndex)
|
||||
)
|
||||
}
|
||||
widgetIndex.inc()
|
||||
taskList.add(emiDataListTask)
|
||||
}
|
||||
}
|
||||
widgetListState.addAll(taskList.awaitAll())
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getWidget(
|
||||
widgetData: UiTronResponse?,
|
||||
data: Any? = null,
|
||||
index: Int
|
||||
): UiTronResponse {
|
||||
|
||||
val widgetObject = JSONObject(widgetData.toJson())
|
||||
appendIndexToWidgetData(widgetObject, index)
|
||||
|
||||
val modifiedWidgetData = getGsonBuilders()
|
||||
.fromJson(widgetObject.toString(), UiTronResponse::class.java)
|
||||
|
||||
val injectedData = BasePathInjector<Map<String, UiTronData?>, Any?>().injectData(
|
||||
modifiedWidgetData.appendIndexToDataKeys(index) ?: mapOf(),
|
||||
data
|
||||
)
|
||||
|
||||
return modifiedWidgetData.copy(data = injectedData.toMutableMap())
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import com.navi.ap.common.models.TelcoResendOtpRequest
|
||||
import com.navi.ap.common.models.WidgetModelDefinition
|
||||
import com.navi.ap.common.models.lambdamodels.BankDataResponse
|
||||
import com.navi.ap.common.models.lambdamodels.ConsentRequest
|
||||
import com.navi.ap.common.models.lambdamodels.EmiModel
|
||||
import com.navi.ap.common.models.lambdamodels.FetchPaymentMethodsRequest
|
||||
import com.navi.ap.common.models.lambdamodels.IfscBranchResponse
|
||||
import com.navi.ap.common.models.lambdamodels.OfferDetails
|
||||
@@ -40,10 +41,13 @@ import com.navi.ap.utils.constants.BANK_NAME
|
||||
import com.navi.ap.utils.constants.BANK_SEARCH_QUERY
|
||||
import com.navi.ap.utils.constants.COINS_VALIDATE_UPI_ID
|
||||
import com.navi.ap.utils.constants.DEFAULT_BANKS_SEARCH_QUERY
|
||||
import com.navi.ap.utils.constants.DOCUMENT_TYPE
|
||||
import com.navi.ap.utils.constants.EMI_AMOUNT
|
||||
import com.navi.ap.utils.constants.FETCH_OFFER_DETAILS_SELECTED_LOAN_AMOUNT
|
||||
import com.navi.ap.utils.constants.FETCH_OFFER_DETAILS_SELECTED_TENURE
|
||||
import com.navi.ap.utils.constants.FINARKEIN_CONSENT_DATA
|
||||
import com.navi.ap.utils.constants.FIRST_EMI_DUE_DATE
|
||||
import com.navi.ap.utils.constants.FAILURE_CAP
|
||||
import com.navi.ap.utils.constants.IFSC_CODE_KEY
|
||||
import com.navi.ap.utils.constants.IFSC_SEARCH_QUERY
|
||||
import com.navi.ap.utils.constants.INVALID_IFSC_CODE
|
||||
@@ -56,6 +60,7 @@ import com.navi.ap.utils.constants.MIN_QUERY_LENGTH
|
||||
import com.navi.ap.utils.constants.OFFER_REFERENCE_ID
|
||||
import com.navi.ap.utils.constants.PG_TOKEN
|
||||
import com.navi.ap.utils.constants.PREFERRED_BANKS_LIST
|
||||
import com.navi.ap.utils.constants.RATE_OF_INTEREST
|
||||
import com.navi.ap.utils.constants.RPD_PAYMENT_METHOD_ID
|
||||
import com.navi.ap.utils.constants.RPD_PAYMENT_METHOD_TOKEN
|
||||
import com.navi.ap.utils.constants.RPD_PAYMENT_METHOD_UPI_LINK_KEY
|
||||
@@ -65,8 +70,9 @@ import com.navi.ap.utils.constants.SELECTED_IFSC_DATA
|
||||
import com.navi.ap.utils.constants.SELECTED_LOAN_AMOUNT
|
||||
import com.navi.ap.utils.constants.SELECTED_TENURE
|
||||
import com.navi.ap.utils.constants.SELECTED_TENURE_PILL_INDEX
|
||||
import com.navi.ap.utils.constants.TENURE_IN_MONTHS
|
||||
import com.navi.ap.utils.constants.SUCCESS_CAP
|
||||
import com.navi.ap.utils.constants.TRIGGER_LOADING_STATE
|
||||
import com.navi.ap.utils.constants.TENURE_IN_MONTHS
|
||||
import com.navi.ap.utils.constants.TRUE
|
||||
import com.navi.ap.utils.getGsonBuilders
|
||||
import com.navi.ap.utils.getSignalPaymentData
|
||||
@@ -86,6 +92,7 @@ import com.navi.common.utils.deviceId
|
||||
import com.navi.common.utils.toJsonObject
|
||||
import com.navi.naviwidgets.utils.FORWARD_SLASH
|
||||
import com.navi.uitron.model.UiTronResponse
|
||||
import com.navi.uitron.model.data.UiTronActionData
|
||||
import getInputId
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
@@ -224,6 +231,83 @@ open class LambdaVM constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun fetchRefillOfferDetails(
|
||||
resolvedValues: MutableMap<String, Any?>,
|
||||
lambdaApiAction: LambdaApiAction
|
||||
) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
if (_clonedScreenDefinitionState.value == null) {
|
||||
_clonedScreenDefinitionState.value = getScreenStructurePreRenderState()
|
||||
}
|
||||
|
||||
val savedAmount = handle.get<String>(FETCH_OFFER_DETAILS_SELECTED_LOAN_AMOUNT)
|
||||
val savedTenure = handle.get<String>(FETCH_OFFER_DETAILS_SELECTED_TENURE)
|
||||
|
||||
val amount = resolvedValues[SELECTED_LOAN_AMOUNT]?.toString()
|
||||
var tenure = resolvedValues[SELECTED_TENURE]?.toString()
|
||||
val emiPlanSelected = resolvedValues[SELECTED_TENURE_PILL_INDEX]?.toString()
|
||||
|
||||
if (
|
||||
savedAmount == amount &&
|
||||
savedTenure == tenure &&
|
||||
savedAmount != null &&
|
||||
savedTenure != null
|
||||
)
|
||||
return@launch
|
||||
handleActions(lambdaApiAction.preExecutionAction)
|
||||
_lambdaState.value = LambdaState.Loading
|
||||
|
||||
if (
|
||||
resolvedValues[LOAN_SLIDER] == TRUE &&
|
||||
emiPlanSelected != LAST_SELECTED_TENURE_PILL_INDEX
|
||||
) {
|
||||
tenure = null
|
||||
}
|
||||
|
||||
val lambdaResponse =
|
||||
repository.fetchRefillOfferDetails(
|
||||
amount = amount,
|
||||
tenureInMonths = tenure,
|
||||
emiPlansSelected = emiPlanSelected
|
||||
)
|
||||
_lambdaState.value =
|
||||
when {
|
||||
lambdaResponse.data != null && _clonedScreenDefinitionState.value != null -> {
|
||||
LambdaState.Success(LambdaResponseType())
|
||||
val updatedResponse =
|
||||
BasePathInjector<ApScreenDefinitionStructure, Any?>()
|
||||
.injectData(
|
||||
_clonedScreenDefinitionState.value,
|
||||
lambdaResponse.data
|
||||
)
|
||||
|
||||
updatedResponse?.let {
|
||||
metaData = updatedResponse.screenData?.metaData ?: mapOf()
|
||||
_screenDefinitionState.value =
|
||||
ApScreenDefinitionState.Success(updatedResponse)
|
||||
}
|
||||
systemBackAction =
|
||||
updatedResponse?.screenData?.screenStructure?.systemBackCta
|
||||
handle[FETCH_OFFER_DETAILS_SELECTED_LOAN_AMOUNT] = amount
|
||||
handle[FETCH_OFFER_DETAILS_SELECTED_TENURE] = tenure
|
||||
LambdaState.Success(LambdaResponseType())
|
||||
}
|
||||
|
||||
lambdaResponse.errorBottomSheetStructure.isNotNull() -> {
|
||||
LambdaState.Error(
|
||||
lambdaResponse.statusCode,
|
||||
lambdaResponse.errorBottomSheetStructure,
|
||||
lambdaResponse.genericErrorBottomSheetFields
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
LambdaState.Nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO : Need to remove it when bankDetails will be powered through AP
|
||||
fun applyLoan(resolvedValues: MutableMap<String, Any?>) {
|
||||
viewModelScope.launch(Dispatchers.Default) {
|
||||
@@ -739,7 +823,10 @@ open class LambdaVM constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun fetchMandateMethods(resolvedValues: MutableMap<String, Any?>, lambdaApiAction: LambdaApiAction) {
|
||||
fun fetchMandateMethods(
|
||||
resolvedValues: MutableMap<String, Any?>,
|
||||
lambdaApiAction: LambdaApiAction
|
||||
) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
if (_clonedScreenDefinitionState.value == null) {
|
||||
_clonedScreenDefinitionState.value = getScreenStructurePreRenderState()
|
||||
@@ -853,10 +940,121 @@ open class LambdaVM constructor(
|
||||
lambdaResponse.genericErrorBottomSheetFields
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
LambdaState.Nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun fetchEmiInstallments(lambdaApiAction: LambdaApiAction, resolvedValues: MutableMap<String, Any?>) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
if (_clonedScreenDefinitionState.value == null) {
|
||||
_clonedScreenDefinitionState.value = getScreenStructurePreRenderState()
|
||||
}
|
||||
_lambdaState.value = LambdaState.Loading
|
||||
val lambdaResponse = repository.fetchEmiInstallments(
|
||||
EmiModel(
|
||||
amount = EmiModel.AmountModel(amount = resolvedValues[SELECTED_LOAN_AMOUNT].toString()),
|
||||
firstEmiDueDate = resolvedValues[FIRST_EMI_DUE_DATE].toString(),
|
||||
rateOfInterest = resolvedValues[RATE_OF_INTEREST].toString(),
|
||||
tenureInMonths = resolvedValues[SELECTED_TENURE].toString()
|
||||
)
|
||||
)
|
||||
|
||||
_lambdaState.value = when {
|
||||
|
||||
lambdaResponse.data.isNotNull() && _clonedScreenDefinitionState.value.isNotNull() -> {
|
||||
val updatedResponse = PathInjector<ApScreenDefinitionStructure, Any?>()
|
||||
.injectData(
|
||||
_clonedScreenDefinitionState.value,
|
||||
lambdaResponse.data
|
||||
)
|
||||
|
||||
updatedResponse?.let {
|
||||
_screenDefinitionState.value =
|
||||
ApScreenDefinitionState.Success(updatedResponse)
|
||||
}
|
||||
handleActions(lambdaApiAction.onSuccess)
|
||||
LambdaState.Success(LambdaResponseType())
|
||||
}
|
||||
|
||||
lambdaResponse.errorBottomSheetStructure.isNotNull() -> {
|
||||
handleActions(lambdaApiAction.onFailure)
|
||||
LambdaState.Error(
|
||||
lambdaResponse.statusCode,
|
||||
null,
|
||||
lambdaResponse.genericErrorBottomSheetFields
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
LambdaState.Nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun initiateAgreementAndSanctionLetterDownload(
|
||||
resolvedValues: MutableMap<String, Any?>,
|
||||
lambdaApiAction: LambdaApiAction
|
||||
) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val loanApplicationId = resolvedValues[LOAN_APPLICATION_ID]?.toString().orEmpty()
|
||||
val documentType = resolvedValues[DOCUMENT_TYPE]?.toString().orEmpty()
|
||||
val lambdaResponse = repository.initiateAgreementAndSanctionLetterDownload(loanApplicationId = loanApplicationId, documentType = documentType)
|
||||
when {
|
||||
lambdaResponse.data.isNotNull() -> {
|
||||
lambdaResponse.data?.requestId?.let {
|
||||
periodicTaskSchedulerFacade.scheduleTask(taskId = documentType){
|
||||
agreementGenerationStatus(requestId = it,lambdaApiAction = lambdaApiAction,taskId = documentType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lambdaResponse.errorBottomSheetStructure.isNotNull() -> {
|
||||
LambdaState.Error(
|
||||
lambdaResponse.statusCode,
|
||||
null,
|
||||
lambdaResponse.genericErrorBottomSheetFields
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun agreementGenerationStatus(requestId: String, lambdaApiAction: LambdaApiAction, taskId : String){
|
||||
viewModelScope.launch {
|
||||
val lambdaResponse = repository.agreementGenerationStatus(requestId)
|
||||
when {
|
||||
lambdaResponse.data.isNotNull() -> {
|
||||
when(lambdaResponse.data?.status){
|
||||
SUCCESS_CAP -> {
|
||||
val updatedSuccessAction = PathInjector<UiTronActionData, Any?>()
|
||||
.injectData(
|
||||
lambdaApiAction.onSuccess,
|
||||
lambdaResponse.data
|
||||
)
|
||||
handleActions(updatedSuccessAction)
|
||||
periodicTaskSchedulerFacade.stopTask(taskId)
|
||||
}
|
||||
FAILURE_CAP -> {
|
||||
periodicTaskSchedulerFacade.stopTask(taskId)
|
||||
}
|
||||
}
|
||||
}
|
||||
lambdaResponse.errorBottomSheetStructure.isNotNull() -> {
|
||||
periodicTaskSchedulerFacade.stopTask(taskId)
|
||||
LambdaState.Error(
|
||||
lambdaResponse.statusCode,
|
||||
null,
|
||||
lambdaResponse.genericErrorBottomSheetFields
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.navi.ap.common.widgetfactory
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.navi.ap.common.models.CustomWidgets
|
||||
import com.navi.ap.common.models.WidgetModelDefinition
|
||||
import com.navi.ap.common.renderer.CardWithHeaderFooterAndLazyColumnWidget
|
||||
import com.navi.ap.common.renderer.CollapsableItemsWithTitleWidget
|
||||
import com.navi.ap.common.renderer.DynamicColumnWidget
|
||||
import com.navi.ap.common.renderer.DynamicGridWidget
|
||||
@@ -29,6 +30,9 @@ fun CustomWidgetRenderer(
|
||||
CustomWidgets.DYNAMIC_GRID_WIDGET.name -> {
|
||||
DynamicGridWidget().Render(viewModel = viewModel, widget = widget)
|
||||
}
|
||||
CustomWidgets.CARD_WITH_HEADER_FOOTER_AND_LAZY_COLUMN_WIDGET.name -> {
|
||||
CardWithHeaderFooterAndLazyColumnWidget().Render(lambdaVM = viewModel,widget = widget)
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.navi.ap.di
|
||||
|
||||
import com.navi.ap.utils.downloader.TaskDownloadManager
|
||||
import com.navi.ap.utils.downloader.TaskDownloadManagerImpl
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.components.ActivityComponent
|
||||
|
||||
|
||||
@Module
|
||||
@InstallIn(ActivityComponent::class)
|
||||
abstract class ActivityComponentManagerModule {
|
||||
@Binds
|
||||
abstract fun getTaskDownloadManager(taskDownloadManagerImpl: TaskDownloadManagerImpl): TaskDownloadManager
|
||||
}
|
||||
@@ -7,6 +7,9 @@
|
||||
|
||||
package com.navi.ap.di
|
||||
|
||||
import android.app.DownloadManager
|
||||
import android.content.Context
|
||||
import android.content.Context.DOWNLOAD_SERVICE
|
||||
import com.navi.ap.common.sdk.handler.DigioHandler
|
||||
import com.navi.ap.common.sdk.handler.FinoramicHandler
|
||||
import com.navi.ap.common.sdk.handler.RazorpayHandler
|
||||
@@ -20,9 +23,11 @@ import com.navi.payment.razorpay.RazorpayHelper
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
object ProviderModule {
|
||||
@@ -75,4 +80,9 @@ object ProviderModule {
|
||||
fun getUpdateShouldPollStrategyUseCase(): UpdateShouldPollStrategyUseCase =
|
||||
UpdateShouldPollStrategyUseCase()
|
||||
|
||||
}
|
||||
@Provides
|
||||
@Singleton
|
||||
fun getDownloadManager(@ApplicationContext context : Context) : DownloadManager? {
|
||||
return context.getSystemService(DOWNLOAD_SERVICE) as? DownloadManager
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import com.navi.ap.utils.constants.ApScreenDestinations
|
||||
import com.navi.ap.utils.identifier
|
||||
import com.navi.base.model.CtaData
|
||||
import com.navi.base.utils.orFalse
|
||||
import com.navi.common.utils.Constants.ADDITIONAL_PARAMETERS
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
@@ -35,6 +36,10 @@ class PlatformNavigationHandler @Inject constructor(): NavigationHandler {
|
||||
apScreenDestination: ApScreenDestinations,
|
||||
) {
|
||||
ctaData.parameters?.forEach { item -> bundle.putString(item.key, item.value) }
|
||||
val nextScreenBundle = Bundle().apply {
|
||||
ctaData.additionalParameters?.forEach { item -> putString(item.key, item.value) }
|
||||
}
|
||||
bundle.putBundle(ADDITIONAL_PARAMETERS, nextScreenBundle)
|
||||
|
||||
if (apScreenDestination == ApScreenDestinations.AP_LAUNCHER) {
|
||||
activity.navController.popBackStack()
|
||||
@@ -44,7 +49,7 @@ class PlatformNavigationHandler @Inject constructor(): NavigationHandler {
|
||||
|
||||
val isScreenPresent = isDestinationScreenPresentInBackStack(activity, ctaScreenId)
|
||||
|
||||
if (bundle.getString(APP_ACTION) == ApNavigationActions.BACK.name) {
|
||||
if (bundle.getString(APP_ACTION) == ApNavigationActions.BACK.name && ctaData.refreshScreen.orFalse().not()) {
|
||||
if (isScreenPresent) {
|
||||
clearBackStackTillDestinationScreen(activity, ctaScreenId)
|
||||
} else {
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.navi.ap.common.repository.ApplicationPlatformRepository
|
||||
import com.navi.ap.common.repository.LambdaRepository
|
||||
import com.navi.ap.common.serializer.CustomUiTronDataSerializer
|
||||
import com.navi.ap.network.retrofit.NaviApHttpClient
|
||||
import com.navi.ap.network.retrofit.service.RetrofitService
|
||||
import com.navi.ap.network.utils.getNetworkInfo
|
||||
@@ -36,7 +37,6 @@ import com.navi.uitron.model.data.UiTronData
|
||||
import com.navi.uitron.model.ui.BaseProperty
|
||||
import com.navi.uitron.model.visualtransformation.VisualTransformationData
|
||||
import com.navi.uitron.serializer.ComposePropertySerializer
|
||||
import com.navi.uitron.serializer.UiTronDataSerializer
|
||||
import com.navi.uitron.serializer.UiTronValidationSerializer
|
||||
import com.navi.uitron.serializer.VisualTransformationDataSerializer
|
||||
import dagger.Module
|
||||
@@ -76,7 +76,7 @@ object APNetworkModule {
|
||||
@Named("apGson")
|
||||
fun providesSerializer(): Gson = GsonBuilder()
|
||||
.registerTypeAdapter(BaseProperty::class.java, ComposePropertySerializer())
|
||||
.registerTypeAdapter(UiTronData::class.java, UiTronDataSerializer())
|
||||
.registerTypeAdapter(UiTronData::class.java, CustomUiTronDataSerializer())
|
||||
.registerTypeAdapter(UiTronAction::class.java, UiTronActionSerializer())
|
||||
.registerTypeAdapter(UploadDataConfig::class.java, UiTronUploadDataSerializer())
|
||||
.registerTypeAdapter(BaseInputValidation::class.java, UiTronValidationSerializer())
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package com.navi.ap.network.retrofit.service
|
||||
|
||||
import com.navi.ap.common.models.TelcoResendOtpRequest
|
||||
import com.navi.ap.common.models.lambdamodels.AgreementLetterDownloadResponse
|
||||
import com.navi.ap.common.models.lambdamodels.ApplyLoanResponse
|
||||
import com.navi.ap.common.models.lambdamodels.BankDataResponse
|
||||
import com.navi.ap.common.models.lambdamodels.ConsentRequest
|
||||
import com.navi.ap.common.models.lambdamodels.ConsentResponse
|
||||
import com.navi.ap.common.models.lambdamodels.EmiModel
|
||||
import com.navi.ap.common.models.lambdamodels.FetchPaymentMethodsRequest
|
||||
import com.navi.ap.common.models.lambdamodels.IfscBranchResponse
|
||||
import com.navi.ap.common.models.lambdamodels.OfferDetails
|
||||
@@ -44,6 +46,13 @@ interface PlLambdaService {
|
||||
@Header("X-Target") target: String = ModuleName.LE.name,
|
||||
): Response<GenericResponse<Any>>
|
||||
|
||||
@GET("/customer/refill-offer-details")
|
||||
suspend fun getRefillOfferDetails(
|
||||
@Query("amount") amount: String? = null,
|
||||
@Query("tenureInMonths") tenureInMonths: String? = null,
|
||||
@Query("emiPlansSelected") emiPlansSelected: String? = null,
|
||||
@Header("X-Target") target: String = ModuleName.LE.name,
|
||||
): Response<GenericResponse<Any>>
|
||||
@POST("/offer/{offerReferenceId}/apply")
|
||||
suspend fun applyLoan(
|
||||
@Header("X-Target") target: String = ModuleName.LE.name,
|
||||
@@ -132,4 +141,23 @@ interface PlLambdaService {
|
||||
@Header("X-Payment-SDK-Version") version: String = Constants.PAYMENT_SDK_VERSION
|
||||
): Response<GenericResponse<Any>>
|
||||
|
||||
@POST("/emi/fetch-installments")
|
||||
suspend fun fetchEmiInstallments(
|
||||
@Header("X-Target") xTarget: String = "OPL",
|
||||
@Body data: EmiModel? = null,
|
||||
): Response<GenericResponse<Any>>
|
||||
|
||||
@POST("/loan-agreement/generate/{loanApplicationId}")
|
||||
suspend fun initiateAgreementAndSanctionLetterDownload(
|
||||
@Path("loanApplicationId") loanApplicationId: String,
|
||||
@Header("X-Target") xTarget: String = "OPL",
|
||||
@Query("documentType") documentType: String? = null
|
||||
): Response<GenericResponse<AgreementLetterDownloadResponse>>
|
||||
|
||||
@GET("/loan-agreement/requests/{requestId}")
|
||||
suspend fun agreementGenerationStatus(
|
||||
@Path("requestId") requestId: String,
|
||||
@Header("X-Target") xTarget: String = "OPL",
|
||||
): Response<GenericResponse<AgreementLetterDownloadResponse>>
|
||||
|
||||
}
|
||||
@@ -38,6 +38,8 @@ import com.navi.ap.utils.constants.SCREEN_TYPE
|
||||
import com.navi.ap.utils.constants.UNDERSCORE_POLL
|
||||
import com.navi.ap.utils.constants.noLoaderScreenTypes
|
||||
import com.navi.ap.utils.logger.ScreenTimeLifecycleLogger
|
||||
import com.navi.ap.utils.initParameters
|
||||
import com.navi.ap.utils.initScreenEvent
|
||||
import com.navi.base.utils.orElse
|
||||
import com.navi.common.utils.EMPTY
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
@@ -55,12 +57,12 @@ fun ApGenericScreen(
|
||||
activity = activity,
|
||||
renderScaffoldState = viewModel.renderScaffoldState.collectAsState().value
|
||||
)
|
||||
|
||||
handleLoadingState(activity, viewModel)
|
||||
|
||||
HandleLoadingState(activity, viewModel)
|
||||
LaunchedEffect(Unit){
|
||||
initParameters(bundle,viewModel)
|
||||
initScreenEvent(activity = activity, screenName = viewModel.getQueryMap()[APP_PLATFORM_SCREEN_ID].orEmpty(), viewModel = viewModel)
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.setQueryMap(bundle)
|
||||
viewModel.eventUtils.setCurrentScreen(activity = activity, screenName = viewModel.getQueryMap()[APP_PLATFORM_SCREEN_ID].orEmpty())
|
||||
activity.getApplicationPlatformSharedVM().getScreenDefinitionStructure()?.let {
|
||||
viewModel.setScreenDefinitionResponse(it)
|
||||
activity.getApplicationPlatformSharedVM()
|
||||
@@ -108,19 +110,18 @@ fun ApGenericScreen(
|
||||
handleLambdaState(activity = activity, viewModel = viewModel, lambdaState = lambdaState)
|
||||
}
|
||||
}
|
||||
|
||||
handleLoaderState(activity, viewModel)
|
||||
HandleLoaderState(activity, viewModel)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun handleLoadingState(activity: ApplicationPlatformActivity, viewModel: ApGenericScreenVM) {
|
||||
private fun HandleLoadingState(activity: ApplicationPlatformActivity, viewModel: ApGenericScreenVM) {
|
||||
if (viewModel.renderScaffoldState.collectAsState().value.not() && viewModel.screenDefinitionState.collectAsState().value !is ApScreenDefinitionState.Error) {
|
||||
ShowShimmerLoader(activity.intent?.extras?.getString(APP_PLATFORM_APPLICATION_TYPE))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun handleLoaderState(activity: ApplicationPlatformActivity, viewModel: LambdaVM){
|
||||
private fun HandleLoaderState(activity: ApplicationPlatformActivity, viewModel: LambdaVM){
|
||||
LaunchedEffect(activity.loaderState.value) {
|
||||
if (activity.loaderState.value.not()) {
|
||||
viewModel.handle.get<String>(HIDE_LOADER_STATE_ID)?.let {
|
||||
|
||||
@@ -47,6 +47,7 @@ import com.navi.ap.utils.constants.SUPER_APP_HOME
|
||||
import com.navi.ap.utils.constants.UNDERSCORE_POLL
|
||||
import com.navi.ap.utils.constants.noLoaderScreenTypes
|
||||
import com.navi.ap.utils.logger.ScreenTimeLifecycleLogger
|
||||
import com.navi.ap.utils.initParameters
|
||||
import com.navi.base.deeplink.DeepLinkManager
|
||||
import com.navi.base.model.CtaData
|
||||
import com.navi.base.model.LineItem
|
||||
@@ -71,10 +72,9 @@ fun ApLauncher(
|
||||
activity = activity,
|
||||
renderScaffoldState = viewModel.renderScaffoldState.collectAsState().value
|
||||
)
|
||||
handleLoadingState(activity, viewModel)
|
||||
|
||||
HandleLoadingState(activity, viewModel)
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.setQueryMap(bundle ?: activity.intent.extras)
|
||||
initParameters(bundle = bundle ?: activity.intent.extras, viewModel = viewModel)
|
||||
viewModel.getQueryMap()[APP_PLATFORM_SCREEN_ID] = LAUNCHER_SCREEN
|
||||
handleRedirection(
|
||||
activity = activity,
|
||||
@@ -100,7 +100,7 @@ fun ApLauncher(
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun handleLoadingState(activity: ApplicationPlatformActivity, viewModel: ApGenericScreenVM) {
|
||||
private fun HandleLoadingState(activity: ApplicationPlatformActivity, viewModel: ApGenericScreenVM) {
|
||||
if (viewModel.createApplicationAndGetCtaResponseState.collectAsState().value == ApplicationIdState.Loading || viewModel.ctaResponseState.collectAsState().value == GetCtaState.Loading) {
|
||||
ShowShimmerLoader(activity.intent?.extras?.getString(APP_PLATFORM_APPLICATION_TYPE))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.navi.ap.utils
|
||||
|
||||
import android.os.Bundle
|
||||
import com.navi.ap.common.ui.ApplicationPlatformActivity
|
||||
import com.navi.ap.common.viewmodel.LambdaVM
|
||||
import com.navi.ap.screens.genericscreen.vm.ApGenericScreenVM
|
||||
import com.navi.ap.utils.constants.APP_PLATFORM_APPLICATION_ID
|
||||
import com.navi.ap.utils.constants.APP_PLATFORM_APPLICATION_TYPE
|
||||
import com.navi.ap.utils.constants.APP_PLATFORM_SCREEN_ID
|
||||
import com.navi.common.utils.Constants
|
||||
|
||||
|
||||
/**
|
||||
* Put all screen-related utilities in this section for use by other screens.
|
||||
**/
|
||||
|
||||
internal fun initParameters(bundle: Bundle?, viewModel: ApGenericScreenVM) {
|
||||
val handleBundle = bundle?.getBundle(Constants.ADDITIONAL_PARAMETERS)
|
||||
handleBundle?.keySet()?.forEach {
|
||||
viewModel.handle[it] = handleBundle.getString(it)
|
||||
}
|
||||
viewModel.setQueryMap(bundle)
|
||||
initHandleWithPlatformParameters(viewModel)
|
||||
}
|
||||
|
||||
internal fun initHandleWithPlatformParameters(viewModel: ApGenericScreenVM) {
|
||||
viewModel.handle[APP_PLATFORM_APPLICATION_TYPE] =
|
||||
viewModel.getQueryMap()[APP_PLATFORM_APPLICATION_TYPE]
|
||||
|
||||
viewModel.handle[APP_PLATFORM_APPLICATION_ID] =
|
||||
viewModel.getQueryMap()[APP_PLATFORM_APPLICATION_ID]
|
||||
|
||||
viewModel.handle[APP_PLATFORM_SCREEN_ID] =
|
||||
viewModel.getQueryMap()[APP_PLATFORM_SCREEN_ID]
|
||||
|
||||
}
|
||||
|
||||
internal fun initScreenEvent(
|
||||
activity: ApplicationPlatformActivity,
|
||||
screenName: String,
|
||||
viewModel: LambdaVM,
|
||||
) {
|
||||
viewModel.eventUtils.setCurrentScreen(activity = activity, screenName = screenName)
|
||||
}
|
||||
@@ -51,6 +51,7 @@ enum class FaqType {
|
||||
enum class LambdaType {
|
||||
TELCO_RESEND_OTP,
|
||||
LOAN_OFFER_DETAILS,
|
||||
REFILL_LOAN_DETAILS,
|
||||
APPLY_LOAN,
|
||||
FETCH_RPD_TOKEN,
|
||||
FETCH_RPD_PAYMENT_METHOD_DETAILS,
|
||||
@@ -65,5 +66,8 @@ enum class LambdaType {
|
||||
POST_MANDATE_STATUS,
|
||||
FETCH_MANDATE_OPTIONS,
|
||||
POST_PAYMENT_METHOD_STATUS,
|
||||
VALIDATE_COINS_UPI_ID
|
||||
VALIDATE_COINS_UPI_ID,
|
||||
FETCH_EMI_CALENDER_DETAILS,
|
||||
DOWNLOAD_LOAN_AGREEMENT_AND_SANCTION_LETTER
|
||||
|
||||
}
|
||||
|
||||
@@ -28,3 +28,6 @@ const val SCREEN_LANDS = "screen_lands"
|
||||
const val SCREEN_EXIT = "screen_exit"
|
||||
const val AP_SCREEN_TIME_SPENT_EVENT = "ap_screen_time_spent"
|
||||
const val ATTR_SCREEN_TIME_SPENT = "screen_time_spent"
|
||||
|
||||
const val STATUS = "status"
|
||||
const val DEV_DOCUMENT_DOWNLOADED_STATUS = "dev_document_downloaded_status"
|
||||
|
||||
@@ -13,6 +13,8 @@ const val SELECTED_TENURE_PILL_INDEX = "selected_tenure_pill_index"
|
||||
const val OFFER_REFERENCE_ID = "offerReferenceId"
|
||||
const val LOAN_APPLICATION_V2 = "LOAN_APPLICATION_V2"
|
||||
const val LOAN_SLIDER = "loan_slider"
|
||||
const val RATE_OF_INTEREST = "rateOfInterest"
|
||||
const val FIRST_EMI_DUE_DATE = "firstEmiDueDate"
|
||||
const val TRUE = "true"
|
||||
const val LAST_SELECTED_TENURE_PILL_INDEX = "2"
|
||||
const val FETCH_OFFER_DETAILS_SELECTED_LOAN_AMOUNT = "fetchOfferDetails_selectedLoanAmount"
|
||||
@@ -42,4 +44,7 @@ const val TENURE_IN_MONTHS = "tenureInMonths"
|
||||
const val ORCHESTRATION_PL = "OPL"
|
||||
const val COINS_VALIDATE_UPI_ID = "coinsUPIId"
|
||||
const val DEFAULT_BANKS_SEARCH_QUERY = ""
|
||||
const val TRIGGER_LOADING_STATE = "trigger_loading_state"
|
||||
const val TRIGGER_LOADING_STATE = "trigger_loading_state"
|
||||
const val SUCCESS_CAP = "SUCCESS"
|
||||
const val FAILURE_CAP = "FAILURE"
|
||||
const val DOCUMENT_TYPE = "documentType"
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.navi.ap.utils.downloader
|
||||
|
||||
import android.app.DownloadManager
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.navi.analytics.utils.SCREEN_NAME
|
||||
import com.navi.ap.utils.constants.DEV_DOCUMENT_DOWNLOADED_STATUS
|
||||
import com.navi.ap.utils.constants.STATUS
|
||||
import com.navi.ap.utils.constants.SUCCESS
|
||||
import com.navi.ap.utils.logApEvent
|
||||
import com.navi.common.constants.FAILED
|
||||
import com.navi.common.uitron.model.action.DownloadAction
|
||||
|
||||
|
||||
class DownloaderBroadcastReceiver(
|
||||
private val downloadManager: DownloadManager,
|
||||
private val downloadInProgressIds: MutableSet<Long?>,
|
||||
private val downloadAction: DownloadAction,
|
||||
private val downloadCallback: DownloadCallback,
|
||||
) : BroadcastReceiver() {
|
||||
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
intent?.let { getDownloadStatus(intent = it) }
|
||||
}
|
||||
|
||||
private fun getDownloadStatus(intent: Intent) {
|
||||
val taskId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
|
||||
if (taskId == -1L || downloadInProgressIds.contains(taskId).not()) return
|
||||
|
||||
val query = DownloadManager.Query().setFilterById(taskId)
|
||||
val cursor = downloadManager.query(query) ?: return
|
||||
|
||||
if (cursor.moveToFirst()) {
|
||||
val columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)
|
||||
if (columnIndex == -1) return
|
||||
handleTaskStatus(taskStatus = cursor.getLong(columnIndex), taskId = taskId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleTaskStatus(taskStatus: Long, taskId: Long) {
|
||||
when (taskStatus) {
|
||||
DownloadManager.STATUS_SUCCESSFUL.toLong() -> {
|
||||
removeTaskAndLog(taskId, SUCCESS)
|
||||
downloadCallback.onDownloadSuccess(downloadId = taskId)
|
||||
|
||||
}
|
||||
|
||||
DownloadManager.STATUS_FAILED.toLong() -> {
|
||||
removeTaskAndLog(taskId, FAILED)
|
||||
downloadCallback.onDownloadFailure(downloadId = taskId)
|
||||
}
|
||||
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeTaskAndLog(taskId: Long, status: String) {
|
||||
downloadInProgressIds.remove(taskId)
|
||||
logApEvent(
|
||||
Pair(SCREEN_NAME, downloadAction.config?.screenName.orEmpty()),
|
||||
Pair(STATUS, status),
|
||||
Pair(SCREEN_NAME, downloadAction.config?.screenName.orEmpty()),
|
||||
eventName = DEV_DOCUMENT_DOWNLOADED_STATUS
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.navi.ap.utils.downloader
|
||||
|
||||
|
||||
import com.navi.common.uitron.model.action.DownloadAction
|
||||
|
||||
|
||||
interface TaskDownloadManager {
|
||||
suspend fun startDownload(downloadAction: DownloadAction,downloadCallback: DownloadCallback)
|
||||
}
|
||||
|
||||
interface DownloadCallback {
|
||||
fun onDownloadSuccess(downloadId : Long)
|
||||
fun onDownloadFailure(downloadId : Long)
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.navi.ap.utils.downloader
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.DownloadManager
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.IntentFilter
|
||||
import android.net.Uri
|
||||
import android.os.Environment
|
||||
import com.navi.common.uitron.model.action.DownloadAction
|
||||
import dagger.hilt.android.qualifiers.ActivityContext
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
class TaskDownloadManagerImpl @Inject constructor(
|
||||
@ActivityContext private val context: Context,
|
||||
private val downloadManager: DownloadManager?,
|
||||
) : TaskDownloadManager {
|
||||
|
||||
private var downloadInProgressIds: MutableSet<Long?> = mutableSetOf()
|
||||
private var downloadStatusReceiver: BroadcastReceiver? = null
|
||||
|
||||
|
||||
private fun getDownloaderRequest(downloadAction: DownloadAction): DownloadManager.Request =
|
||||
DownloadManager.Request(Uri.parse(downloadAction.documentUrl)).apply {
|
||||
setDestinationInExternalPublicDir(
|
||||
Environment.DIRECTORY_DOWNLOADS,
|
||||
downloadAction.config?.documentName
|
||||
)
|
||||
setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
|
||||
setTitle(downloadAction.config?.documentName)
|
||||
setAllowedOverMetered(true)
|
||||
setAllowedOverRoaming(true)
|
||||
}
|
||||
|
||||
@SuppressLint("UnspecifiedRegisterReceiverFlag")
|
||||
private fun initReceivers(
|
||||
downloadAction: DownloadAction,
|
||||
downloadCallback: DownloadCallback,
|
||||
) {
|
||||
if (downloadStatusReceiver != null) return
|
||||
downloadManager?.let {
|
||||
downloadStatusReceiver = DownloaderBroadcastReceiver(
|
||||
downloadManager = downloadManager,
|
||||
downloadInProgressIds = downloadInProgressIds,
|
||||
downloadAction = downloadAction,
|
||||
downloadCallback = downloadCallback
|
||||
)
|
||||
}
|
||||
val downloadCompleteIntentFilter = IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
|
||||
context.registerReceiver(downloadStatusReceiver, downloadCompleteIntentFilter)
|
||||
}
|
||||
|
||||
override suspend fun startDownload(
|
||||
downloadAction: DownloadAction,
|
||||
downloadCallback: DownloadCallback,
|
||||
): Unit = withContext(Dispatchers.IO) {
|
||||
initReceivers(downloadAction, downloadCallback)
|
||||
val downloadId = downloadManager?.enqueue(getDownloaderRequest(downloadAction))
|
||||
downloadInProgressIds.add(downloadId)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.navi.ap.utils.helper
|
||||
|
||||
import com.navi.ap.utils.PeriodicTaskScheduler
|
||||
import com.navi.ap.utils.constants.AP_POLL_INITIAL_DELAY
|
||||
import com.navi.ap.utils.constants.AP_POLL_INTERVAL
|
||||
import com.navi.ap.utils.constants.AP_POLL_RETRY_COUNT
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import orVal
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
/**
|
||||
* Use this to schedule multiple task with taskId.
|
||||
* @param taskId : Represent unique task
|
||||
**/
|
||||
|
||||
|
||||
class PeriodicTaskSchedulerFacade {
|
||||
|
||||
private val mutex = Mutex()
|
||||
private val taskScheduler: ConcurrentHashMap<String, PeriodicTaskScheduler?> =
|
||||
ConcurrentHashMap()
|
||||
|
||||
internal suspend fun scheduleTask(
|
||||
taskId: String,
|
||||
numberOfRetry: Int? = null,
|
||||
pollInterval: Int? = null,
|
||||
initialDelay: Int? = null,
|
||||
onTimeOut: (() -> Unit)? = null,
|
||||
task: () -> Unit,
|
||||
) {
|
||||
mutex.lock()
|
||||
try {
|
||||
stopAndRemoveTask(taskId)
|
||||
schedulePeriodically(
|
||||
taskId = taskId,
|
||||
numberOfRetry = numberOfRetry.orVal(AP_POLL_RETRY_COUNT),
|
||||
pollInterval = pollInterval.orVal(AP_POLL_INTERVAL),
|
||||
initialDelay = initialDelay.orVal(AP_POLL_INITIAL_DELAY),
|
||||
onTimeOut = onTimeOut,
|
||||
task = task
|
||||
)
|
||||
} finally {
|
||||
mutex.unlock()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal fun stopTask(taskId: String) {
|
||||
stopAndRemoveTask(taskId)
|
||||
}
|
||||
|
||||
private fun schedulePeriodically(
|
||||
taskId: String,
|
||||
numberOfRetry: Int,
|
||||
pollInterval: Int,
|
||||
initialDelay: Int,
|
||||
onTimeOut: (() -> Unit)? = null,
|
||||
task: () -> Unit,
|
||||
) {
|
||||
val periodicTaskScheduler = PeriodicTaskScheduler(
|
||||
maxAttempts = numberOfRetry,
|
||||
taskIntervalSeconds = pollInterval.toLong(),
|
||||
initialDelaySeconds = initialDelay.toLong(),
|
||||
onTimeout = {
|
||||
stopAndRemoveTask(taskId)
|
||||
onTimeOut?.invoke()
|
||||
},
|
||||
) {
|
||||
task.invoke()
|
||||
}
|
||||
taskScheduler[taskId] = periodicTaskScheduler
|
||||
periodicTaskScheduler.startTask()
|
||||
}
|
||||
|
||||
private fun stopAndRemoveTask(taskId: String) {
|
||||
taskScheduler[taskId]?.stopTask()
|
||||
taskScheduler.remove(taskId)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.navi.ap.utils.injector
|
||||
|
||||
import com.navi.ap.utils.readJsonPath
|
||||
import com.navi.ap.utils.toJson
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
|
||||
@@ -18,16 +19,18 @@ class PathInjector<U, V>() : BasePathInjector<U, V>() {
|
||||
currentObject.put(key, JSONArray(placeHolderValue.toString()))
|
||||
}
|
||||
}
|
||||
|
||||
JSON_TYPE.JSONObject -> {
|
||||
val valueObject = currentObject.optJSONObject(key)
|
||||
val placeHolderKeyName =
|
||||
valueObject?.optString(JSON_PATH_PLACE_HOLDER_KEY_NAME).orEmpty()
|
||||
injectValues(
|
||||
placeHolderKeyName = placeHolderKeyName,
|
||||
currentObject = currentObject,
|
||||
key = key
|
||||
)
|
||||
if (placeHolderKeyName.startsWith('$')) {
|
||||
val jsonPath = placeHolderKeyName.trim()
|
||||
val placeHolderValue = readJsonPath(jsonResponse.toString(), jsonPath)
|
||||
currentObject.put(key, JSONObject(placeHolderValue.toJson()))
|
||||
}
|
||||
}
|
||||
|
||||
JSON_TYPE.JSONString -> {
|
||||
val placeHolderKeyName = currentObject.optString(key)
|
||||
injectValues(
|
||||
|
||||
@@ -31,5 +31,6 @@ data class CtaData(
|
||||
@SerializedName("requestCode") val requestCode: Int? = null,
|
||||
var bundle: Bundle? = null,
|
||||
@SerializedName("buttonState") val buttonState: String? = null,
|
||||
@SerializedName("needsResult") val needsResult: Boolean? = null
|
||||
@SerializedName("needsResult") val needsResult: Boolean? = null,
|
||||
@SerializedName("refreshScreen") val refreshScreen: Boolean? = null
|
||||
) : NaviClickAction(), Parcelable
|
||||
|
||||
@@ -16,6 +16,7 @@ import kotlinx.parcelize.Parcelize
|
||||
open class LineItem(
|
||||
@SerializedName("key") val key: String? = null,
|
||||
@SerializedName("value") var value: String? = null,
|
||||
@SerializedName("expressionValue") val expressionValue : String? = null,
|
||||
@SerializedName("subtitle") val subtitle: String? = null,
|
||||
@SerializedName("subValue") val subValue: String? = null,
|
||||
@SerializedName("iconCode") val iconCode: String? = null,
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.navi.analytics.utils.NaviTrackEvent
|
||||
import com.navi.analytics.utils.SCREEN_NAME
|
||||
import com.navi.analytics.utils.SCREEN_NAME_CAMEL_CASE
|
||||
import com.navi.common.utils.Constants.APP_PLATFORM_APPLICATION_ID
|
||||
import com.navi.common.utils.Constants.APP_PLATFORM_APPLICATION_TYPE
|
||||
import com.navi.uitron.model.data.ActionDetails
|
||||
import com.navi.uitron.model.data.UiTronAction
|
||||
import getInputId
|
||||
@@ -65,6 +66,9 @@ data class AnalyticsActionV2(
|
||||
handle.get<String>(APP_PLATFORM_APPLICATION_ID)?.let {
|
||||
properties[APP_PLATFORM_APPLICATION_ID] = it
|
||||
}
|
||||
handle.get<String>(APP_PLATFORM_APPLICATION_TYPE)?.let {
|
||||
properties[APP_PLATFORM_APPLICATION_TYPE] = it
|
||||
}
|
||||
NaviTrackEvent.trackEvent(
|
||||
eventName = event,
|
||||
eventValues = properties,
|
||||
|
||||
@@ -2,10 +2,13 @@ package com.navi.common.uitron.model.action
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.navi.analytics.utils.NaviTrackEvent
|
||||
import com.navi.base.model.CtaData
|
||||
import com.navi.common.utils.log
|
||||
import com.navi.uitron.model.data.ActionDetails
|
||||
import com.navi.uitron.model.data.UiTronAction
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import org.mvel2.MVEL
|
||||
|
||||
@Parcelize
|
||||
data class CtaAction(
|
||||
@@ -16,6 +19,31 @@ data class CtaAction(
|
||||
actionDetails: ActionDetails
|
||||
) {
|
||||
val action = actionDetails.uiTronAction as CtaAction
|
||||
action.ctaData?.additionalParameters?.filter { it.expressionValue != null }?.forEach {
|
||||
it.value = evaluateExpression(expression = it.expressionValue, actionDetails = actionDetails)
|
||||
}
|
||||
actionDetails.actionCallbackFlow?.emit(action)
|
||||
}
|
||||
|
||||
private fun evaluateExpression(expression: String?, actionDetails: ActionDetails): String? {
|
||||
if (expression == null) return null
|
||||
return try {
|
||||
MVEL.eval(
|
||||
expression,
|
||||
mapOf(
|
||||
Pair(ExecuteActionsCorrespondingToKey.ACTION, this),
|
||||
Pair(ExecuteActionsCorrespondingToKey.ACTION_DETAILS, actionDetails)
|
||||
)
|
||||
).toString()
|
||||
} catch (e: Exception) {
|
||||
e.log()
|
||||
NaviTrackEvent.trackEvent(
|
||||
eventName = "dev_ExecuteActionsCorrespondingToKey_action",
|
||||
eventValues = mapOf(
|
||||
Pair("mvelExpression", expression),
|
||||
)
|
||||
)
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.navi.common.uitron.model.action
|
||||
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.navi.uitron.model.data.ActionDetails
|
||||
import com.navi.uitron.model.data.UiTronAction
|
||||
import com.navi.uitron.model.data.UiTronActionData
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
|
||||
@Parcelize
|
||||
open class DownloadAction(
|
||||
val documentUrl: String? = null,
|
||||
val config: Config? = null,
|
||||
val downloadAction: Actions? = null,
|
||||
) : UiTronAction() {
|
||||
|
||||
@Parcelize
|
||||
data class Config(
|
||||
val screenName: String? = null,
|
||||
val documentName: String? = null,
|
||||
val allowedOverMetered: Boolean = true,
|
||||
val allowedOverRoaming: Boolean = true,
|
||||
) : Parcelable
|
||||
|
||||
@Parcelize
|
||||
data class Actions(
|
||||
val onStart: UiTronActionData? = null,
|
||||
val onCompleted: UiTronActionData? = null,
|
||||
val onFailed: UiTronActionData? = null,
|
||||
) : Parcelable
|
||||
|
||||
override suspend fun manageAction(actionDetails: ActionDetails) {
|
||||
actionDetails.actionCallbackFlow?.emit(this)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.navi.common.uitron.model.action
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.navi.uitron.model.data.ActionDetails
|
||||
import com.navi.uitron.model.data.UiTronAction
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
|
||||
@Parcelize
|
||||
data class SystemUiAction(
|
||||
val isStatusBarVisible: Boolean? = true,
|
||||
val isNavigationBarVisible: Boolean? = true,
|
||||
val statusBarColor: SystemUiColor? = null,
|
||||
val navigationBarColor: SystemUiColor? = null,
|
||||
) : UiTronAction(), Parcelable {
|
||||
|
||||
@Parcelize
|
||||
data class SystemUiColor(
|
||||
val color: String? = null
|
||||
) : Parcelable
|
||||
|
||||
override suspend fun manageAction(actionDetails: ActionDetails) {
|
||||
actionDetails.actionCallbackFlow?.emit(this)
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,10 @@ import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonSerializationContext
|
||||
import com.navi.common.uitron.model.action.AnalyticsActionV2
|
||||
import com.navi.common.uitron.model.action.CtaAction
|
||||
import com.navi.common.uitron.model.action.DownloadAction
|
||||
import com.navi.common.uitron.model.action.ExecuteActionsCorrespondingToKey
|
||||
import com.navi.common.uitron.model.action.LaunchIntentAction
|
||||
import com.navi.common.uitron.model.action.SystemUiAction
|
||||
import com.navi.common.uitron.model.action.ThirdPartySdkAction
|
||||
import com.navi.common.uitron.model.action.UpdateStateHandleActionV2
|
||||
import com.navi.common.uitron.model.action.UpdateViewStateActionV2
|
||||
@@ -68,6 +70,10 @@ class UiTronActionSerializer : BaseUiTronActionSerializer() {
|
||||
context?.serialize(src as AnalyticsActionV2, AnalyticsActionV2::class.java)
|
||||
ApActionType.LaunchIntentAction.name ->
|
||||
context?.serialize(src as LaunchIntentAction, LaunchIntentAction::class.java)
|
||||
ApActionType.DownloadAction.name ->
|
||||
context?.serialize(src as DownloadAction, DownloadAction::class.java)
|
||||
ApActionType.SystemUiAction.name ->
|
||||
context?.serialize(src as SystemUiAction, SystemUiAction::class.java)
|
||||
else -> super.serialize(src, typeOfSrc, context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,5 +16,7 @@ enum class ApActionType {
|
||||
ExecuteActionsCorrespondingToKey,
|
||||
AnalyticsActionV2,
|
||||
LaunchIntentAction,
|
||||
UpdateDataViaHandleAction
|
||||
UpdateDataViaHandleAction,
|
||||
DownloadAction,
|
||||
SystemUiAction
|
||||
}
|
||||
|
||||
@@ -196,4 +196,9 @@
|
||||
<string name="navi_app_common_channel_name">Navi</string>
|
||||
<string name="navi_app_custom_channel_name">Miscellaneous</string>
|
||||
<string name="taking_longer_time_desc">We\'re experiencing some delays in processing your request, but we\'re working hard to get it done as soon as possible. Thank you for your patience.</string>
|
||||
<string name="document_downloading">Your document is being downloaded</string>
|
||||
<string name="failed_document_download">Failed to download document, need write storage permission</string>
|
||||
<string name="download_failed">Download failed</string>
|
||||
<string name="start_download">Starting Download…</string>
|
||||
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user