TP-55716 | Manual vs Autopay sip (#10090)

Co-authored-by: Prakhar Saxena <prakhar.saxena@navi.com>
This commit is contained in:
Ashutosh Y
2024-03-12 23:02:59 +05:30
committed by GitHub
parent 4544e7eabe
commit f928edfac0
29 changed files with 882 additions and 59 deletions

View File

@@ -29,6 +29,7 @@ import com.navi.amc.common.taskProcessor.AmcTaskManager
import com.navi.amc.common.viewmodel.CheckerVM
import com.navi.amc.common.viewmodel.PaymentSharedVM
import com.navi.amc.databinding.CheckerActivityAmcBinding
import com.navi.amc.fundbuy.models.SipDetailsData
import com.navi.amc.navigator.NaviAmcDeeplinkNavigator
import com.navi.amc.utils.*
import com.navi.amc.utils.AmcAnalytics.AMC_LATENCY_ESIGN_CALLBACK_TIME
@@ -41,12 +42,17 @@ import com.navi.amc.utils.AmcAnalytics.AMC_LATENCY_PAYMENT_INITIATION_TIME
import com.navi.amc.utils.AmcAnalytics.AMC_LATENCY_PENNY_DROP_TIME
import com.navi.amc.utils.AmcAnalytics.POLLING_TIMEOUT
import com.navi.amc.utils.Constant.AMC_PERSONAL
import com.navi.amc.utils.Constant.AUTOPAY_TYPE
import com.navi.amc.utils.Constant.AUTO_PAY_CALLBACK_ERROR
import com.navi.amc.utils.Constant.CHECKER_DATA
import com.navi.amc.utils.Constant.CHECKER_TYPE
import com.navi.amc.utils.Constant.FLOW_TYPE
import com.navi.amc.utils.Constant.ORDER_ID
import com.navi.amc.utils.Constant.OTP_FLOW_TYPE_SIP_AUTOPAY
import com.navi.amc.utils.Constant.OTP_FLOW_TYPE_SIP_MANUAL
import com.navi.amc.utils.Constant.PAYMENT_CALLBACK_ERROR
import com.navi.amc.utils.Constant.REQUEST_CONFIG
import com.navi.amc.utils.Constant.SIP_REFERENCE_ID
import com.navi.amc.utils.Constant.TRANSACTION_ID
import com.navi.base.model.ActionData
import com.navi.base.model.CtaData
@@ -156,6 +162,7 @@ class CheckerActivity : BasePaymentActivity() {
if(type == PAYMENT_CALLBACK_SYNC || type == AUTO_PAY_PAYMENT_CALLBACK_SYNC) {
val requestId = intent.getStringExtra(TRANSACTION_ID)
val requestConfig = intent.getParcelableExtra<RequestConfig>(REQUEST_CONFIG)
viewModel.autopayType = intent.getStringExtra(AUTOPAY_TYPE)
requestId?.let {
apiPollInit(requestConfig, requestId)
}
@@ -351,6 +358,7 @@ class CheckerActivity : BasePaymentActivity() {
response.data?.data?.statusData?.let {
bundle.putParcelable(DATA, it)
bundle.putString(TYPE, type)
bundle.putString(SIP_REFERENCE_ID,viewModel.sipReferenceId)
}
addUpcomingPaymentParams(bundle, response.data?.status)
response.data
@@ -358,6 +366,21 @@ class CheckerActivity : BasePaymentActivity() {
?.nextCTA
?.toNavigateAmcModule(activity = this, finish = true, bundle = bundle)
}
} else if (intent.getStringExtra(FLOW_TYPE) in listOf(
OTP_FLOW_TYPE_SIP_MANUAL,
OTP_FLOW_TYPE_SIP_AUTOPAY
) && response.data?.status == FirebaseStatusType.SUCCESS
) {
val sipDetailsData = SipDetailsData(
scheme = intent?.getStringExtra(AmcAnalytics.ISIN),
amount = intent?.getStringExtra(Constant.AMOUNT),
frequency = intent?.getStringExtra(Constant.FREQUENCY),
sipDate = intent?.getStringExtra(Constant.SIP_DATE),
paymentMode = intent?.getStringExtra(Constant.PAYMENT_MODE),
orderId = intent?.getStringExtra(ORDER_ID),
autoPayChecked = intent.getStringExtra(FLOW_TYPE) == OTP_FLOW_TYPE_SIP_AUTOPAY
)
viewModel.postSipDetails(sipDetailsData)
} else {
onFailureResponse(response.errors?.firstOrNull())
}

View File

@@ -22,12 +22,14 @@ import com.navi.amc.databinding.AmcPaymentBottomsheetBinding
import com.navi.amc.fundbuy.models.SipDetailsResponse
import com.navi.amc.utils.AmcAnalytics
import com.navi.amc.utils.Constant
import com.navi.amc.utils.Constant.PAYMENT
import com.navi.amc.utils.Constant.PAYMENT_MODE
import com.navi.amc.utils.SubPageStatusType
import com.navi.amc.utils.TempStorageHelper
import com.navi.base.model.ActionData
import com.navi.base.model.LineItem
import com.navi.base.utils.isNotNull
import com.navi.base.utils.orFalse
import com.navi.common.ui.activity.BaseActivity
import com.navi.common.ui.fragment.BaseBottomSheet
import com.navi.design.utils.parseColorSafe
@@ -83,13 +85,29 @@ class AmcPaymentBottomSheet : BaseBottomSheet() {
setTextColor(it.parseColorSafe())
}
setOnClickListener {
AmcAnalytics.sendEvent(
eventsData = response?.metaData?.clickedData,
extraAttributes = hashMapOf(PAYMENT_MODE to paymentMode.orEmpty()),
screenName = screenName
)
isPaymentAttempted = true
initiateSipPayment()
if (response?.footer?.nextCta?.url?.contains(PAYMENT).orFalse()) {
AmcAnalytics.sendEvent(
eventsData = response?.metaData?.clickedData,
extraAttributes = hashMapOf(PAYMENT_MODE to paymentMode.orEmpty()),
screenName = screenName
)
isPaymentAttempted = true
initiateSipPayment()
} else {
val cta = response?.footer?.nextCta?.copy(
parameters = response?.footer?.nextCta?.parameters?.toMutableList()
?.apply {
add(
LineItem(
PAYMENT_MODE,
paymentMode
)
)
})
cta?.let { cta -> action?.invoke(cta) }
safelyDismissDialog()
}
}
}
AmcAnalytics.sendEvent(

View File

@@ -6,8 +6,6 @@
package com.navi.amc.common.fragment
import com.navi.naviwidgets.R as WidgetsR
import com.navi.design.R as DesignR
import android.content.Context
import android.content.IntentFilter
import android.os.Bundle
@@ -35,10 +33,13 @@ import com.navi.amc.fundbuy.models.PaymentOrder
import com.navi.amc.fundbuy.models.PaymentPostData
import com.navi.amc.fundbuy.models.SipDetailsData
import com.navi.amc.fundbuy.models.SipDetailsResponse
import com.navi.amc.utils.*
import com.navi.amc.utils.AmcAnalytics
import com.navi.amc.utils.Constant
import com.navi.amc.utils.Constant.AMOUNT
import com.navi.amc.utils.Constant.API_CALL_MULTI_CLICK_THRESOLD_DUR
import com.navi.amc.utils.Constant.AUTOPAY_CHECKED
import com.navi.amc.utils.Constant.AUTO_PAY_PRESENT
import com.navi.amc.utils.Constant.CAPS_DATA
import com.navi.amc.utils.Constant.CREATE_REDEEM_ORDER
import com.navi.amc.utils.Constant.DATA_SOURCE
import com.navi.amc.utils.Constant.DISMISS
@@ -48,6 +49,8 @@ import com.navi.amc.utils.Constant.OTP_COUNTDOWN_IN_SECOND
import com.navi.amc.utils.Constant.OTP_FLOW_TYPE_LUMPSUM_PURCHASE
import com.navi.amc.utils.Constant.OTP_FLOW_TYPE_REDEEMPTION
import com.navi.amc.utils.Constant.OTP_FLOW_TYPE_RETRY_PAYMENT
import com.navi.amc.utils.Constant.OTP_FLOW_TYPE_SIP_AUTOPAY
import com.navi.amc.utils.Constant.OTP_FLOW_TYPE_SIP_MANUAL
import com.navi.amc.utils.Constant.OTP_FLOW_TYPE_SIP_PURCHASE
import com.navi.amc.utils.Constant.REDEMPTION_ORDER_ID
import com.navi.amc.utils.Constant.REQUEST_CONFIG
@@ -55,8 +58,15 @@ import com.navi.amc.utils.Constant.SECONDS_PER_MINUTE
import com.navi.amc.utils.Constant.SIP_DATE
import com.navi.amc.utils.Constant.SIP_REFERENCE_ID
import com.navi.amc.utils.Constant.TRANSACTION_ID
import com.navi.amc.utils.TempStorageHelper
import com.navi.amc.utils.getPaymentSyncFlowStatusCta
import com.navi.amc.utils.toNavigateAmcModule
import com.navi.base.model.ActionData
import com.navi.base.utils.*
import com.navi.base.utils.BaseUtils
import com.navi.base.utils.isNotNullAndNotEmpty
import com.navi.base.utils.orElse
import com.navi.base.utils.orFalse
import com.navi.base.utils.orTrue
import com.navi.common.customview.BoxInputGroup.Companion.OTP_LENGTH_4
import com.navi.common.listeners.FragmentInterchangeListener
import com.navi.common.listeners.HeaderInteractionListener
@@ -69,10 +79,12 @@ import com.navi.common.utils.RETRY
import com.navi.common.utils.getErrorData
import com.navi.design.utils.getNaviDrawable
import com.navi.design.utils.setSpannableString
import com.navi.paymentclients.viewmodel.base.PaymentManager
import com.navi.payment.listener.PaymentListener
import com.navi.payment.utils.PaymentAnalytics
import com.navi.paymentclients.viewmodel.base.PaymentManager
import dagger.hilt.android.AndroidEntryPoint
import com.navi.design.R as DesignR
import com.navi.naviwidgets.R as WidgetsR
@AndroidEntryPoint
class OtpFragment : AmcBaseFragment(), View.OnClickListener {
@@ -106,14 +118,16 @@ class OtpFragment : AmcBaseFragment(), View.OnClickListener {
viewStub.layoutResource = R.layout.otp_fragment_amc
binding = DataBindingUtil.getBinding(viewStub.inflate())!!
initError(viewModel, buttonListener = {
when(it){
when (it) {
DISMISS -> {
popThisFromBackStack()
}
RETRY -> {
generateOtp(true)
}
else ->{
else -> {
}
}
})
@@ -138,7 +152,7 @@ class OtpFragment : AmcBaseFragment(), View.OnClickListener {
// inNewTranscation will be false only for new sip purchase flow
private fun fetchData() {
var isNewTransaction = true
if(flowType == OTP_FLOW_TYPE_SIP_PURCHASE) {
if (flowType == OTP_FLOW_TYPE_SIP_PURCHASE) {
isNewTransaction = arguments?.getString(SIP_REFERENCE_ID).isNotNullAndNotEmpty()
}
viewModel.fetchScreenData(flowType, isin, isNewTransaction)
@@ -188,7 +202,7 @@ class OtpFragment : AmcBaseFragment(), View.OnClickListener {
viewModel.generateOtpResponse.observe(viewLifecycleOwner) {
hideLoader()
binding.otpLayout.clear()
if(it?.isResendOtp.orFalse()) hideResendOtpOnCallUiState()
if (it?.isResendOtp.orFalse()) hideResendOtpOnCallUiState()
}
viewModel.verifyOtpResponse.observe(viewLifecycleOwner) {
hideLoader()
@@ -279,6 +293,7 @@ class OtpFragment : AmcBaseFragment(), View.OnClickListener {
paymentVM.postPaymentStatus(it)
val url = getPaymentSyncFlowStatusCta(CheckerActivity.PAYMENT_CALLBACK_SYNC)
val bundle = Bundle().apply {
putAll(arguments)
putString(
TRANSACTION_ID,
viewModel.paymentInitiateData.value?.tokenDetails?.transactionId
@@ -298,6 +313,17 @@ class OtpFragment : AmcBaseFragment(), View.OnClickListener {
fragmentInterchangeListener?.navigateToNextScreen(ActionData(url = url), bundle)
}
}
viewModel.sipCreateResponse.observe(viewLifecycleOwner) { response ->
val bundle = Bundle()
response?.data?.statusData?.let {
bundle.putParcelable(CAPS_DATA, it)
bundle.putString(SIP_REFERENCE_ID, viewModel.sipReferenceId)
}
response?.data
?.nextCTA
?.toNavigateAmcModule(activity = activity, bundle = bundle)
}
}
private fun fireOtpVerificationResultEvent(isValid: Boolean) {
@@ -338,6 +364,36 @@ class OtpFragment : AmcBaseFragment(), View.OnClickListener {
"", OTP_FLOW_TYPE_REDEEMPTION -> {
redeemUnits(otpResponseData)
}
OTP_FLOW_TYPE_SIP_AUTOPAY -> {
val ctaAction = viewModel.dataResponse.value?.footer?.nextCta
if (ctaAction?.type == AUTO_PAY_PRESENT) {
viewModel.createSip(
SipDetailsData(
scheme = arguments?.getString(AmcAnalytics.ISIN),
amount = arguments?.getString(AMOUNT),
frequency = arguments?.getString(FREQUENCY),
sipDate = arguments?.getString(SIP_DATE),
autoPayChecked = true
)
)
} else {
fragmentInterchangeListener?.navigateToNextScreen(
viewModel.dataResponse.value?.footer?.nextCta, arguments ?: Bundle()
)
}
}
OTP_FLOW_TYPE_SIP_MANUAL -> {
val sipDetailsData = SipDetailsData(
scheme = arguments?.getString(AmcAnalytics.ISIN),
amount = arguments?.getString(AMOUNT),
frequency = arguments?.getString(FREQUENCY),
sipDate = arguments?.getString(SIP_DATE),
paymentMode = arguments?.getString(Constant.PAYMENT_MODE)
)
viewModel.initiateSipPayment(sipDetailsData)
}
}
}
@@ -431,7 +487,7 @@ class OtpFragment : AmcBaseFragment(), View.OnClickListener {
if (isinParam.isNotNullAndNotEmpty()) {
this.isin = isinParam
}else{
} else {
this.isin = fundIdParam
}
}

View File

@@ -177,7 +177,7 @@ class StatusFragment : AmcBaseFragment() , RewardDialogCancelListener {
activity?.onBackPressed()
} else {
fragmentInterchangeListener?.navigateToNextScreen(
redirectCta ?: viewModel.dataItems.value?.footer?.nextCta
redirectCta ?: viewModel.dataItems.value?.footer?.nextCta, arguments ?: Bundle()
)
}
}

View File

@@ -9,6 +9,7 @@ package com.navi.amc.common.repo
import com.navi.amc.common.activity.CheckerActivity
import com.navi.amc.common.model.CheckerResponse
import com.navi.amc.fundbuy.models.SipDetailsData
import com.navi.amc.network.retrofit.RetrofitService
import com.navi.common.network.models.RepoResult
import com.navi.common.network.retrofit.ResponseCallback
@@ -48,8 +49,8 @@ class CheckerRepository @Inject constructor(private val retrofitService: Retrofi
suspend fun fetchAsyncRequestDataPayment(requestId: String) =
apiResponseCallback(retrofitService.fetchAsyncRequestDataPayment(requestId))
suspend fun fetchAsyncRequestAutoPayDataPayment(requestId: String) =
apiResponseCallback(retrofitService.fetchAsyncRequestAutoPayDataPayment(requestId))
suspend fun fetchAsyncRequestAutoPayDataPayment(requestId: String , autopayType: String? = null) =
apiResponseCallback(retrofitService.fetchAsyncRequestAutoPayDataPayment(requestId, autopayType))
suspend fun fetchEsignAsyncRequestDataWithNextCta(requestId: String) =
apiResponseCallback(retrofitService.fetchEsignAsyncRequestDataWithNextCta(requestId))
@@ -75,4 +76,10 @@ class CheckerRepository @Inject constructor(private val retrofitService: Retrofi
suspend fun postNameDataV2() =
apiResponseCallback(retrofitService.postKycNameDataV2())
suspend fun postSipDetails(details: SipDetailsData) =
apiResponseCallback(retrofitService.postSipDetails(details))
suspend fun fetchSipSuccessPage() =
apiResponseCallback(retrofitService.fetchSipSuccessPage())
}

View File

@@ -42,4 +42,10 @@ class OTPRepository @Inject constructor(private val retrofitService: RetrofitSer
suspend fun postSameOrderPayment(details: PaymentOrder) =
apiResponseCallback(retrofitService.postSameOrderPayment(details))
suspend fun initiateSipPayment(sipDetails: SipDetailsData) =
apiResponseCallback(retrofitService.initiateSipPayment(sipDetails))
suspend fun fetchSipSuccessPage() =
apiResponseCallback(retrofitService.fetchSipSuccessPage())
}

View File

@@ -16,15 +16,18 @@ import com.navi.amc.common.model.KycCheckerContent
import com.navi.amc.common.model.NextCtaResponse
import com.navi.amc.common.repo.CheckerRepository
import com.navi.amc.common.taskProcessor.AmcTaskManager
import com.navi.amc.fundbuy.models.SipDetailsData
import com.navi.amc.kyc.model.KycPersonalDetailsResponse
import com.navi.amc.utils.AmcAnalytics
import com.navi.amc.utils.TempStorageHelper
import com.navi.base.model.ActionData
import com.navi.base.utils.EMPTY
import com.navi.base.utils.isNotNull
import com.navi.common.firebasedb.FirebaseStatusType
import com.navi.common.network.models.GenericErrorResponse
import com.navi.common.network.models.RepoResult
import com.navi.common.network.models.SuccessResponse
import com.navi.common.network.models.isSuccessWithData
import com.navi.design.textview.model.NaviSpan
import com.navi.design.textview.model.TextWithStyle
import com.navi.naviwidgets.models.response.DataSafeWidget
@@ -32,6 +35,7 @@ import com.navi.naviwidgets.models.response.NoteWidget
import com.navi.payment.model.clientmodels.PostPaymentData
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import javax.inject.Inject
@@ -79,6 +83,9 @@ class CheckerVM @Inject constructor(private val repository: CheckerRepository) :
val panErrorData: LiveData<GenericErrorResponse?>
get() = _panErrorData
var sipReferenceId = EMPTY
var autopayType: String? = null
var isNavigationRequiredOnFinish = true
val placeHolderPanCheckerData = CheckerResponse(
@@ -222,7 +229,7 @@ class CheckerVM @Inject constructor(private val repository: CheckerRepository) :
}
CheckerActivity.AUTO_PAY_PAYMENT_CALLBACK,
CheckerActivity.AUTO_PAY_PAYMENT_CALLBACK_SYNC -> {
val response = repository.fetchAsyncRequestAutoPayDataPayment(requestId)
val response = repository.fetchAsyncRequestAutoPayDataPayment(requestId, autopayType)
_asyncResponse.value = response
}
CheckerActivity.PL_DISBURSED_JOURNEY -> {
@@ -259,6 +266,23 @@ class CheckerVM @Inject constructor(private val repository: CheckerRepository) :
}
}
fun postSipDetails(details: SipDetailsData) {
viewModelScope.launch {
val responseAsync = async {
repository.postSipDetails(details)
}
val successDataAsync = async {
repository.fetchSipSuccessPage()
}
val response = responseAsync.await()
val successData = successDataAsync.await()
if (response.isSuccessWithData() && successData.isSuccessWithData()) {
sipReferenceId = response.data?.sipReferenceId.orEmpty()
_asyncResponse.value = successData
}
}
}
fun isBackAllowed(type: String): Boolean {
return type !in listOf(
CheckerActivity.KYC_CALLBACK,

View File

@@ -25,13 +25,16 @@ import com.navi.amc.fundbuy.models.SipDetailsResponse
import com.navi.amc.utils.AmcAnalytics
import com.navi.amc.utils.Constant
import com.navi.amc.utils.updateCheckerResponse
import com.navi.base.utils.EMPTY
import com.navi.common.network.ApiConstants.API_WRONG_OTP
import com.navi.common.network.models.RepoResult
import com.navi.common.network.models.isSuccessWithData
import com.navi.common.utils.Constants
import com.navi.common.utils.SingleLiveEvent
import com.navi.common.viewmodel.BaseVM
import com.navi.payment.utils.PaymentAnalytics
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import javax.inject.Inject
@@ -63,6 +66,12 @@ class OTPVM @Inject constructor(private val repository: OTPRepository) : BaseAmc
val sipDetailsResponse: LiveData<SipDetailsResponse?>
get() = _sipDetailsResponse
private val _sipCreateResponse = SingleLiveEvent<AdditionalDataAsyncResponse<NextCtaResponse>?>()
val sipCreateResponse: LiveData<AdditionalDataAsyncResponse<NextCtaResponse>?>
get() = _sipCreateResponse
var sipReferenceId = EMPTY
fun fetchScreenData(flowType: String, isin: String? = null, isNewTransaction: Boolean) {
viewModelScope.launch {
val response = repository.getOtpDetails(flowType, isin, isNewTransaction)
@@ -151,6 +160,30 @@ class OTPVM @Inject constructor(private val repository: OTPRepository) : BaseAmc
}
}
fun initiateSipPayment(sipDetailsData: SipDetailsData) {
viewModelScope.launch {
val response = repository.initiateSipPayment(sipDetailsData)
consumePaymentInitiateResponse(response)
}
}
fun createSip(details: SipDetailsData) {
viewModelScope.launch {
val responseAsync = async {
repository.postSipDetails(details)
}
val successDataAsync = async {
repository.fetchSipSuccessPage()
}
val response = responseAsync.await()
val successData = successDataAsync.await()
if (response.isSuccessWithData() && successData.isSuccessWithData()) {
sipReferenceId = response.data?.sipReferenceId.orEmpty()
_sipCreateResponse.value = successData.data
}
}
}
fun clearData() {
errorResponse.value = null
_sipDetailsResponse.value = null

View File

@@ -0,0 +1,91 @@
package com.navi.amc.fundbuy.adapters
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.RecyclerView
import com.navi.amc.R
import com.navi.amc.databinding.SipTypeOptionLayoutBinding
import com.navi.amc.fundbuy.models.SipOption
import com.navi.amc.utils.ColorUtils
import com.navi.base.utils.isValidIndex
import com.navi.base.utils.orFalse
import com.navi.design.utils.CornerRadius
import com.navi.design.utils.dpToPx
import com.navi.design.utils.dpToPxInInt
import com.navi.design.utils.getNaviDrawable
import com.navi.design.utils.parseColorSafe
import com.navi.design.utils.setSpannableString
class SipTypeListAdapter(
private val items: List<SipOption>,
val listener: ((SipOption) -> Unit)? = null
) : RecyclerView.Adapter<SipTypeViewHolder>() {
var lastCheckedPosition = -1
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SipTypeViewHolder {
return SipTypeViewHolder(
DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.sip_type_option_layout,
parent,
false
)
)
}
override fun getItemCount(): Int {
return items.size
}
override fun onBindViewHolder(holder: SipTypeViewHolder, position: Int) {
if (!isValidIndex(position, itemCount)) return
val itemData = items[position]
holder.binding.apply {
title.setSpannableString(itemData.title)
root.background =
ColorUtils.getBackGroundColor4RoundedDrawable(holder.binding.root.context)
label.isVisible =
itemData.label?.let {
label.setSpannableString(it.title)
label.background =
getNaviDrawable(
cornerRadius = dpToPxInInt(4),
backgroundColor = it.bgColor.parseColorSafe()
)
true
} ?: run { false }
if (lastCheckedPosition != -1)
items[position].isSelected = (lastCheckedPosition == position)
radio.isClickable = false
if (lastCheckedPosition == -1 && itemData.isSelected.orFalse()) {
lastCheckedPosition = position
listener?.invoke(itemData)
}
radio.isChecked = (lastCheckedPosition == position)
if (radio.isChecked || itemData.toShowNote.orFalse()) {
note.setSpannableString(itemData.note?.title)
note.background =
getNaviDrawable(
radii = CornerRadius(leftBottom = dpToPx(4), rightBottom = dpToPx(4)),
backgroundColor = itemData.note?.bgColor.parseColorSafe()
)
root.setBackgroundResource(R.drawable.bg_purple_rounded_4_amc)
} else {
note.isVisible = false
}
root.setOnClickListener {
radio.isChecked = true
val tempLastCheckedPosition = lastCheckedPosition
lastCheckedPosition = position
notifyItemChanged(tempLastCheckedPosition)
notifyItemChanged(lastCheckedPosition)
listener?.invoke(itemData)
}
}
}
}
class SipTypeViewHolder(val binding: SipTypeOptionLayoutBinding) :
RecyclerView.ViewHolder(binding.root)

View File

@@ -57,8 +57,8 @@ class AutoPaySetupFragment() : AmcBaseFragment(), FooterInteractionListener {
override val screenName: String
get() = AUTO_PAY_SETUP
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
override fun onResume() {
super.onResume()
fetchData()
}
@@ -219,6 +219,7 @@ class AutoPaySetupFragment() : AmcBaseFragment(), FooterInteractionListener {
)
putString(PaymentAnalytics.PROVIDER, it.provider)
putParcelable(Constant.PAYMENT_DATA, it)
putAll(arguments)
}
fragmentInterchangeListener?.navigateToNextScreen(ActionData(url = url), bundle)
}

View File

@@ -784,18 +784,26 @@ class FundBuyingFragmentV2 : AmcBaseFragment(), WidgetCallback {
)
), isNeededForFirebase = true
)
val bottomSheetData = viewModel.getSipBottomSheetData(
binding.sipAmount.widgetBinding.plainTextInput.text.toString(),
frequency,
binding.autopayCheckbox.checkbox.isChecked
)
val bundle = Bundle().apply { putString(Constant.DATA, bottomSheetData) }
getBottomSheet(
SubPageStatusType.AMC_COMMON_BOTTOMSHEET,
bundle = bundle,
genericListener = ::sipWeeklyFortnightlyAction
)?.let { safelyShowBottomSheet(it, SubPageStatusType.HORIZONTAL_BUTTON_BOTTOMSHEET) }
if (viewModel.isManualVsAutopayExperiment()) {
sipWeeklyFortnightlyAction(null)
} else {
val bottomSheetData = viewModel.getSipBottomSheetData(
binding.sipAmount.widgetBinding.plainTextInput.text.toString(),
frequency,
binding.autopayCheckbox.checkbox.isChecked
)
val bundle = Bundle().apply { putString(Constant.DATA, bottomSheetData) }
getBottomSheet(
SubPageStatusType.AMC_COMMON_BOTTOMSHEET,
bundle = bundle,
genericListener = ::sipWeeklyFortnightlyAction
)?.let {
safelyShowBottomSheet(
it,
SubPageStatusType.HORIZONTAL_BUTTON_BOTTOMSHEET
)
}
}
} else {
sendEvent(
getCreateSipEventName(), hashMapOf(
@@ -843,18 +851,26 @@ class FundBuyingFragmentV2 : AmcBaseFragment(), WidgetCallback {
)
), isNeededForFirebase = true
)
val bottomSheetData = viewModel.getSipBottomSheetData(
binding.sipAmount.widgetBinding.plainTextInput.text.toString(),
frequency,
binding.autopayCheckbox.checkbox.isChecked
)
val bundle = Bundle().apply { putString(Constant.DATA, bottomSheetData) }
getBottomSheet(
SubPageStatusType.AMC_COMMON_BOTTOMSHEET,
bundle = bundle,
genericListener = ::sipMonthlyAction
)?.let { safelyShowBottomSheet(it, SubPageStatusType.HORIZONTAL_BUTTON_BOTTOMSHEET) }
if (viewModel.isManualVsAutopayExperiment()) {
sipMonthlyAction(null)
} else {
val bottomSheetData = viewModel.getSipBottomSheetData(
binding.sipAmount.widgetBinding.plainTextInput.text.toString(),
frequency,
binding.autopayCheckbox.checkbox.isChecked
)
val bundle = Bundle().apply { putString(Constant.DATA, bottomSheetData) }
getBottomSheet(
SubPageStatusType.AMC_COMMON_BOTTOMSHEET,
bundle = bundle,
genericListener = ::sipMonthlyAction
)?.let {
safelyShowBottomSheet(
it,
SubPageStatusType.HORIZONTAL_BUTTON_BOTTOMSHEET
)
}
}
} else {
sendEvent(
getCreateSipEventName(), hashMapOf(
@@ -981,8 +997,8 @@ class FundBuyingFragmentV2 : AmcBaseFragment(), WidgetCallback {
}
}
private fun createSipRecurringAction(action: ActionData, includeSipDate: Boolean) {
if (action.url == DISMISS) {
private fun createSipRecurringAction(action: ActionData?, includeSipDate: Boolean) {
if (action?.url == DISMISS) {
// do nothing
return
}
@@ -995,8 +1011,11 @@ class FundBuyingFragmentV2 : AmcBaseFragment(), WidgetCallback {
val autoPayChecked =
if (viewModel.isAutoPayThere()) binding.autopayCheckbox.checkbox.isChecked.toString() else null
if (ctaUrl.endsWith(SubPageStatusType.OTP, true)) {
if (ctaUrl.endsWith(
SubPageStatusType.OTP,
true
) || ctaUrl.endsWith(SubPageStatusType.SIP_TYPE, true)
) {
val bundle = Bundle().apply {
putString(AmcAnalytics.ISIN, isin)
@@ -1034,8 +1053,8 @@ class FundBuyingFragmentV2 : AmcBaseFragment(), WidgetCallback {
}
}
private fun sipWeeklyFortnightlyAction(action: ActionData) = createSipRecurringAction(action, false)
private fun sipMonthlyAction(action: ActionData) = createSipRecurringAction(action, true)
private fun sipWeeklyFortnightlyAction(action: ActionData?) = createSipRecurringAction(action, false)
private fun sipMonthlyAction(action: ActionData?) = createSipRecurringAction(action, true)
override fun onAttach(context: Context) {
super.onAttach(context)

View File

@@ -0,0 +1,235 @@
package com.navi.amc.fundbuy.fragments
import android.content.Context
import android.os.Bundle
import android.view.ViewStub
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DividerItemDecoration
import com.google.gson.Gson
import com.navi.amc.common.fragment.AmcBaseFragment
import com.navi.amc.databinding.SipTypeLayoutBinding
import com.navi.amc.fundbuy.adapters.SipTypeListAdapter
import com.navi.amc.fundbuy.models.AmountPageFooter
import com.navi.amc.fundbuy.models.GenericFooter
import com.navi.amc.fundbuy.models.SipOption
import com.navi.amc.fundbuy.models.SipTypeScreenContent
import com.navi.amc.fundbuy.models.SipTypeScreenData
import com.navi.amc.fundbuy.models.SipTypeScreenState
import com.navi.amc.fundbuy.viewmodel.SipTypeViewModel
import com.navi.amc.utils.AmcAnalytics.ISIN
import com.navi.amc.utils.AmcAnalytics.SIP_TYPE
import com.navi.amc.utils.Constant
import com.navi.amc.utils.Constant.AMOUNT
import com.navi.amc.utils.Constant.CONTINUE
import com.navi.amc.utils.Constant.DISMISS
import com.navi.amc.utils.Constant.FREQUENCY
import com.navi.amc.utils.Constant.PAYMENT_MODE
import com.navi.amc.utils.Constant.SHOW_BOTTOMSHEET
import com.navi.amc.utils.Constant.SIP_DATE
import com.navi.amc.utils.Constant.SIP_START_DATE
import com.navi.amc.utils.SubPageStatusType
import com.navi.amc.utils.getBottomSheet
import com.navi.base.model.ActionData
import com.navi.base.utils.CurrencyUtils
import com.navi.common.listeners.FragmentInterchangeListener
import com.navi.common.model.Header
import com.navi.design.utils.setSpannableString
import com.navi.naviwidgets.R
import com.navi.naviwidgets.extensions.showWhenDataIsAvailable
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import java.math.BigDecimal
import com.navi.amc.R as amcR
@AndroidEntryPoint
class SipTypeFragment() : AmcBaseFragment() {
private val viewModel by viewModels<SipTypeViewModel>()
private lateinit var binding: SipTypeLayoutBinding
override val screenName: String
get() = SIP_TYPE
override fun onAttach(context: Context) {
super.onAttach(context)
fragmentInterchangeListener = context as? FragmentInterchangeListener
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
fetchData()
}
override fun setContainerView(viewStub: ViewStub) {
viewStub.layoutResource = amcR.layout.sip_type_layout
binding = DataBindingUtil.getBinding(viewStub.inflate())!!
initObservers()
}
private fun initObservers() {
viewLifecycleOwner.lifecycleScope.launch {
viewModel.sipTypeViewState.collectLatest {
when (it) {
is SipTypeScreenState.Loading -> {
showLoader()
}
is SipTypeScreenState.Success -> {
hideLoader()
initUI(it.data)
}
is SipTypeScreenState.Error -> {
hideLoader()
}
}
}
}
}
private fun fetchData() {
val map = mutableMapOf<String, String>()
arguments?.getString(ISIN)?.let {
map.put(ISIN, it)
}
arguments?.getString(AMOUNT)?.let {
map.put(AMOUNT, it)
}
arguments?.getString(FREQUENCY)?.let {
map.put(FREQUENCY, it)
}
arguments?.getString(SIP_DATE)?.let {
map.put(SIP_START_DATE, it)
}
viewModel.fetchSipTypeData(map)
}
private fun initUI(data: SipTypeScreenData) {
data.header?.let { initHeader(it) }
data.content?.let { initContent(it) }
}
private fun initHeader(header: Header) {
setHeaderProperties(header)
hideDivider()
}
private fun initContent(content: SipTypeScreenContent) {
binding.fundHeader.setProperties(content.fundHeaderData, ::navigate)
binding.title.setSpannableString(content.title)
initRecyclerView(content.items)
}
private fun initRecyclerView(items: List<SipOption>? = null) {
items?.let {
binding.items.adapter = SipTypeListAdapter(it, ::clickAction)
val itemDecorator = DividerItemDecoration(context, DividerItemDecoration.VERTICAL)
context?.let {
ContextCompat.getDrawable(it, R.drawable.empty_space_divider)?.let { drawable ->
itemDecorator.setDrawable(drawable)
}
}
binding.items.addItemDecoration(itemDecorator)
}
}
private fun clickAction(item: SipOption) {
viewModel.selectedType = item.id.orEmpty()
viewModel.nextPageCta = item.actionData
setFooterData()
}
private fun setFooterData() {
viewModel.getPaymentFooter()?.let { footer ->
when (footer) {
is AmountPageFooter -> {
binding.footer.root.isVisible = false
binding.paymentFooter.apply {
root.isVisible = true
account.setSpannableString(footer.paymentCta?.account)
footer.paymentCta?.let {
val amountData = arguments?.getString(Constant.AMOUNT)
amount.text =
CurrencyUtils.getNormalizedAmount(
if (!amountData.isNullOrEmpty()) amountData.toBigDecimal()
else BigDecimal(0)
)
leftIcon.showWhenDataIsAvailable(it.leftIcon)
rightIcon.showWhenDataIsAvailable(it.rightIcon)
actionText.showWhenDataIsAvailable(it.actionText)
llAction.setOnClickListener { view ->
performAction(it.actionData)
}
}
btn.text = footer.nextCta?.title
btn.setOnClickListener { showSipBottomSheet() }
}
}
is GenericFooter -> {
binding.paymentFooter.root.isVisible = false
binding.footer.root.isVisible = true
binding.footer.title.setSpannableString(footer.title)
binding.footer.btn.apply {
text = footer.nextCta?.title
setOnClickListener { showSipBottomSheet() }
}
}
else -> {
binding.paymentFooter.root.isVisible = false
binding.footer.root.isVisible = false
}
}
}
}
private fun navigate(action: ActionData?) {
fragmentInterchangeListener?.navigateToNextScreen(action, arguments ?: Bundle())
}
private fun showSipBottomSheet() {
val data = viewModel.getBottomSheetData()
val bottomSheetData = Gson().toJson(data)
val bundle = Bundle().apply { putString(Constant.DATA, bottomSheetData) }
getBottomSheet(
SubPageStatusType.AMC_COMMON_BOTTOMSHEET,
bundle = bundle,
genericListener = ::performAction
)
?.let { safelyShowBottomSheet(it, SubPageStatusType.HORIZONTAL_BUTTON_BOTTOMSHEET) }
}
private fun performAction(actionData: ActionData?) {
when (actionData?.url) {
DISMISS -> return
CONTINUE -> {
val action = viewModel.nextPageCta
navigate(action)
}
SHOW_BOTTOMSHEET -> {
val data = actionData.parameters?.getOrNull(0)?.value
val key = actionData.parameters?.getOrNull(0)?.key.orEmpty()
val bundle = Bundle().apply { putString(Constant.DATA, data) }
getBottomSheet(key,bundle, genericListener = {
viewModel.nextPageCta = it
showSipBottomSheet()
})?.let{
safelyShowBottomSheet(it,key)
}
}
else -> {
navigate(actionData)
}
}
}
companion object {
fun newInstance(bundle: Bundle): SipTypeFragment {
return SipTypeFragment().apply { arguments = bundle }
}
}
}

View File

@@ -40,10 +40,12 @@ data class FundBuyData(
@SerializedName("footerVariations") val footerVariations: Map<String, Footer>? = null,
@SerializedName("newFooterVariations") val newFooterVariations: Map<String, CardType>? = null,
@SerializedName("amountPageFooter") val amountPageFooter: CardType? = null,
@SerializedName("isManualVsAutopay") val isManualVsAutopay:Boolean? = null
)
open class GenericFooter(
@SerializedName("nextCta") val nextCta: ActionData? = null
@SerializedName("nextCta") val nextCta: ActionData? = null,
@SerializedName("title") val title: TextWithStyle? = null
) : CardType()
data class AmountPageFooter(

View File

@@ -14,7 +14,11 @@ data class SipDetailsData(
@SerializedName("customer_id")
val customer_id: String? = null,
@SerializedName("autoPayChecked")
val autoPayChecked: Boolean? = null
val autoPayChecked: Boolean? = null,
@SerializedName("paymentMode")
val paymentMode: String? = null,
@SerializedName("orderId")
val orderId: String? = null,
)
data class SipDetailsResponse(

View File

@@ -0,0 +1,53 @@
package com.navi.amc.fundbuy.models
import com.google.gson.annotations.SerializedName
import com.navi.amc.common.model.AmcCommonBottomSheetData
import com.navi.amc.common.model.Footer
import com.navi.amc.kyc.model.Note
import com.navi.base.model.ActionData
import com.navi.common.model.Header
import com.navi.common.model.LabelData
import com.navi.common.network.models.ErrorMessage
import com.navi.common.network.models.GenericErrorResponse
import com.navi.design.textview.model.TextWithStyle
import com.navi.naviwidgets.models.response.CardType
sealed class SipTypeScreenState {
object Loading : SipTypeScreenState()
data class Success(val data: SipTypeScreenData) : SipTypeScreenState()
data class Error(
val errors: List<GenericErrorResponse>? = null,
val error: ErrorMessage? = null,
) : SipTypeScreenState()
}
data class SipTypeScreenData(
@SerializedName("header") val header: Header? = null,
@SerializedName("content") val content: SipTypeScreenContent? = null,
@SerializedName("footer") val footer: Footer? = null
)
data class SipTypeScreenContent(
@SerializedName("fundHeader") val fundHeaderData: AmcHeaderData? = null,
@SerializedName("sipCommonBottomSheetWithOutAutoPay")
val sipCommonBottomSheetWithOutAutoPay: AmcCommonBottomSheetData? = null,
@SerializedName("sipCommonBottomSheetWithAutoPay")
val sipCommonBottomSheetWithAutoPay: AmcCommonBottomSheetData? = null,
@SerializedName("footerVariations") val footerVariations: Map<String, CardType>? = null,
@SerializedName("title") val title: TextWithStyle? = null,
@SerializedName("items") val items: List<SipOption>? = null
)
data class SipOption(
@SerializedName("label") val label: LabelData? = null,
@SerializedName("title") val title: TextWithStyle? = null,
@SerializedName("isSelected", alternate = ["selected"]) var isSelected: Boolean? = null,
@SerializedName("id") val id: String? = null,
@SerializedName("subTitle") val subTitle: TextWithStyle? = null,
@SerializedName("toShowNote") val toShowNote: Boolean? = null,
@SerializedName("note") val note: Note? = null,
@SerializedName("actionData") val actionData: ActionData? = null,
@SerializedName("icon") val icon: String? = null
)

View File

@@ -0,0 +1,12 @@
package com.navi.amc.fundbuy.repository
import com.navi.amc.network.retrofit.RetrofitService
import com.navi.common.network.retrofit.ResponseCallback
import javax.inject.Inject
class SipTypeRepository @Inject constructor(private val retrofitService: RetrofitService) :
ResponseCallback() {
suspend fun fetchSipTypeData(map: Map<String, String>) =
apiResponseCallback(retrofitService.fetchSipTypeData(map))
}

View File

@@ -305,6 +305,10 @@ class FundBuyV2ViewModel @Inject constructor(private val repository: FundBuyRepo
return TextWithStyle(text = formattedText, style = styleList)
}
fun isManualVsAutopayExperiment(): Boolean {
return _fundBuyScreenData.value?.content?.isManualVsAutopay.orFalse()
}
companion object {
private const val DAY_IN_MILLIS = 24 * 60 * 60 * 1000L
}

View File

@@ -0,0 +1,72 @@
package com.navi.amc.fundbuy.viewmodel
import androidx.lifecycle.viewModelScope
import com.navi.amc.common.model.AmcCommonBottomSheetData
import com.navi.amc.common.viewmodel.BaseAmcVM
import com.navi.amc.fundbuy.models.SipTypeScreenState
import com.navi.amc.fundbuy.repository.SipTypeRepository
import com.navi.amc.utils.Constant.AUTOPAY
import com.navi.base.model.ActionData
import com.navi.common.utils.isValidResponse
import com.navi.naviwidgets.models.response.CardType
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import javax.inject.Inject
@HiltViewModel
class SipTypeViewModel @Inject constructor(private val repository: SipTypeRepository) : BaseAmcVM() {
private val _sipTypeViewState = MutableStateFlow<SipTypeScreenState>(SipTypeScreenState.Loading)
val sipTypeViewState = _sipTypeViewState.asStateFlow()
var selectedType = ""
var nextPageCta: ActionData? = null
fun fetchSipTypeData(map: Map<String, String>) {
viewModelScope.safeLaunch(Dispatchers.IO) {
_sipTypeViewState.emit(SipTypeScreenState.Loading)
val response = repository.fetchSipTypeData(map)
if (response.isValidResponse()) {
response.data?.let { _sipTypeViewState.emit(SipTypeScreenState.Success(it)) }
} else {
_sipTypeViewState.emit(SipTypeScreenState.Error(response.errors, response.error))
setErrorData(response.errors, response.error)
}
}
}
fun getPaymentFooter(): CardType? {
return (sipTypeViewState.value as? SipTypeScreenState.Success)
?.data
?.content
?.footerVariations
?.let { variations ->
return variations.getOrDefault(selectedType, null)
}
}
fun getBottomSheetData(): AmcCommonBottomSheetData? {
return if (selectedType == AUTOPAY) {
sipCommonBottomSheetWithAutoPay()
} else {
sipCommonBottomSheetWithOutAutoPay()
}
}
private fun sipCommonBottomSheetWithOutAutoPay(): AmcCommonBottomSheetData? {
return (sipTypeViewState.value as? SipTypeScreenState.Success)
?.data
?.content
?.sipCommonBottomSheetWithOutAutoPay
}
private fun sipCommonBottomSheetWithAutoPay(): AmcCommonBottomSheetData? {
return (sipTypeViewState.value as? SipTypeScreenState.Success)
?.data
?.content
?.sipCommonBottomSheetWithAutoPay
}
}

View File

@@ -224,7 +224,8 @@ interface RetrofitService {
@GET("/autopay/{requestId}/status")
suspend fun fetchAsyncRequestAutoPayDataPayment(
@Path("requestId") requestId: String
@Path("requestId") requestId: String,
@Query("autopay_type") autopayType: String? = null
): Response<GenericResponse<AdditionalDataAsyncResponse<NextCtaResponse>>>
@GET("/amc/esign/{requestId}")
@@ -479,4 +480,14 @@ interface RetrofitService {
suspend fun postQuestionnaireData(
@Body response: QuestionnaireResponse?
): Response<GenericResponse<NextCtaResponse>>
@GET("fund/get-sip-type-page")
suspend fun fetchSipTypeData(@QueryMap map: Map<String, String>): Response<GenericResponse<SipTypeScreenData>>
@POST("orders/initiate-sip-transaction")
suspend fun initiateSipPayment(@Body sipDetails: SipDetailsData): Response<GenericResponse<AdditionalDataAsyncResponse<NextCtaResponse>>>
@GET("fund/get-sip-success")
suspend fun fetchSipSuccessPage(): Response<GenericResponse<AdditionalDataAsyncResponse<NextCtaResponse>>>
}

View File

@@ -179,6 +179,7 @@ object AmcAnalytics {
const val AMOUNT_REDEEMED = "amount_redeemed"
const val UNITS_REDEEMED = "units_redeemed"
const val PERC_TOTAL_FUND_VALUE = "perc_total_fund_value"
const val SIP_TYPE = "sip_type"
const val AMC_INIT_SIMPLIFIED_LUMPSUM = "amc_init_simplified_lumpsum"
const val AMC_INIT_SIMPLIFIED_SIP = "amc_init_simplified_sip"

View File

@@ -61,6 +61,8 @@ object Constant {
const val FREQUENCY = "frequency"
const val AUTOPAY_CHECKED = "autopayChecked"
const val SIP_DATE = "sipDate"
const val SIP_START_DATE = "sipStartDate"
const val AUTO_PAY_PRESENT = "AUTO_PAY_PRESENT"
const val AMOUNT_SOURCE = "amount_source"
const val CONFINED_INVESTMENT_TYPE = "CONFINED_INVESTMENT_TYPE"
@@ -106,6 +108,8 @@ object Constant {
const val INIT_REFUND = "INIT_REFUND"
const val BACK_PRESS = "backPress"
const val DISMISS = "dismiss"
const val CONTINUE = "continue"
const val AUTO_PAY = "AUTO_PAY"
const val PD = "PD"
const val IS_INVESTMENT_ON_BOTTOM_NAV = "isInvestmentOnBottomNav"
const val THOUSAND = 1000L
@@ -135,7 +139,8 @@ object Constant {
const val OTP_FLOW_TYPE_SIP_PURCHASE = "SIP_PURCHASE"
const val OTP_FLOW_TYPE_REDEEMPTION = "REDEMPTION"
const val OTP_FLOW_TYPE_RETRY_PAYMENT = "RETRY_PAYMENT"
const val OTP_FLOW_TYPE_SIP_AUTOPAY = "SIP_AUTOPAY"
const val OTP_FLOW_TYPE_SIP_MANUAL = "SIP_MANUAL"
const val CUSTOMER_PRIOR_INVESTMENT_QUESTIONNAIRE_REQUIRED = "customerPriorInvestmentQuestionnaireRequired"
const val UPI_APP_INTENT_URL = "upi://pay"
@@ -166,4 +171,8 @@ object Constant {
const val HPC_REDIRECTION_ENTRY_TIME = "HPC_REDIRECTION_ENTRY_TIME"
const val BYPASS_HPC = "BYPASS_HPC"
const val AUTO_PAY_CALLBACK_RESULT_CODE = 1002
const val PAYMENT = "payment"
const val CAPS_DATA ="DATA"
const val AUTOPAY_TYPE = "autopay_type"
const val AUTOPAY = "AUTOPAY"
}

View File

@@ -237,6 +237,7 @@ fun getFragment(screen: String, bundle: Bundle): Fragment? {
SubPageStatusType.KYC_VERIFICATION_OPTIONS -> KycVerificationOptionsFragment.newInstance(bundle)
SubPageStatusType.AMC_WEB_VIEW_PAGE -> AmcWebViewPageFragment.newInstance(bundle)
SubPageStatusType.AMC_QUESTIONNAIRE_PAGE -> QuestionnaireFragment.newInstance(bundle)
SubPageStatusType.SIP_TYPE -> SipTypeFragment.newInstance(bundle)
else -> null
}
}

View File

@@ -66,4 +66,5 @@ object SubPageStatusType {
const val KYC_VERIFICATION_OPTIONS = "kyc_verification_options"
const val AMC_WEB_VIEW_PAGE = "amc_web_view_page"
const val AMC_QUESTIONNAIRE_PAGE = "amc_questionnaire_page"
const val SIP_TYPE ="sip_type"
}

View File

@@ -88,7 +88,7 @@
android:id="@+id/btn"
android:layout_width="@dimen/dp_0"
android:layout_height="@dimen/dp_48"
android:layout_marginTop="@dimen/dp_20"
android:layout_marginTop="@dimen/dp_16"
android:layout_marginBottom="@dimen/dp_32"
android:background="@drawable/bg_cta_primary_purple_amc_rounded_4"
android:fontFamily="@font/tt_semi_bold"

View File

@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white">
<com.navi.amc.fundbuy.views.AmcHeaderView
android:id="@+id/fund_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.navi.design.textview.NaviTextView
android:id="@+id/title"
android:layout_width="@dimen/dp_0"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_16"
android:layout_marginTop="@dimen/dp_32"
android:layout_marginEnd="@dimen/dp_16"
android:gravity="start"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/fund_header"
tools:text="Choose SIP type" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/items"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_16"
android:layout_marginTop="@dimen/dp_16"
android:layout_marginEnd="@dimen/dp_16"
android:overScrollMode="never"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/title" />
<include
android:id="@+id/footer"
layout="@layout/new_footer_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<include
android:id="@+id/payment_footer"
layout="@layout/payment_btn_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.navi.design.textview.NaviTextView
android:id="@+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="@dimen/dp_8"
android:paddingTop="@dimen/dp_2"
android:paddingEnd="@dimen/dp_8"
android:paddingBottom="@dimen/dp_2"
android:layout_marginStart="@dimen/dp_12"
app:layout_constraintStart_toEndOf="@id/title"
app:layout_constraintTop_toTopOf="@id/title"
app:layout_constraintBottom_toBottomOf="@id/title"
tools:text="RECOMMENDED" />
<com.navi.design.textview.NaviTextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_16"
android:layout_marginTop="@dimen/dp_16"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/note"
android:layout_marginBottom="@dimen/dp_16"
app:layout_constraintStart_toStartOf="parent"
tools:text="Via UPI" />
<RadioButton
android:id="@+id/radio"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_16"
android:layout_marginEnd="@dimen/dp_16"
android:button="@drawable/purple_radio_selector"
app:layout_constraintBottom_toBottomOf="@id/title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.navi.design.textview.NaviTextView
android:id="@+id/note"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_1"
android:layout_marginEnd="@dimen/dp_1"
android:layout_marginBottom="@dimen/dp_1"
android:gravity="start"
android:paddingStart="@dimen/dp_16"
android:paddingTop="@dimen/dp_8"
android:paddingEnd="@dimen/dp_16"
android:paddingBottom="@dimen/dp_8"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/note"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:text="Amount will be refunded within 24-48 hours" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -1341,6 +1341,9 @@ fun LottieAnimationView.showWhenDataIsAvailable(
LottieEnums.YELLOW_EXCLAMATION_LOTTIE.name -> {
setAnimation(R.raw.yellow_exclamation_lottie)
}
LottieEnums.AMC_SIP_SUCCESS.name -> {
setAnimation(R.raw.amc_sip_success)
}
else -> {
isInAppLottie = false
CoroutineScope(Dispatchers.Main).launch {

View File

@@ -115,5 +115,6 @@ enum class LottieEnums {
GOLD_PROCESSING_TRANSACTION_LOTTIE,
PURCHASE_SUCCESSFUL_LOTTIE,
RED_CROSS_LOTTIE,
YELLOW_EXCLAMATION_LOTTIE
YELLOW_EXCLAMATION_LOTTIE,
AMC_SIP_SUCCESS
}

File diff suppressed because one or more lines are too long